mirror of
https://github.com/sipeed/NanoKVM.git
synced 2026-11-09 17:44:43 -06:00
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:
@@ -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: '中文' }
|
||||
];
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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...',
|
||||
|
||||
@@ -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...',
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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...',
|
||||
|
||||
@@ -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...',
|
||||
|
||||
@@ -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: 'Загрузка...',
|
||||
|
||||
@@ -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: '加载中...',
|
||||
|
||||
@@ -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}`);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -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 () => {
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user