Optimize some code

- add support for right alt
- show message when image mounting failed
- rename virtual-usb to virtual-disk
- update mjpeg stream url
This commit is contained in:
wj-xiao
2024-09-25 10:34:11 +08:00
parent 46c41ff414
commit cfe8172364
15 changed files with 154 additions and 89 deletions

View File

@@ -2,9 +2,9 @@ export const languages = [
{ key: 'de', name: 'Deutsch' },
{ key: 'en', name: 'English' },
{ key: 'fr', name: 'Français' },
{ key: 'it', name: 'Italian' },
{ key: 'pl', name: 'Polski' },
{ key: 'ru', name: 'Русский' },
{ key: 'ko', name: '한국어' },
{ key: 'pl', name: 'Polski' },
{ key: 'zh', name: '中文' },
{ key: 'it', name: 'Italian' }
{ key: 'zh', name: '中文' }
];

View File

@@ -12,7 +12,8 @@ const de = {
powerLong: 'Einschalten (Langes Drücken)',
hddLed: 'Festplatten-LED',
checkLibFailed: 'Überprüfung der Laufzeitbibliothek fehlgeschlagen, bitte erneut versuchen.',
updateLibFailed: 'Aktualisierung der Laufzeitbibliothek fehlgeschlagen, bitte erneut versuchen.',
updateLibFailed:
'Aktualisierung der Laufzeitbibliothek fehlgeschlagen, bitte erneut versuchen.',
updatingLib: 'Aktualiserung der Laufzeitbibliothek. Bitte nach dem Update die Seite neu laden.',
checkForUpdate: 'Nach Update suchen',
head: {
@@ -28,7 +29,8 @@ const de = {
placeholderPassword2: 'Passwort wiederholen',
noEmptyUsername: 'Der Benutzername darf nicht leer sein',
noEmptyPassword: 'Das Passwort darf nicht leer sein',
noAccount: 'Fehler beim Auslsen der Benutzerinformation, bitte die Seite neu laden oder das Passwort zurücksetzen',
noAccount:
'Fehler beim Auslsen der Benutzerinformation, bitte die Seite neu laden oder das Passwort zurücksetzen',
invalidUser: 'Falscher Benutzername oder falsches Passwort',
error: 'Unerwarteter Fehler',
changePassword: 'Passwort ändern',
@@ -37,7 +39,8 @@ const de = {
illegalPassword: 'Passwort beinhaltet ungültige Zeichen',
forgetPassword: 'Passwort vergessen',
resetPassword: 'Password zurücksetzen',
reset1: 'Falls sie das Passwort vergessen haben, führen sie folgende Schritte durch um dieses zurückzusetzen:',
reset1:
'Falls sie das Passwort vergessen haben, führen sie folgende Schritte durch um dieses zurückzusetzen:',
reset2: '1. In das NanoKVM via SSH einloggen;',
reset3: '2. Die Datei auf dem Gerät löschen: ',
reset4: '3. Den Standardaccount zum Einloggen benutzen: ',
@@ -48,13 +51,13 @@ const de = {
resolution: 'Auflösung',
auto: 'Automatisch',
autoTips:
"Falls es bei bestimmten Auflösungen zu Screen-Tearing oder einem Offset des Mauszeigers kommen sollte, bitte die Auflösung des Remote Hosts anpassen oder den automatischen Modus ausschalten.",
'Falls es bei bestimmten Auflösungen zu Screen-Tearing oder einem Offset des Mauszeigers kommen sollte, bitte die Auflösung des Remote Hosts anpassen oder den automatischen Modus ausschalten.',
fps: 'FPS',
customizeFps: 'Anpassen',
quality: 'Qualität',
frameDetect: 'Frame Detect',
frameDetectTip:
"Berechnet den Unterschied zwischen den Einzelbildern. Beendet die Liveübertragung des Videostreams wenn keine Änderungen auf dem Bildschirm des Hosts festgestellt werden kann."
'Berechnet den Unterschied zwischen den Einzelbildern. Beendet die Liveübertragung des Videostreams wenn keine Änderungen auf dem Bildschirm des Hosts festgestellt werden kann.'
},
keyboard: {
paste: 'Einfügen',
@@ -73,18 +76,22 @@ const de = {
mode: 'Maus Modus',
absolute: 'Absoluter Modus',
relative: 'Relativer Modus',
requestPointer: 'Der Relative Modus wird benutzt. Bitte auf den Desktop klicken um einen Mauszeiger anzuzeigen.',
requestPointer:
'Der Relative Modus wird benutzt. Bitte auf den Desktop klicken um einen Mauszeiger anzuzeigen.',
resetHid: 'Eingeabegeräte zurücksetzen'
},
image: {
title: 'ISO oder Festplatten Abbilder',
loading: 'Laden...',
empty: 'Nichts gefunden',
mountFailed: 'Mounten fehlgeschlagen',
mountDesc:
'Bei einigen Systemen muss die virtuelle Festplatte auf dem Remote-Host ausgeworfen werden, bevor das Image gemountet werden kann.',
tips: {
title: 'Wie man Abbilder hochlädt',
usb1: 'NanoKVM per USB mit dem Computer verbindenn.',
usb2: 'Stellen sie sicher, das das Virtuelle USB-Gerät eingebunden ist. (Einstellungen - Virtuelles USB-Gerät).',
usb3: 'Öffnen sie die virtuelle USB-Disk auf ihrem Computer und kopieren sie die Abbilddatei in das root-Verzeichnis des USB-Geräts.',
usb1: 'Den NanoKVM via USB mit dem Computer verbinden.',
usb2: 'Stellen sie sicher, das das Virtuelle Festplatte eingebunden ist. (Einstellungen - Virtuelle Festplatte).',
usb3: 'Öffnen sie die Virtuelle Festplatte auf ihrem Computer und kopieren sie die Abbilddatei in das root-Verzeichnis des USB-Geräts.',
scp1: 'Stellen sie sicher, das sich der NanoKVM und ihr Computer im gleichen lokalen Netzwerk befinden.',
scp2: 'Öffnen sie ein Terminal auf ihrem Computer und benutzen sie den SCP-Befehl um die Abbilddatei in das /data Verzeichnis des NanoKVM zu kopieren.',
scp3: 'Beispiel: scp ihr-abbild-verzeichnis root@ihre-nanokvm-ip:/data',
@@ -140,14 +147,15 @@ const de = {
queryFailed: 'Versionsnummer konnte nicht erkannt werden',
updateFailed: 'Update gescheitert. Bitte versuchen sie es erneut.',
isLatest: 'Die aktuelle Version ist bereits installiert.',
available: 'Ein Update ist verfügbar. Sind sie sicher, dass sie diese Version aktualisieren möchten?',
available:
'Ein Update ist verfügbar. Sind sie sicher, dass sie diese Version aktualisieren möchten?',
updating: 'Update wird gestartet. Bitte warten...',
confirm: 'Bestätigen',
cancel: 'Abbrechen'
},
virtualDevice: {
network: 'Virtuelles Netzwerk',
usb: 'Virtuelles USB Gerät'
disk: 'Virtuelle Festplatte'
},
tailscale: {
loading: 'Lade...',
@@ -155,7 +163,8 @@ const de = {
install: 'Installieren',
installing: 'Installation wird durchgeführt',
failed: 'Installation gescheitert',
retry: 'Bitte neu laden und noch einmal versuchen. Oder versuchen sie Tailscale manuell zu installieren',
retry:
'Bitte neu laden und noch einmal versuchen. Oder versuchen sie Tailscale manuell zu installieren',
download: 'Laden sie das',
package: 'Installationspaket',
unzip: 'und entpacken sie es manuell',

View File

@@ -80,11 +80,14 @@ const en = {
title: 'Images',
loading: 'Loading...',
empty: 'Nothing Found',
mountFailed: 'Mount Failed',
mountDesc:
"In some systems, it's necessary to eject the virtual disk on the remote host before mounting the image.",
tips: {
title: 'How to upload',
usb1: 'Connect the NanoKVM to your computer via USB.',
usb2: 'Ensure that the Virtual USB is mounted (Settings - Virtual USB).',
usb3: 'Open the virtual usb disk on your computer and copy the image file to the root directory of the usb disk.',
usb2: 'Ensure that the virtual disk is mounted (Settings - Virtual Disk).',
usb3: 'Open the virtual disk on your computer and copy the image file to the root directory of the virtual disk.',
scp1: 'Make sure the NanoKVM and your computer are on the same local network.',
scp2: 'Open a terminal on your computer and use the SCP command to upload the image file to the /data directory on the NanoKVM.',
scp3: 'Example: scp your-image-path root@your-nanokvm-ip:/data',
@@ -147,7 +150,7 @@ const en = {
},
virtualDevice: {
network: 'Virtual Network',
usb: 'Virtual USB'
disk: 'Virtual Disk'
},
tailscale: {
loading: 'Loading...',

View File

@@ -84,11 +84,14 @@ const fr = {
title: 'Images',
loading: 'Chargement',
empty: 'Vide',
mountFailed: "Échec du montage de l'image.",
mountDesc:
"Dans certains systèmes, il est nécessaire de déséjecter le disque virtuel sur l'hôte distant avant de monter l'image.",
tips: {
title: 'Comment télécharger',
usb1: 'Connectez le NanoKVM à votre ordinateur via USB.',
usb2: 'Assurez-vous que le disque virtuel est monté (Paramètres - Disque virtuel).',
usb3: 'Ouvrez le disque virtuel sur votre ordinateur et copiez le fichier image dans le répertoire racine du disque.',
usb3: 'Ouvrez le disque virtuel sur votre ordinateur et copiez le fichier image dans le répertoire racine du disque virtuel.',
scp1: 'Assurez-vous que le NanoKVM et votre ordinateur sont sur le même réseau local.',
scp2: 'Ouvrez un terminal sur votre ordinateur et utilisez la commande SCP pour télécharger le fichier image dans le répertoire /data du NanoKVM.',
scp3: 'Exemple : scp chemin-de-votre-image root@ip-de-votre-nanokvm:/data',
@@ -150,8 +153,8 @@ const fr = {
cancel: 'Annuler'
},
virtualDevice: {
network: 'Réseau virtuel',
usb: 'USB virtuel'
network: 'carte réseau virtuelle',
disk: 'disque virtuel'
},
tailscale: {
loading: 'Chargement...',

View File

@@ -13,7 +13,7 @@ const it = {
hddLed: 'LED HDD',
checkLibFailed: 'Impossibile verificare la libreria runtime, riprova',
updateLibFailed: 'Impossibile aggiornare la libreria runtime, riprova',
updatingLib: 'Aggiornamento libreria runtime. Aggiorna la pagina dopo l\'aggiornamento.',
updatingLib: "Aggiornamento libreria runtime. Aggiorna la pagina dopo l'aggiornamento.",
checkForUpdate: 'Controlla Aggiornamenti',
head: {
desktop: 'Desktop Remoto',
@@ -28,7 +28,8 @@ const it = {
placeholderPassword2: 'Inserisci nuovamente la password',
noEmptyUsername: 'Il nome utente non può essere vuoto',
noEmptyPassword: 'La password non può essere vuota',
noAccount: 'Impossibile ottenere informazioni utente, aggiorna la pagina o reimposta la password',
noAccount:
'Impossibile ottenere informazioni utente, aggiorna la pagina o reimposta la password',
invalidUser: 'Nome utente o password non validi',
error: 'Errore imprevisto',
changePassword: 'Cambia Password',
@@ -40,19 +41,21 @@ const it = {
reset1: 'Se hai dimenticato la password, segui questi passaggi per reimpostarla:',
reset2: '1. Accedi al dispositivo NanoKVM tramite SSH;',
reset3: '2. Elimina il file nel dispositivo: ',
reset4: '3. Usa l\'account predefinito per accedere: ',
reset4: "3. Usa l'account predefinito per accedere: ",
ok: 'Ok',
cancel: 'Annulla'
},
screen: {
resolution: 'Risoluzione',
auto: 'Automatico',
autoTips: 'Potrebbero verificarsi tearing dello schermo o spostamento del mouse a risoluzioni specifiche. Considera di regolare la risoluzione del dispositivo remoto o disabilitare la modalità automatica.',
autoTips:
'Potrebbero verificarsi tearing dello schermo o spostamento del mouse a risoluzioni specifiche. Considera di regolare la risoluzione del dispositivo remoto o disabilitare la modalità automatica.',
fps: 'FPS',
customizeFps: 'Personalizza',
quality: 'Qualità',
frameDetect: 'Rilevamento Frame',
frameDetectTip: 'Calcola la differenza tra i frame. Interrompe la trasmissione del flusso video quando non vengono rilevate modifiche sullo schermo del dispositivo remoto.'
frameDetectTip:
'Calcola la differenza tra i frame. Interrompe la trasmissione del flusso video quando non vengono rilevate modifiche sullo schermo del dispositivo remoto.'
},
keyboard: {
paste: 'Incolla',
@@ -71,18 +74,22 @@ const it = {
mode: 'Modalità mouse',
absolute: 'Modalità assoluta',
relative: 'Modalità relativa',
requestPointer: 'Usando la modalità relativa. Clicca sul desktop per ottenere il puntatore del mouse.',
requestPointer:
'Usando la modalità relativa. Clicca sul desktop per ottenere il puntatore del mouse.',
resetHid: 'Reimposta HID'
},
image: {
title: 'Immagini',
loading: 'Caricamento...',
empty: 'Nessun risultato',
mountFailed: 'Montaggio immagine fallito',
mountDesc:
"In alcuni sistemi, è necessario espellere il disco virtuale sull'host remoto prima di montare l'immagine.",
tips: {
title: 'Come caricare',
usb1: 'Collega il NanoKVM al tuo computer tramite USB.',
usb2: 'Assicurati che la Virtual USB sia montata (Impostazioni - Virtual USB).',
usb3: 'Apri il disco virtuale usb sul tuo computer e copia il file immagine nella directory principale del disco usb.',
usb2: 'Assicurati che la Virtual Disk sia montata (Impostazioni - Virtual Disk).',
usb3: 'Apri il disk virtuale sul tuo computer e copia il file immagine nella directory principale del disk.',
scp1: 'Assicurati che il NanoKVM e il tuo computer siano sulla stessa rete locale.',
scp2: 'Apri un terminale sul tuo computer e usa il comando SCP per caricare il file immagine nella directory /data del NanoKVM.',
scp3: 'Esempio: scp il-tuo-percorso-immagine root@il-tuo-ip-nanokvm:/data',
@@ -145,7 +152,7 @@ const it = {
},
virtualDevice: {
network: 'Rete Virtuale',
usb: 'USB Virtuale'
usb: 'Disk Virtuale'
},
tailscale: {
loading: 'Caricamento...',
@@ -160,7 +167,8 @@ const it = {
upTailscale: 'Carica tailscale nella directory /usr/bin/ del NanoKVM',
upTailscaled: 'Carica tailscaled nella directory /usr/sbin/ del NanoKVM',
refresh: 'Aggiorna la pagina corrente',
notLogin: 'Il dispositivo non è ancora stato associato. Effettua il login e associa questo dispositivo al tuo account.',
notLogin:
'Il dispositivo non è ancora stato associato. Effettua il login e associa questo dispositivo al tuo account.',
urlPeriod: 'Questo URL è valido per 10 minuti',
login: 'Accedi',
loginSuccess: 'Accesso riuscito',

View File

@@ -81,11 +81,14 @@ const ko = {
title: '이미지',
loading: 'Loading...',
empty: '아무것도 없습니다.',
mountFailed: '이미지 마운트 실패',
mountDesc:
'일부 시스템에서는 이미지를 마운트하기 전에 원격 호스트에서 가상 디스크를 제거해야 합니다.',
tips: {
title: '업로드 방법',
usb1: 'USB를 통해 NanoKVM을 컴퓨터에 연결하세요.',
usb2: '가상 USB가 마운트되어 있는지 확인하세요(설정 - 가상 USB).',
usb3: '컴퓨터에서 가상 USB 디스크를 열고 이미지 파일을 USB 디스크의 루트 디렉터리에 복사하세요.',
usb2: '가상 디스크가 마운트되었는지 확인하세요. (설정 - 가상 디스크).',
usb3: '컴퓨터에서 가상 디스크를 열고 이미지 파일을 가상 디스크의 루트 디렉토리로 복사하세요.',
scp1: 'NanoKVM과 컴퓨터가 동일한 로컬 네트워크에 있는지 확인하세요.',
scp2: '컴퓨터에서 터미널을 열고 SCP 명령을 사용하여 이미지 파일을 NanoKVM의 /data 디렉터리에 업로드하세요.',
scp3: '예: scp your-image-path root@your-nanokvm-ip:/data',
@@ -147,8 +150,8 @@ const ko = {
cancel: '취소'
},
virtualDevice: {
network: '가상 네트워크',
usb: '가상 USB'
network: '가상 네트워크 카드',
disk: '가상 디스크'
},
tailscale: {
loading: 'Loading...',

View File

@@ -28,7 +28,8 @@ const pl = {
placeholderPassword2: 'wprowadź hasło ponownie',
noEmptyUsername: 'nazwa użykownika nie może być pusta',
noEmptyPassword: 'hasło nie może być puste',
noAccount: 'Nie udało się uzyskać informacji o użytkowniku, odśwież stronę lub zresetuj hasło',
noAccount:
'Nie udało się uzyskać informacji o użytkowniku, odśwież stronę lub zresetuj hasło',
invalidUser: 'Błędne hasło lub nazwa użykownika',
error: 'niespodziewany błąd',
changePassword: 'Zmień Hasło',
@@ -48,13 +49,13 @@ const pl = {
resolution: 'Rozdzielczość',
auto: 'Automatyczny',
autoTips:
"W określonych rozdzielczościach może wystąpić rozrywanie ekranu lub przesunięcie myszy. Rozważ dostosowanie rozdzielczości zdalnego hosta lub wyłącz tryb automatyczny.",
'W określonych rozdzielczościach może wystąpić rozrywanie ekranu lub przesunięcie myszy. Rozważ dostosowanie rozdzielczości zdalnego hosta lub wyłącz tryb automatyczny.',
fps: 'FPS',
customizeFps: 'Personalizuj',
quality: 'Jakość',
frameDetect: 'Wykrywanie klatek',
frameDetectTip:
"Obliczanie różnicy między klatkami. Zatrzymaj transmisję strumienia wideo, gdy na ekranie zdalnego hosta nie zostaną wykryte żadne zmiany."
'Obliczanie różnicy między klatkami. Zatrzymaj transmisję strumienia wideo, gdy na ekranie zdalnego hosta nie zostaną wykryte żadne zmiany.'
},
keyboard: {
paste: 'Wklej',
@@ -80,11 +81,14 @@ const pl = {
title: 'Obrazy',
loading: 'Ładowanie...',
empty: 'Nic nie znaleziono',
mountFailed: 'Nie udało się zamontować obrazu',
mountDesc:
'W niektórych systemach wymagane jest wyjęcie dysku wirtualnego na zdalnym hoście przed zamontowaniem obrazu.',
tips: {
title: 'Jak przesłać obrazy',
usb1: 'Podłącz urządzenie NanoKVM do komputera przez USB.',
usb2: 'Upewnij się że Wirtualny USB jest zamontowany (Ustawienia - Wirtualny USB).',
usb3: 'Otwórz wirtualny dysk USB na komputerze i prześlij obraz do katalogu głównego (tzw. root) dysku USB.',
usb2: 'Upewnij się, że dysk wirtualny jest zamontowany (Ustawienia - Dysk wirtualny).',
usb3: 'Otwórz dysk wirtualny na swoim komputerze i skopiuj plik obrazu do katalogu głównego dysku wirtualnego.',
scp1: 'Upewnij się że NanoKVM i twój komputer są na tej samej sieci lokalnej.',
scp2: 'Otwórz terminal na komputerze i użyj komendę SCP aby przesłać obraz do katalogu /data na NanoKVM.',
scp3: 'Przykład: scp lokalizacja-zrodlowego-obrazu root@ip-twojego-nanokvm:/data',
@@ -147,7 +151,7 @@ const pl = {
},
virtualDevice: {
network: 'Sieć wirtualna',
usb: 'Wirtualny USB'
usb: 'Dysk wirtualny'
},
tailscale: {
loading: 'Ładowanie...',

View File

@@ -85,11 +85,14 @@ const ru = {
title: 'Образы',
loading: 'Загрузка...',
empty: 'Пусто',
mountFailed: 'Монтирование образа не удалось',
mountDesc:
'В некоторых системах необходимо отсоединить виртуальный диск на удаленном хосте перед монтированием образа.',
tips: {
title: 'Как загрузить',
usb1: 'Подключите NanoKVM к вашему компьютеру через USB.',
usb2: 'Убедитесь что Виртуальный USB-накопитель смонтирован (Настройки - Виртуальный USB-накопитель).',
usb3: 'Откройте виртуальный USB-накопитель на вашем компьютере и скопируйте файл образа в корневую директорию USB-накопителя.',
usb2: 'Убедитесь, что виртуальный диск подключен (Настройки - Виртуальный диск).',
usb3: 'Откройте виртуальный диск на вашем компьютере и скопируйте файл образа в корневой каталог диска.',
scp1: 'Убедитесь что NanoKVM и ваш компьютер находятся в одной и той же сети.',
scp2: 'Откройте терминал на вашем компьютере и используйте команду scp чтобы загрузить файл образа в директорию /data, находящуюся на NanoKVM.',
scp3: 'Например: scp путь-к-образу root@ip-вашего-nanokvm:/data',
@@ -151,8 +154,8 @@ const ru = {
cancel: 'Отмена'
},
virtualDevice: {
network: 'Виртуальная сеть',
usb: 'Виртуальный USB-накопитель'
network: 'виртуальная сетевая карта',
disk: 'виртуальный диск'
},
tailscale: {
loading: 'Загрузка...',

View File

@@ -79,11 +79,13 @@ const zh = {
title: '镜像',
loading: '加载中',
empty: '无镜像文件',
mountFailed: '挂载失败',
mountDesc: '在某些系统中,需要在远程主机中弹出虚拟硬盘后再挂载镜像。',
tips: {
title: '如何上传',
usb1: '将 NanoKVM 通过 USB 连接到你的电脑;',
usb2: '确保已经挂载了虚拟U盘(设置 - 虚拟U盘);',
usb3: '在电脑上打开虚拟U盘,将镜像文件拷贝到虚拟U盘的根目录下。',
usb2: '确保已经挂载了虚拟硬盘(设置 - 虚拟硬盘);',
usb3: '在电脑上打开虚拟硬盘,将镜像文件拷贝到虚拟硬盘的根目录下。',
scp1: '确保 NanoKVM 和你的电脑在同一个局域网内;',
scp2: '在电脑上打开终端软件,使用 SCP 命令将镜像文件上传到 NanoKVM 的 /data 目录。',
scp3: '示例:scp your-image-path root@your-nanokvm-ip:/data',
@@ -145,8 +147,8 @@ const zh = {
cancel: '取消'
},
virtualDevice: {
network: '虚拟网口',
usb: '虚拟U盘'
network: '虚拟网卡',
disk: '虚拟硬盘'
},
tailscale: {
loading: '加载中...',

View File

@@ -49,7 +49,7 @@ export const Desktop = () => {
} else {
setIsLoading(false);
const now = Date.now();
setStreamUrl(`${getBaseUrl('http')}/api/stream/mjpeg?${now}`);
setStreamUrl(`${getBaseUrl('http')}/api/stream/mjpeg?n=${now}`);
}
});

View File

@@ -5,7 +5,8 @@ import { client } from '@/lib/websocket.ts';
import { KeyboardCodes } from './mappings.ts';
export const Keyboard = () => {
const metaRef = useRef(false);
const isMetaPressedRef = useRef(false);
const isAltRightPressedRef = useRef(false);
// listen keyboard events
useEffect(() => {
@@ -16,11 +17,15 @@ export const Keyboard = () => {
function handleKeyDown(event: any) {
disableEvent(event);
if (event.code === 'MetaLeft' || event.code === 'MetaRight') {
metaRef.current = true;
if (['MetaLeft', 'MetaRight'].includes(event.code)) {
isMetaPressedRef.current = true;
return;
}
metaRef.current = false;
isMetaPressedRef.current = false;
if (event.code === 'AltRight') {
isAltRightPressedRef.current = true;
}
const code = KeyboardCodes.get(event.code);
if (!code) {
@@ -30,7 +35,7 @@ export const Keyboard = () => {
const ctrl = event.ctrlKey ? 1 : 0;
const shift = event.shiftKey ? 1 : 0;
const alt = event.altKey ? 1 : 0;
const alt = event.altKey ? (isAltRightPressedRef.current ? 2 : 1) : 0;
const meta = event.metaKey ? 1 : 0;
const data = [1, code, ctrl, shift, alt, meta];
@@ -41,13 +46,16 @@ export const Keyboard = () => {
function handleKeyUp(event: any) {
disableEvent(event);
if (metaRef.current) {
if (isMetaPressedRef.current) {
client.send([1, KeyboardCodes.get('MetaLeft')!, 0, 0, 0, 1]);
metaRef.current = false;
isMetaPressedRef.current = false;
}
const data = [1, 0, 0, 0, 0, 0];
client.send(data);
if (event.code === 'AltRight') {
isAltRightPressedRef.current = false;
}
client.send([1, 0, 0, 0, 0, 0]);
}
return () => {

View File

@@ -1,4 +1,5 @@
import { useEffect, useState } from 'react';
import { notification } from 'antd';
import clsx from 'clsx';
import { FileBoxIcon, LoaderCircleIcon, PackageIcon, PackageSearchIcon, XIcon } from 'lucide-react';
import { useTranslation } from 'react-i18next';
@@ -12,6 +13,7 @@ type ImagesProps = {
export const Images = ({ setIsMounted }: ImagesProps) => {
const { t } = useTranslation();
const [notify, contextHolder] = notification.useNotification();
const [isLoading, setIsLoading] = useState(false);
const [images, setImages] = useState<string[]>([]);
@@ -72,6 +74,7 @@ export const Images = ({ setIsMounted }: ImagesProps) => {
.mountImage(filename)
.then((rsp) => {
if (rsp.code !== 0) {
openNotification();
return;
}
@@ -84,8 +87,18 @@ export const Images = ({ setIsMounted }: ImagesProps) => {
});
}
function openNotification() {
notify.open({
message: t('image.mountFailed'),
description: t('image.mountDesc'),
duration: 6
});
}
return (
<>
{contextHolder}
{isLoading ? (
// loading
<div className="flex items-center space-x-2 py-2 pl-2 pr-4 text-neutral-400">

View File

@@ -7,22 +7,21 @@ import * as api from '@/api/virtual-device';
export const VirtualDevices = () => {
const { t } = useTranslation();
const [isNetworkOn, setIsNetworkOn] = useState(false);
const [isUSBOn, setIsUSBOn] = useState(false);
const [loading, setLoading] = useState(''); // '' | 'network' | 'usb'
const [isDiskOn, setIsDiskOn] = useState(false);
const [loading, setLoading] = useState(''); // '' | 'network' | 'disk'
useEffect(() => {
api.getVirtualDevice().then((rsp) => {
if (rsp.code !== 0) {
console.log(rsp.msg);
return;
}
setIsNetworkOn(rsp.data.network);
setIsUSBOn(rsp.data.usb);
setIsDiskOn(rsp.data.usb);
});
}, []);
function update(device: string) {
function update(device: 'network' | 'disk') {
if (loading) return;
setLoading(device);
@@ -30,14 +29,13 @@ export const VirtualDevices = () => {
.updateVirtualDevice(device)
.then((rsp) => {
if (rsp.code !== 0) {
console.log(rsp.msg);
return;
}
if (device === 'network') {
setIsNetworkOn(rsp.data.on);
} else {
setIsUSBOn(rsp.data.on);
setIsDiskOn(rsp.data.on);
}
})
.finally(() => {
@@ -47,6 +45,14 @@ export const VirtualDevices = () => {
return (
<>
<div
className="flex cursor-pointer select-none items-center justify-between space-x-3 rounded px-3 py-1.5 text-white hover:bg-neutral-600"
onClick={() => update('disk')}
>
<div>{t('virtualDevice.disk')}</div>
<Switch size="small" checked={isDiskOn} loading={loading === 'disk'} />
</div>
<div
className="flex cursor-pointer select-none items-center justify-between space-x-3 rounded px-3 py-1.5 text-white hover:bg-neutral-600"
onClick={() => update('network')}
@@ -54,14 +60,6 @@ export const VirtualDevices = () => {
<div>{t('virtualDevice.network')}</div>
<Switch size="small" checked={isNetworkOn} loading={loading === 'network'} />
</div>
<div
className="flex cursor-pointer select-none items-center justify-between space-x-3 rounded px-3 py-1.5 text-white hover:bg-neutral-600"
onClick={() => update('usb')}
>
<div>{t('virtualDevice.usb')}</div>
<Switch size="small" checked={isUSBOn} loading={loading === 'usb'} />
</div>
</>
);
};

View File

@@ -1,4 +1,5 @@
import { useEffect, useState } from 'react';
import { notification } from 'antd';
import clsx from 'clsx';
import { FileBoxIcon, LoaderCircleIcon, PackageIcon, PackageSearchIcon, XIcon } from 'lucide-react';
import { useTranslation } from 'react-i18next';
@@ -12,6 +13,7 @@ type ImagesProps = {
export const Images = ({ setIsMounted }: ImagesProps) => {
const { t } = useTranslation();
const [notify, contextHolder] = notification.useNotification();
const [isLoading, setIsLoading] = useState(false);
const [images, setImages] = useState<string[]>([]);
@@ -72,6 +74,7 @@ export const Images = ({ setIsMounted }: ImagesProps) => {
.mountImage(filename)
.then((rsp) => {
if (rsp.code !== 0) {
openNotification();
return;
}
@@ -84,8 +87,18 @@ export const Images = ({ setIsMounted }: ImagesProps) => {
});
}
function openNotification() {
notify.open({
message: t('image.mountFailed'),
description: t('image.mountDesc'),
duration: 6
});
}
return (
<>
{contextHolder}
{isLoading ? (
// loading
<div className="flex items-center space-x-2 py-2 pl-2 pr-4 text-neutral-400">

View File

@@ -7,22 +7,21 @@ import * as api from '@/api/virtual-device';
export const VirtualDevices = () => {
const { t } = useTranslation();
const [isNetworkOn, setIsNetworkOn] = useState(false);
const [isUSBOn, setIsUSBOn] = useState(false);
const [loading, setLoading] = useState(''); // '' | 'network' | 'usb'
const [isDiskOn, setIsDiskOn] = useState(false);
const [loading, setLoading] = useState(''); // '' | 'network' | 'disk'
useEffect(() => {
api.getVirtualDevice().then((rsp) => {
if (rsp.code !== 0) {
console.log(rsp.msg);
return;
}
setIsNetworkOn(rsp.data.network);
setIsUSBOn(rsp.data.usb);
setIsDiskOn(rsp.data.disk);
});
}, []);
function update(device: string) {
function update(device: 'network' | 'disk') {
if (loading) return;
setLoading(device);
@@ -30,14 +29,13 @@ export const VirtualDevices = () => {
.updateVirtualDevice(device)
.then((rsp) => {
if (rsp.code !== 0) {
console.log(rsp.msg);
return;
}
if (device === 'network') {
setIsNetworkOn(rsp.data.on);
} else {
setIsUSBOn(rsp.data.on);
setIsDiskOn(rsp.data.on);
}
})
.finally(() => {
@@ -47,6 +45,14 @@ export const VirtualDevices = () => {
return (
<>
<div
className="flex cursor-pointer select-none items-center justify-between space-x-3 rounded px-3 py-1.5 text-white hover:bg-neutral-600"
onClick={() => update('disk')}
>
<div>{t('virtualDevice.disk')}</div>
<Switch size="small" checked={isDiskOn} loading={loading === 'disk'} />
</div>
<div
className="flex cursor-pointer select-none items-center justify-between space-x-3 rounded px-3 py-1.5 text-white hover:bg-neutral-600"
onClick={() => update('network')}
@@ -54,14 +60,6 @@ export const VirtualDevices = () => {
<div>{t('virtualDevice.network')}</div>
<Switch size="small" checked={isNetworkOn} loading={loading === 'network'} />
</div>
<div
className="flex cursor-pointer select-none items-center justify-between space-x-3 rounded px-3 py-1.5 text-white hover:bg-neutral-600"
onClick={() => update('usb')}
>
<div>{t('virtualDevice.usb')}</div>
<Switch size="small" checked={isUSBOn} loading={loading === 'usb'} />
</div>
</>
);
};