mirror of
https://github.com/harvester/harvester-ui-extension.git
synced 2026-11-15 13:26:15 +08:00
feat: redesign the VNC console (#1233)
* chore: bump @rancher/shell to 3.0.15-rc.1 for the Rc components Signed-off-by: Marcelo Fukumoto <marcelo.fukumoto@suse.com> * feat: rebuild the VNC console toolbar with Rc components Name and namespace, a connection status pill with its own menu, Rc tertiary buttons with the design's icons, and the keyboard hint under the screen. noVNC's grey backdrop is gone and the screen keeps a thin frame that turns into the keyboard-focus ring. Signed-off-by: Marcelo Fukumoto <marcelo.fukumoto@suse.com> * feat: move VNC shortcut keys into one Rc dropdown menu Saved shortcuts, Ctrl and Alt toggles that apply to the next key, Print Screen, F1 to F12 and the way into custom shortcuts now share the Shortcut keys menu, replacing the cascading popover and the separate custom keys menu. Saved shortcuts can carry a name; the old list format still reads. Focus goes back to the screen after a key is sent. Signed-off-by: Marcelo Fukumoto <marcelo.fukumoto@suse.com> * feat: replace the VNC settings popover with the View options panel Automatic or Manual scaling on the shell ButtonGroup, a zoom slider with step buttons, JPEG quality and compression with their trade-off spelled out, and Reset to defaults. The screen now scales to fit by default, inside its focus ring. View only is gone with the old panel. Signed-off-by: Marcelo Fukumoto <marcelo.fukumoto@suse.com> * feat: confirm Ctrl+Alt+Del and Soft reboot in an RcModal Small RcModal with the VM's name in the title and a destructive primary button, coloured the way the shell's own dialogs do it. Esc and the background now close it too, and the keyboard goes back to the screen afterwards. Signed-off-by: Marcelo Fukumoto <marcelo.fukumoto@suse.com> * feat: rebuild VNC custom shortcuts as an RcModal with a key recorder Record a combination by pressing it, name it, and see the saved ones as RcTags with a delete button each. Every change is saved straight away. Keys keep the format older versions read; names live in their own preference so those versions keep working. Signed-off-by: Marcelo Fukumoto <marcelo.fukumoto@suse.com> * feat: show VNC connecting and connection failures as console overlays A spinner with the VM's name while connecting (and the attempt count while retrying), and a card with an icon badge, title, explanation and Rc buttons when the console cannot be used: in use, unavailable, connection lost or failed. Take over confirms in an RcModal. Signed-off-by: Marcelo Fukumoto <marcelo.fukumoto@suse.com> * feat: float a compact VNC bar over the screen in full screen Name, connection status and Exit full screen in a floating bar instead of the toolbar, with the keyboard on the screen. Take over leaves full screen first, since its modal opens outside the console. Signed-off-by: Marcelo Fukumoto <marcelo.fukumoto@suse.com> * fix: VNC console review fixes Show a not-running state instead of connecting forever when the VM has no instance, and find the VM by its uid so the name shows. Keep a failed shortcut save from looking saved. Offer Take over only when another session holds the console, and Reconnect now while retrying. Wrap the toolbar on narrow windows, keep the screen black so overlays read in the light theme, keep the destructive button red on focus, announce only the status label, let Esc and Tab leave the key recorder, release held modifiers when the menu closes, and let Ctrl+T and Ctrl+N through in full screen. Signed-off-by: Marcelo Fukumoto <marcelo.fukumoto@suse.com> * feat: use the redrawn VNC console icons Power, maximize, minimize and wifi-off on the design's 24px grid, the 20px connecting spinner, and a status card badge that stays dark on the black screen. The screen is only black while there is no live picture. Signed-off-by: Marcelo Fukumoto <marcelo.fukumoto@suse.com> * feat: run standalone Harvester Prime with the SUSE brand Standalone Harvester reports ui-brand=harvester, which has no stylesheet, so Prime only got a green primary on top of the default palette: 33 of the 36 SUSE theme tokens differed from Rancher Prime (blue tertiary buttons, another green, dark text on primary buttons, no SUSE font). On the Prime flavor the brand is now switched to suse in this browser, and all 36 match Rancher Prime in light and dark. Signed-off-by: Marcelo Fukumoto <marcelo.fukumoto@suse.com> * fix: name recorded VNC shortcut letters after the key pressed macOS Option turns letters into symbols (Option+G gives ©), so a recorded Alt+Cmd+G was labelled and sent as ©. Letter keys now take their name from the key code when recording, labelling and sending, which also fixes shortcuts saved before. Signed-off-by: Marcelo Fukumoto <marcelo.fukumoto@suse.com> * fix: give the VNC shortcut menu chips the secondary button colours The key chips, held modifiers and the manage item used the link colour, so they stayed blue under the SUSE brand. They now take RcButton's secondary, active and tertiary tokens and follow the brand like the toolbar buttons. Signed-off-by: Marcelo Fukumoto <marcelo.fukumoto@suse.com> * fix: VNC console review fixes, round three Wait for the instance to run before connecting and retry refusals right after a start, keep Reconnect now counting retries, stop key sending once the VM is down, only rename option-mangled letters (Alt without Ctrl, dead keys) so other layouts keep their keys, give the keyboard back to View when its panel closes, add a shortcut with Enter, keep long shortcut names to two lines, only replace Harvester's own brand and refresh the favicon, and stop relying on stylesheet order for the spinner and danger button. Signed-off-by: Marcelo Fukumoto <marcelo.fukumoto@suse.com> * feat: change the VNC menu keys with the held modifiers The keys under Hold modifiers follow what is held, so the menu sends what the old cascading menu did: Ctrl+N/T/W/Y with Ctrl, Ctrl+Alt+Del with Ctrl and Alt (through the same confirmation as its button), and Print Screen and F1-F12 alone or with Alt. Ctrl+Alt+F1-F12 stays for switching Linux consoles. The recorder points to the menu for browser-reserved combinations. Signed-off-by: Marcelo Fukumoto <marcelo.fukumoto@suse.com> * feat: bring View only back to the VNC View menu The View menu has a View only switch again, as the old settings panel did: the screen stays live but no mouse or keyboard input reaches the VM, the status reads Connected (view only), the key menus are disabled and the hint says how to turn it off. The design does not have it yet. Signed-off-by: Marcelo Fukumoto <marcelo.fukumoto@suse.com> * feat: move View only into the VNC connection menu View only is how this session uses the connection, so it sits in the Connected menu (also in full screen) as a checkbox item, and the View menu is only about how the screen is shown. Signed-off-by: Marcelo Fukumoto <marcelo.fukumoto@suse.com> * feat: keep the VNC console's controls usable around the connection Full screen goes over the whole page and its bar gets Shortcut keys and Ctrl+Alt+Del, so menus and the confirmation show there too and Take over no longer has to leave full screen. Soft reboot works whatever the console's state, since it does not go through it. The Shortcut keys menu always opens: while keys cannot be sent it says why and disables only the keys, so custom shortcuts can still be managed. The focus ring now sits 2px off the screen so it shows against a dark framebuffer edge in the light theme. Signed-off-by: Marcelo Fukumoto <marcelo.fukumoto@suse.com> * feat: disable the VNC console's input controls while View only is on View only sends nothing to the VM, so Shortcut keys (managing shortcuts included), Ctrl+Alt+Del and Soft reboot are disabled instead of the menu opening to explain why. The menu still opens while disconnected, as there are shortcuts to manage. The hint under the screen takes the new wording, read raw since t() would escape its quotes. Signed-off-by: Marcelo Fukumoto <marcelo.fukumoto@suse.com> * feat: fill the window with the VNC console in full screen The screen takes the whole window, with no padding, ring or inset, and black around a picture of another shape. It always fits in full screen, whatever View mode is saved, which comes back on leaving. The bar floats over the screen, slightly transparent until it is hovered, holds the keyboard focus or has a menu open, and no longer has Ctrl+Alt+Del, which stays in the Shortcut keys menu. Signed-off-by: Marcelo Fukumoto <marcelo.fukumoto@suse.com> * feat: show the mouse over the VNC console noVNC hides the mouse over the screen until the guest sends a pointer image, and a text console or a guest that draws its own pointer never does. Show the system pointer unless one arrives, so it can be seen in the window and in full screen. A guest that sends an image keeps it. Signed-off-by: Marcelo Fukumoto <marcelo.fukumoto@suse.com> * fix: close the VNC console's menus on a click on the screen in Firefox Firefox delivers the click to the canvas, where noVNC stops it, so an open menu never saw a click outside it. Full screen makes this matter, with little else left to click. noVNC does nothing else with a click, so let it through. Signed-off-by: Marcelo Fukumoto <marcelo.fukumoto@suse.com> * feat: make View only a toggle in the VNC console toolbar View only moves from the Connected menu to the shared ToggleSwitch between the connection status and Shortcut keys, in the toolbar and in the full screen bar, so its state is always in view and the status no longer has to repeat it. Turning it off hands the keyboard back to the console. The full screen bar no longer gets only half the window from its left: 50%, which would have squeezed its buttons with the wider content; it wraps when it really does not fit. Signed-off-by: Marcelo Fukumoto <marcelo.fukumoto@suse.com> * feat: leave View only out of the VNC console's full screen bar View only is set from the toolbar. A session that enters full screen with it on stays that way, with Shortcut keys disabled, and changes it after leaving. Signed-off-by: Marcelo Fukumoto <marcelo.fukumoto@suse.com> * feat: make View only a checkbox in the VNC console toolbar It takes the shared Checkbox instead of ToggleSwitch, in the same place between the connection status and Shortcut keys. Clicking its label now turns it off as well as on. Signed-off-by: Marcelo Fukumoto <marcelo.fukumoto@suse.com> * fix: save the VNC view settings only when the user changes them The previous console wrote its defaults to localStorage the first time it opened, so Manual zoom at 100% looked like a choice and the new Automatic default never reached anyone who had used the console. Settings now live under a new key and are saved only when the user changes them. Signed-off-by: Marcelo Fukumoto <marcelo.fukumoto@suse.com> * fix: keep View only on when the VNC View panel resets its settings View only moved to the toolbar, but Reset to defaults in the View panel still set it back to off, so the screen started sending the mouse and keyboard to the VM again. The reset now leaves View only as it is. Signed-off-by: Marcelo Fukumoto <marcelo.fukumoto@suse.com> * fix: give the VNC quality and compression sliders their full height The slider rule's flex: 1 is meant for the zoom row. In the quality and compression sections, which are columns, it set the height instead and left those two sliders 4px tall, so a click on the visible thumb missed. Signed-off-by: Marcelo Fukumoto <marcelo.fukumoto@suse.com> * fix: reach the VNC shortcut keys from the keyboard after holding a modifier RcDropdown collects its items when the menu opens, and holding Ctrl or Alt swaps the keys below the toggles while it is open, so the arrow keys stopped at Alt. The menu now collects its items again on each key press, as RcDropdown does for its submenus. Signed-off-by: Marcelo Fukumoto <marcelo.fukumoto@suse.com> * fix: show a failed custom shortcut save inside the dialog The dialog only reported a failed save with a growl, and the console page's layout has no growls, so a delete or an add that could not be saved changed nothing and said nothing. The error now shows as an alert in the dialog until the next change or until it closes. Signed-off-by: Marcelo Fukumoto <marcelo.fukumoto@suse.com> * fix: keep the focus in the custom shortcuts dialog after adding or deleting The delete buttons and Add shortcut were disabled while a change saved, and disabling the button that was just pressed dropped the keyboard's focus to the page. They now stay enabled, and a change made while one saves is ignored. After a delete the next row's button takes the focus, or the last one's, and after an add, or once the list is empty, the recorder does. Signed-off-by: Marcelo Fukumoto <marcelo.fukumoto@suse.com> * fix: record a modifier pressed on its own as a custom VNC shortcut The recorder held every modifier for the key that ends the combination, so the Windows key on its own could not be saved any more, while the old dialog could. Modifiers released before any other key are now the combination, once the last of them is released. Signed-off-by: Marcelo Fukumoto <marcelo.fukumoto@suse.com> * fix: stop the custom shortcut form saying a new shortcut is already saved The list shows a new shortcut while it saves, and the form kept it until the save came back, so every add showed "This key combination is already saved." as an alert for as long as the save took. The form now empties as soon as Add shortcut is pressed, and the shortcut goes back in it if the save fails, unless the next one was started meanwhile. Signed-off-by: Marcelo Fukumoto <marcelo.fukumoto@suse.com> * fix: make the VNC Reconnect button an RcButton The plain button took the global 40px button height inside the 32px Disconnected pill, so its focus ring stuck out above and below the pill. As a small ghost RcButton it is 24px tall and its ring stays inside. Signed-off-by: Marcelo Fukumoto <marcelo.fukumoto@suse.com> * fix: carry View only over from the previous VNC console The new storage key dropped a View only that someone turned on in the previous console, so their mouse and keyboard reached the VM again after the upgrade. View only was off unless the user turned it on, so it is read from the previous key until this console saves its own settings; the display settings still start fresh. The comment on the key now says the settings are written once the user changes one, rather than only the ones changed. Signed-off-by: Marcelo Fukumoto <marcelo.fukumoto@suse.com> * fix: warn that a VNC soft reboot loses unsaved work The soft reboot confirmation said the console disconnects while the VM restarts and reconnects when it is back, which it does not: a soft reboot restarts the guest in the same VM instance and pod, so the console stays connected. It also dropped the previous dialog's warning that unsaved work is lost, which is back. Signed-off-by: Marcelo Fukumoto <marcelo.fukumoto@suse.com> --------- Signed-off-by: Marcelo Fukumoto <marcelo.fukumoto@suse.com>
This commit is contained in:
@@ -0,0 +1,232 @@
|
||||
<script>
|
||||
import RcStatusIndicator from '@components/Pill/RcStatusIndicator';
|
||||
import RcTag from '@components/Pill/RcTag';
|
||||
import { MODIFIER_KEYS, baseKey, keyLabel, recordKey } from './shortcuts';
|
||||
|
||||
export default {
|
||||
name: 'NovncKeyRecorder',
|
||||
|
||||
components: { RcStatusIndicator, RcTag },
|
||||
|
||||
props: {
|
||||
// The recorded keys, as stored for a shortcut.
|
||||
value: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
|
||||
ariaLabelledby: {
|
||||
type: String,
|
||||
default: undefined,
|
||||
},
|
||||
},
|
||||
|
||||
emits: ['update:value'],
|
||||
|
||||
data() {
|
||||
return { recording: false, pending: [] };
|
||||
},
|
||||
|
||||
computed: {
|
||||
state() {
|
||||
if (this.recording) {
|
||||
return 'recording';
|
||||
}
|
||||
|
||||
return this.value.length ? 'captured' : 'empty';
|
||||
},
|
||||
|
||||
labels() {
|
||||
return this.value.map((K) => keyLabel(baseKey(K, this.value).key));
|
||||
},
|
||||
|
||||
valueId() {
|
||||
return `vnc-key-recorder-value-${ this.$.uid }`;
|
||||
},
|
||||
|
||||
// Read out with the field's label, so screen readers hear what is recorded.
|
||||
announcement() {
|
||||
if (this.state === 'recording') {
|
||||
return this.t('harvester.virtualMachine.detail.console.customKeys.recorder.recordingStatus');
|
||||
}
|
||||
|
||||
return this.labels.join('+');
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
start() {
|
||||
this.pending = [];
|
||||
this.recording = true;
|
||||
},
|
||||
|
||||
stop() {
|
||||
this.recording = false;
|
||||
this.pending = [];
|
||||
},
|
||||
|
||||
// Modifiers wait for the key they go with; the first other key ends the combination.
|
||||
onKeydown(event) {
|
||||
if (!this.recording) {
|
||||
if (event.key === 'Enter' || event.key === ' ') {
|
||||
event.preventDefault();
|
||||
this.start();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Esc or Tab on their own leave the field, so it never traps the keyboard; with a modifier
|
||||
// they are part of the combination (Ctrl+Shift+Esc).
|
||||
if (!this.pending.length && (event.key === 'Escape' || event.key === 'Tab')) {
|
||||
if (event.key === 'Escape') {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
}
|
||||
this.stop();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
if (event.repeat) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (MODIFIER_KEYS.includes(event.key)) {
|
||||
if (!this.pending.some((K) => K.key === event.key)) {
|
||||
this.pending.push(recordKey(event));
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
this.$emit('update:value', [...this.pending, recordKey(event, this.pending)]);
|
||||
this.stop();
|
||||
},
|
||||
|
||||
// Modifiers released before any other key are the combination themselves, such as the Windows key on
|
||||
// its own. It waits for the last one, so Ctrl+Shift can be let go one key at a time.
|
||||
onKeyup(event) {
|
||||
if (!this.recording || !this.pending.length || !MODIFIER_KEYS.includes(event.key)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.ctrlKey || event.altKey || event.shiftKey || event.metaKey) {
|
||||
return;
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
this.$emit('update:value', [...this.pending]);
|
||||
this.stop();
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="key-recorder"
|
||||
:class="`key-recorder--${ state }`"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
:aria-labelledby="ariaLabelledby ? `${ ariaLabelledby } ${ valueId }` : valueId"
|
||||
:aria-pressed="recording"
|
||||
data-testid="vnc-key-recorder"
|
||||
@click="start"
|
||||
@keydown="onKeydown"
|
||||
@keyup="onKeyup"
|
||||
@blur="stop"
|
||||
>
|
||||
<span
|
||||
:id="valueId"
|
||||
class="sr-only"
|
||||
aria-live="polite"
|
||||
>{{ announcement }}</span>
|
||||
|
||||
<template v-if="state === 'recording'">
|
||||
<span class="key-recorder__hint">{{ t('harvester.virtualMachine.detail.console.customKeys.recorder.recording') }}</span>
|
||||
<span class="key-recorder__status">
|
||||
<RcStatusIndicator
|
||||
shape="disc"
|
||||
status="error"
|
||||
/>
|
||||
{{ t('harvester.virtualMachine.detail.console.customKeys.recorder.recordingStatus') }}
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<template v-else-if="state === 'captured'">
|
||||
<span class="key-recorder__keys">
|
||||
<RcTag
|
||||
v-for="(label, i) in labels"
|
||||
:key="i"
|
||||
type="active"
|
||||
>
|
||||
{{ label }}
|
||||
</RcTag>
|
||||
</span>
|
||||
<span class="key-recorder__again">{{ t('harvester.virtualMachine.detail.console.customKeys.recorder.again') }}</span>
|
||||
</template>
|
||||
|
||||
<span
|
||||
v-else
|
||||
class="key-recorder__hint"
|
||||
>{{ t('harvester.virtualMachine.detail.console.customKeys.recorder.empty') }}</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.key-recorder {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
min-height: 48px;
|
||||
padding: 0 14px;
|
||||
border: 1px solid var(--input-border);
|
||||
border-radius: 4px;
|
||||
background: var(--input-bg);
|
||||
cursor: pointer;
|
||||
|
||||
&--empty {
|
||||
border-style: dashed;
|
||||
}
|
||||
|
||||
&--recording {
|
||||
border: 2px solid var(--primary-keyboard-focus);
|
||||
padding: 0 13px;
|
||||
}
|
||||
|
||||
&:focus-visible {
|
||||
@include focus-outline;
|
||||
}
|
||||
|
||||
&__hint {
|
||||
color: var(--muted);
|
||||
font-size: 14px;
|
||||
line-height: 21px;
|
||||
}
|
||||
|
||||
&__keys {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
&__status,
|
||||
&__again {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-left: auto;
|
||||
font-size: 12px;
|
||||
line-height: 18px;
|
||||
}
|
||||
|
||||
&__again {
|
||||
color: var(--muted);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user