Skip to content

Commit

Permalink
feat(web-stack): implement actions and clipboard for VM console (#8125)
Browse files Browse the repository at this point in the history
  • Loading branch information
MathieuRA authored Nov 25, 2024
1 parent 87b7016 commit fa974f9
Show file tree
Hide file tree
Showing 18 changed files with 242 additions and 88 deletions.
1 change: 1 addition & 0 deletions @xen-orchestra/lite/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

- [Pool/Dashboard] `Patches` and `Status` cards are displayed with a greater minimum width (PR [#8108](https://github.com/vatesfr/xen-orchestra/pull/8108))
- [Header] Update user menu button (PR [#8109](https://github.com/vatesfr/xen-orchestra/pull/8109))
- [VM/Console] Display _Console Clipboard_ and _Console Actions_ (PR [#8125](https://github.com/vatesfr/xen-orchestra/pull/8125))

## **0.5.0** (2024-10-31)

Expand Down
8 changes: 7 additions & 1 deletion @xen-orchestra/lite/src/components/RemoteConsole.vue
Original file line number Diff line number Diff line change
Expand Up @@ -110,10 +110,16 @@ defineExpose({

<style lang="postcss" scoped>
.vm-console {
height: 80rem;
flex: 1;
max-width: 100%;
& > :deep(div) {
background-color: transparent !important;
}
/* Required because the library adds "margin: auto" to the canvas which makes the canvas centered in space and not aligned to the rest of the layout */
:deep(canvas) {
margin: 0 auto !important;
}
}
</style>
3 changes: 0 additions & 3 deletions @xen-orchestra/lite/src/locales/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,6 @@
"following-hosts-unreachable": "Die folgenden Hosts sind nicht erreichbar",
"force-reboot": "Neustart erzwingen",
"force-shutdown": "Herunterfahren erzwingen",
"fullscreen": "Vollbild",
"fullscreen-leave": "Vollbild schließen",
"gateway": "Gateway",
"n-gb-left": "{n} GB frei",
Expand Down Expand Up @@ -137,7 +136,6 @@
"object": "Objekt",
"ok": "OK",
"on-object": "auf {object}",
"open-console-in-new-tab": "Konsole in neuem Reiter öffnen",
"or": "Oder",
"page-not-found": "Diese Seite wurde nicht gefunden…",
"password": "Passwort",
Expand Down Expand Up @@ -167,7 +165,6 @@
"select-compression": "Kompression auswählen",
"select-destination-host": "Zielhost auswählen",
"selected-vms-in-execution": "Einige der ausgewählten VMs sind eingeschaltet",
"send-ctrl-alt-del": "Ctrl+Alt+Del senden",
"send-us-feedback": "Gib uns Feedback",

"select.network": "Ein Netzwerk auswählen",
Expand Down
3 changes: 0 additions & 3 deletions @xen-orchestra/lite/src/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,6 @@
"following-hosts-unreachable": "The following hosts are unreachable",
"force-reboot": "Force reboot",
"force-shutdown": "Force shutdown",
"fullscreen": "Fullscreen",
"fullscreen-leave": "Leave fullscreen",
"gateway": "Gateway",
"n-gb-left": "{n} GB left",
Expand Down Expand Up @@ -139,7 +138,6 @@
"object": "Object",
"ok": "OK",
"on-object": "on {object}",
"open-console-in-new-tab": "Open console in new tab",
"or": "Or",
"page-not-found": "This page is not to be found…",
"password": "Password",
Expand Down Expand Up @@ -169,7 +167,6 @@
"select-compression": "Select a compression",
"select-destination-host": "Select a destination host",
"selected-vms-in-execution": "Some selected VMs are running",
"send-ctrl-alt-del": "Send Ctrl+Alt+Del",
"send-us-feedback": "Send us feedback",

"select.network": "Select a network",
Expand Down
3 changes: 0 additions & 3 deletions @xen-orchestra/lite/src/locales/fa.json
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,6 @@
"following-hosts-unreachable": "میزبان های زیر در دسترس نیستند",
"force-reboot": "راه اندازی مجدد اجباری",
"force-shutdown": "خاموش کردن اجباری",
"fullscreen": "تمام صفحه",
"fullscreen-leave": "خروج از حالت تمام صفحه",
"gateway": "دروازه",
"n-gb-left": "{n} GB باقی مانده است",
Expand Down Expand Up @@ -137,7 +136,6 @@
"object-not-found": "شیء {id} پیدا نمی شود…",
"ok": "اوکی",
"on-object": "در {object}",
"open-console-in-new-tab": "باز کردن کنسول در برگه جدید",
"or": "یا",
"page-not-found": "این صفحه پیدا نمی شود…",
"password": "کلمه عبور",
Expand Down Expand Up @@ -167,7 +165,6 @@
"select-compression": "یک فشرده سازی انتخاب کنید",
"select-destination-host": "یک میزبان مقصد انتخاب کنید",
"selected-vms-in-execution": "برخی از ماشین های مجازی انتخاب شده در حال اجرا هستند",
"send-ctrl-alt-del": "ارسال کلیدهای Ctrl+Alt+Del",
"send-us-feedback": "برای ما بازخورد ارسال کنید",

"select.network": "یک شبکه انتخاب کنید",
Expand Down
3 changes: 0 additions & 3 deletions @xen-orchestra/lite/src/locales/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,6 @@
"following-hosts-unreachable": "Les hôtes suivants sont inaccessibles",
"force-reboot": "Forcer le redémarrage",
"force-shutdown": "Forcer l'arrêt",
"fullscreen": "Plein écran",
"fullscreen-leave": "Quitter plein écran",
"gateway": "Passerelle",
"n-gb-left": "{n} Go libres",
Expand Down Expand Up @@ -139,7 +138,6 @@
"object": "Objet",
"ok": "OK",
"on-object": "sur {object}",
"open-console-in-new-tab": "Ouvrir la console dans un nouvel onglet",
"or": "Ou",
"page-not-found": "Cette page est introuvable…",
"password": "Mot de passe",
Expand Down Expand Up @@ -169,7 +167,6 @@
"select-compression": "Sélectionnez une compression",
"select-destination-host": "Sélectionnez un hôte de destination",
"selected-vms-in-execution": "Certaines VMs sélectionnées sont en cours d'exécution",
"send-ctrl-alt-del": "Envoyer Ctrl+Alt+Suppr",
"send-us-feedback": "Envoyez-nous vos commentaires",

"select.network": "Sélectionner un réseau",
Expand Down
101 changes: 31 additions & 70 deletions @xen-orchestra/lite/src/views/vm/VmConsoleView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,24 @@
<UiSpinner v-else-if="!isReady" class="spinner" />
<UiStatusPanel v-else-if="!isVmRunning" :image-source="monitor" :title="$t('power-on-vm-for-console')" />
<template v-else-if="vm && vmConsole">
<MenuList horizontal>
<MenuItem v-if="uiStore.hasUi" :icon="faArrowUpRightFromSquare" @click="openInNewTab">
{{ $t('open-console-in-new-tab') }}
</MenuItem>
<MenuItem
:icon="uiStore.hasUi ? faUpRightAndDownLeftFromCenter : faDownLeftAndUpRightToCenter"
@click="toggleFullScreen"
>
{{ $t(uiStore.hasUi ? 'fullscreen' : 'fullscreen-leave') }}
</MenuItem>
<MenuItem :disabled="!consoleElement" :icon="faKeyboard" @click="sendCtrlAltDel">
{{ $t('send-ctrl-alt-del') }}
</MenuItem>
</MenuList>
<RemoteConsole
ref="consoleElement"
:is-console-available="isConsoleAvailable"
:location="vmConsole.location"
class="remote-console"
/>
<VtsLayoutConsole>
<RemoteConsole
ref="consoleElement"
:is-console-available="isConsoleAvailable"
:location="vmConsole.location"
class="remote-console"
/>
<template #actions>
<VtsActionsConsole
:open-in-new-tab="openInNewTab"
:send-ctrl-alt-del="sendCtrlAltDel"
:toggle-full-screen="toggleFullScreen"
:is-fullscreen="!uiStore.hasUi"
/>
<VtsDivider type="stretch" />
<VtsClipboardConsole />
</template>
</VtsLayoutConsole>
</template>
</div>
</template>
Expand All @@ -39,15 +37,13 @@ import type { XenApiVm } from '@/libs/xen-api/xen-api.types'
import { usePageTitleStore } from '@/stores/page-title.store'
import { useConsoleStore } from '@/stores/xen-api/console.store'
import { useVmStore } from '@/stores/xen-api/vm.store'
import MenuItem from '@core/components/menu/MenuItem.vue'
import MenuList from '@core/components/menu/MenuList.vue'
import VtsActionsConsole from '@core/components/console/VtsActionsConsole.vue'
import VtsClipboardConsole from '@core/components/console/VtsClipboardConsole.vue'
import VtsLayoutConsole from '@core/components/console/VtsLayoutConsole.vue'
import VtsDivider from '@core/components/divider/VtsDivider.vue'
import { useUiStore } from '@core/stores/ui.store'
import {
faArrowUpRightFromSquare,
faDownLeftAndUpRightToCenter,
faKeyboard,
faUpRightAndDownLeftFromCenter,
} from '@fortawesome/free-solid-svg-icons'
import { useActiveElement, useMagicKeys, whenever } from '@vueuse/core'
import { logicAnd } from '@vueuse/math'
import { computed, ref } from 'vue'
import { useI18n } from 'vue-i18n'
import { useRoute, useRouter } from 'vue-router'
Expand Down Expand Up @@ -110,6 +106,13 @@ const openInNewTab = () => {
const routeData = router.resolve({ query: { ui: '0' } })
window.open(routeData.href, '_blank')
}
const { escape } = useMagicKeys()
const activeElement = useActiveElement()
const canClose = computed(
() => (activeElement.value == null || activeElement.value.tagName !== 'CANVAS') && !uiStore.hasUi
)
whenever(logicAnd(escape, canClose), toggleFullScreen)
</script>

<style lang="postcss" scoped>
Expand All @@ -136,46 +139,4 @@ const openInNewTab = () => {
max-width: 100%;
height: 100%;
}
.not-available {
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
text-align: center;
gap: 4rem;
color: var(--color-info-txt-base);
font-size: 3.6rem;
}
.open-in-new-window {
position: absolute;
top: 0;
right: 0;
overflow: hidden;
& > .link {
display: flex;
align-items: center;
gap: 1rem;
background-color: var(--color-info-txt-base);
color: var(--color-info-txt-item);
text-decoration: none;
padding: 1.5rem;
font-size: 1.6rem;
border-radius: 0 0 0 0.8rem;
white-space: nowrap;
transform: translateX(calc(100% - 4.5rem));
transition: transform 0.2s ease-in-out;
&:hover {
transform: translateX(0);
}
}
}
.vm-console-view:deep(.menu-list) {
background-color: transparent;
align-self: center;
}
</style>
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
<template>
<UiCardTitle>{{ $t('console-actions') }}</UiCardTitle>
<UiButton
v-tooltip="toggleFullScreen === undefined ? $t('coming-soon') : undefined"
class="button"
:disabled="toggleFullScreen === undefined"
accent="info"
variant="tertiary"
size="medium"
:left-icon="isFullscreen ? faDownLeftAndUpRightToCenter : faUpRightAndDownLeftFromCenter"
@click="toggleFullScreen"
>
{{ $t(isFullscreen ? 'exit-fullscreen' : 'fullscreen') }}
</UiButton>
<UiButton
v-tooltip="openInNewTab === undefined ? $t('coming-soon') : undefined"
class="button"
:disabled="openInNewTab === undefined"
accent="info"
variant="tertiary"
size="medium"
:left-icon="faArrowUpRightFromSquare"
@click="openInNewTab"
>
{{ $t('open-console-in-new-tab') }}
</UiButton>
<UiButton
v-tooltip="sendCtrlAltDel === undefined ? $t('coming-soon') : undefined"
class="button"
accent="info"
variant="tertiary"
size="medium"
:disabled="sendCtrlAltDel === undefined"
:left-icon="faKeyboard"
@click="sendCtrlAltDel"
>
{{ $t('send-ctrl-alt-del') }}
</UiButton>
</template>

<script lang="ts" setup>
import UiButton from '@core/components/ui/button/UiButton.vue'
import UiCardTitle from '@core/components/ui/card-title/UiCardTitle.vue'
import { vTooltip } from '@core/directives/tooltip.directive'
import {
faArrowUpRightFromSquare,
faDownLeftAndUpRightToCenter,
faKeyboard,
faUpRightAndDownLeftFromCenter,
} from '@fortawesome/free-solid-svg-icons'
// temporary undefined for xo6
defineProps<{
openInNewTab?: () => void
toggleFullScreen?: () => void
sendCtrlAltDel?: () => void
isFullscreen?: boolean
}>()
</script>

<style lang="postcss" scoped>
.button {
align-self: start;
gap: 0.8rem;
text-align: left;
}
</style>
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<template>
<div class="vts-clipboard-console">
<UiCardTitle>{{ $t('console-clipboard') }}</UiCardTitle>
<UiTextarea v-tooltip="$t('coming-soon')" accent="info" disabled :model-value="modelValue" />
<div class="buttons-container">
<UiButton v-tooltip="$t('coming-soon')" accent="info" variant="primary" size="medium" disabled>
{{ $t('send') }}
</UiButton>
<UiButton v-tooltip="$t('coming-soon')" accent="info" variant="secondary" size="medium" disabled>
{{ $t('receive') }}
</UiButton>
</div>
</div>
</template>

<script setup lang="ts">
import UiButton from '@core/components/ui/button/UiButton.vue'
import UiCardTitle from '@core/components/ui/card-title/UiCardTitle.vue'
import UiTextarea from '@core/components/ui/input/UiTextarea.vue'
import { vTooltip } from '@core/directives/tooltip.directive'
import { ref } from 'vue'
const modelValue = ref('')
</script>

<style lang="postcss" scoped>
.vts-clipboard-console {
display: flex;
flex-direction: column;
gap: 0.8rem;
width: 100%;
.buttons-container {
display: flex;
gap: 0.8rem;
}
}
</style>
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<template>
<div :class="uiStore.isMobile ? 'mobile' : undefined" class="vts-layout-console">
<slot />
<UiCard class="card">
<slot name="actions" />
</UiCard>
</div>
</template>

<script lang="ts" setup>
import UiCard from '@core/components/ui/card/UiCard.vue'
import { useUiStore } from '@core/stores/ui.store'
defineSlots<{
default(): any
actions(): any
}>()
const uiStore = useUiStore()
</script>

<style lang="postcss" scoped>
.vts-layout-console {
display: flex;
gap: 2.4rem;
height: 100%;
padding: 0.8rem;
&.mobile {
flex-direction: column;
}
.card {
height: fit-content;
gap: 1.6rem;
padding: 1.6rem;
width: 43rem;
}
}
</style>
Loading

0 comments on commit fa974f9

Please sign in to comment.