From 7f3f8694b9a40501f02ac5f890da50ada836b54a Mon Sep 17 00:00:00 2001 From: Marc Brooks Date: Fri, 3 Oct 2025 14:25:28 -0500 Subject: [PATCH 1/7] Add inlang/paraglide-js localization Localize the extension popovers. Update package and fix tsconfig.json Expand development directory guide Move messages under localization --- .devcontainer/docker/devcontainer.json | 10 +- .vscode/extensions.json | 25 + DEVELOPMENT.md | 68 +- ui/eslint.config.cjs | 5 +- ui/index.html | 26 +- ui/localization/jetKVM.UI.inlang/.gitignore | 1 + ui/localization/jetKVM.UI.inlang/project_id | 1 + .../jetKVM.UI.inlang/settings.json | 40 + ui/localization/messages/da.json | 66 + ui/localization/messages/de.json | 66 + ui/localization/messages/en.json | 64 + ui/localization/messages/es.json | 66 + ui/localization/messages/fr.json | 66 + ui/localization/messages/it.json | 66 + ui/localization/messages/nb.json | 66 + ui/localization/messages/sv.json | 66 + ui/localization/messages/zh.json | 66 + ui/package-lock.json | 1236 +++++++++++++---- ui/package.json | 45 +- ui/src/components/ActionBar.tsx | 44 +- .../components/extensions/ATXPowerControl.tsx | 36 +- .../components/extensions/DCPowerControl.tsx | 49 +- .../components/extensions/SerialConsole.tsx | 43 +- .../components/popovers/ExtensionPopover.tsx | 25 +- ui/src/main.tsx | 11 +- .../devices.$id.settings.general.reboot.tsx | 14 +- ui/src/routes/welcome-local.tsx | 18 +- ui/tsconfig.json | 44 +- ui/tsconfig.node.json | 11 +- ui/vite.config.ts | 11 + 30 files changed, 1859 insertions(+), 496 deletions(-) create mode 100644 .vscode/extensions.json create mode 100644 ui/localization/jetKVM.UI.inlang/.gitignore create mode 100644 ui/localization/jetKVM.UI.inlang/project_id create mode 100644 ui/localization/jetKVM.UI.inlang/settings.json create mode 100644 ui/localization/messages/da.json create mode 100644 ui/localization/messages/de.json create mode 100644 ui/localization/messages/en.json create mode 100644 ui/localization/messages/es.json create mode 100644 ui/localization/messages/fr.json create mode 100644 ui/localization/messages/it.json create mode 100644 ui/localization/messages/nb.json create mode 100644 ui/localization/messages/sv.json create mode 100644 ui/localization/messages/zh.json diff --git a/.devcontainer/docker/devcontainer.json b/.devcontainer/docker/devcontainer.json index 6a4e6ae0..2c7c6d17 100644 --- a/.devcontainer/docker/devcontainer.json +++ b/.devcontainer/docker/devcontainer.json @@ -8,7 +8,8 @@ } }, "mounts": [ - "source=${localEnv:HOME}/.ssh,target=/home/vscode/.ssh,type=bind,consistency=cached" + "source=${localEnv:HOME}/.ssh,target=/home/vscode/.ssh,type=bind,consistency=cached", + "source=/var/run/docker.sock,target=/var/run/docker.sock,type=bind" ], "onCreateCommand": ".devcontainer/install-deps.sh", "customizations": { @@ -31,8 +32,11 @@ // Frontend "esbenp.prettier-vscode", "dbaeumer.vscode-eslint", - "bradlc.vscode-tailwindcss" + "bradlc.vscode-tailwindcss", + "codeandstuff.package-json-upgrade", + // Localization + "inlang.vs-code-extension" ] } } -} +} \ No newline at end of file diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 00000000..d89bc64c --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,25 @@ +{ + "recommendations": [ + // coding styles + "chrislajoie.vscode-modelines", + "editorconfig.editorconfig", + // GitHub + "GitHub.vscode-pull-request-github", + "github.vscode-github-actions", + // Golang + "golang.go", + // C / C++ + "ms-vscode.cpptools", + "ms-vscode.cpptools-extension-pack", + // CMake / Makefile + "ms-vscode.makefile-tools", + "ms-vscode.cmake-tools", + // Frontend + "esbenp.prettier-vscode", + "dbaeumer.vscode-eslint", + "bradlc.vscode-tailwindcss", + "codeandstuff.package-json-upgrade", + // Localization + "inlang.vs-code-extension" + ] +} \ No newline at end of file diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md index 964526d6..5c906860 100644 --- a/DEVELOPMENT.md +++ b/DEVELOPMENT.md @@ -97,38 +97,42 @@ tail -f /var/log/jetkvm.log ``` /kvm/ -├── main.go # App entry point -├── config.go # Settings & configuration -├── display.go # Device UI control -├── web.go # API endpoints -├── cmd/ # Command line main -├── internal/ # Internal Go packages -│ ├── confparser/ # Configuration file implementation -│ ├── hidrpc/ # HIDRPC implementation for HID devices (keyboard, mouse, etc.) -│ ├── logging/ # Logging implementation -│ ├── mdns/ # mDNS implementation -│ ├── native/ # CGO / Native code glue layer (on-device hardware) -│ │ ├── cgo/ # C files for the native library (HDMI, Touchscreen, etc.) -│ │ └── eez/ # EEZ Studio Project files (for Touchscreen) -│ ├── network/ # Network implementation -│ ├── timesync/ # Time sync/NTP implementation -│ ├── tzdata/ # Timezone data and generation -│ ├── udhcpc/ # DHCP implementation -│ ├── usbgadget/ # USB gadget -│ ├── utils/ # SSH handling -│ └── websecure/ # TLS certificate management -├── resource/ # netboot iso and other resources -├── scripts/ # Bash shell scripts for building and deploying -└── static/ # (react client build output) -└── ui/ # React frontend - ├── public/ # UI website static images and fonts - └── src/ # Client React UI - ├── assets/ # UI in-page images - ├── components/ # UI components - ├── hooks/ # Hooks (stores, RPC handling, virtual devices) - ├── keyboardLayouts/ # Keyboard layout definitions - ├── providers/ # Feature flags - └── routes/ # Pages (login, settings, etc.) +├── main.go # App entry point +├── config.go # Settings & configuration +├── display.go # Device UI control +├── web.go # API endpoints +├── cmd/ # Command line main +├── internal/ # Internal Go packages +│ ├── confparser/ # Configuration file implementation +│ ├── hidrpc/ # HIDRPC implementation for HID devices (keyboard, mouse, etc.) +│ ├── logging/ # Logging implementation +│ ├── mdns/ # mDNS implementation +│ ├── native/ # CGO / Native code glue layer (on-device hardware) +│ │ ├── cgo/ # C files for the native library (HDMI, Touchscreen, etc.) +│ │ └── eez/ # EEZ Studio Project files (for Touchscreen) +│ ├── network/ # Network implementation +│ ├── timesync/ # Time sync/NTP implementation +│ ├── tzdata/ # Timezone data and generation +│ ├── udhcpc/ # DHCP implementation +│ ├── usbgadget/ # USB gadget +│ ├── utils/ # SSH handling +│ └── websecure/ # TLS certificate management +├── resource/ # netboot iso and other resources +├── scripts/ # Bash shell scripts for building and deploying +└── static/ # (react client build output) +└── ui/ # React frontend + ├── localization/ # Client UI localization (i18n) + │ ├── jetKVM.UI.inlang/ # Settings for inlang + │ └── messages/ # Messages localized + ├── public/ # UI website static images and fonts + └── src/ # Client React UI + ├── assets/ # UI in-page images + ├── components/ # UI components + ├── hooks/ # Hooks (stores, RPC handling, virtual devices) + ├── keyboardLayouts/ # Keyboard layout definitions + │ ├── paraglide/ # (localization compiled messages output) + ├── providers/ # Feature flags + └── routes/ # Pages (login, settings, etc.) ``` **Key files for beginners:** diff --git a/ui/eslint.config.cjs b/ui/eslint.config.cjs index 6e972586..8c6d57d6 100644 --- a/ui/eslint.config.cjs +++ b/ui/eslint.config.cjs @@ -66,7 +66,7 @@ module.exports = defineConfig([{ groups: ["builtin", "external", "internal", "parent", "sibling"], "newlines-between": "always", }], - + "@typescript-eslint/no-unused-vars": ["warn", { "argsIgnorePattern": "^_", "varsIgnorePattern": "^_" }], @@ -81,7 +81,10 @@ module.exports = defineConfig([{ map: [ ["@components", "./src/components"], ["@routes", "./src/routes"], + ["@hooks", "./src/hooks"], + ["@providers", "./src/providers"], ["@assets", "./src/assets"], + ["@localizations", "./localization/paraglide"], ["@", "./src"], ], diff --git a/ui/index.html b/ui/index.html index 3c6c5606..77936233 100644 --- a/ui/index.html +++ b/ui/index.html @@ -45,31 +45,39 @@ - + - + -
+
diff --git a/ui/localization/jetKVM.UI.inlang/.gitignore b/ui/localization/jetKVM.UI.inlang/.gitignore new file mode 100644 index 00000000..5e465967 --- /dev/null +++ b/ui/localization/jetKVM.UI.inlang/.gitignore @@ -0,0 +1 @@ +cache \ No newline at end of file diff --git a/ui/localization/jetKVM.UI.inlang/project_id b/ui/localization/jetKVM.UI.inlang/project_id new file mode 100644 index 00000000..07e358cc --- /dev/null +++ b/ui/localization/jetKVM.UI.inlang/project_id @@ -0,0 +1 @@ +TI1a2RjjH4qkImNj0w \ No newline at end of file diff --git a/ui/localization/jetKVM.UI.inlang/settings.json b/ui/localization/jetKVM.UI.inlang/settings.json new file mode 100644 index 00000000..efe14c88 --- /dev/null +++ b/ui/localization/jetKVM.UI.inlang/settings.json @@ -0,0 +1,40 @@ +{ + "$schema": "https://inlang.com/schema/project-settings", + "baseLocale": "en", + "sourceLanguageTag": "en", + "locales": [ + "en", + "da", + "de", + "es", + "fr", + "it", + "nb", + "sv", + "zh" + ], + "languageTags": [ + "en", + "da", + "de", + "es", + "fr", + "it", + "nb", + "sv", + "zh" + ], + "modules": [ + "https://cdn.jsdelivr.net/npm/@inlang/plugin-message-format@latest/dist/index.js", + "https://cdn.jsdelivr.net/npm/@inlang/plugin-m-function-matcher@latest/dist/index.js" + ], + "plugin.inlang.messageFormat": { + "pathPattern": "./messages/{locale}.json" + }, + "strategy": [ + "cookie", + "localStorage", + "preferredLanguage", + "baseLocale" + ] +} \ No newline at end of file diff --git a/ui/localization/messages/da.json b/ui/localization/messages/da.json new file mode 100644 index 00000000..50f6c500 --- /dev/null +++ b/ui/localization/messages/da.json @@ -0,0 +1,66 @@ +{ + "$schema": "https://inlang.com/schema/inlang-message-format", + "jetkvm": "JetKVM", + "oh_no": "Åh nej!", + "something_went_wrong": "Noget gik galt. Prøv igen senere, eller kontakt support.", + "jetkvm_logo": "JetKVM-logo", + "load": "Indlæs", + "unknown_error": "Ukendt fejl", + "action_bar_virtual_media": "Virtuelle medier", + "action_bar_paste_text": "Indsæt tekst", + "action_bar_web_terminal": "Webterminal", + "action_bar_wake_on_lan": "Vågn på LAN", + "action_bar_virtual_keyboard": "Virtuelt tastatur", + "action_bar_extension": "Udvidelse", + "action_bar_connection_stats": "Forbindelsesstatistik", + "action_bar_settings": "Indstillinger", + "action_bar_fullscreen": "Fuldskærm", + "action_bar_exit_fullscreen": "Afslut fuldskærm", + "extensions_popover_extensions": "Udvidelser", + "extension_popover_set_error_notification": "Kunne ikke angive aktiv udvidelse: {error}", + "extension_popover_unload_extension": "Fjern udvidelse", + "extension_popover_load_and_manage_extensions": "Indlæs og administrer dine udvidelser", + "extensions_atx_power_control": "ATX-strømstyring", + "extensions_atx_power_control_description": "Styr din maskines strømtilstand via ATX-strømstyring.", + "extensions_dc_power_control": "DC-strømstyring", + "extensions_dc_power_control_description": "Styr din DC-strømforlænger", + "extension_serial_console": "Seriel konsol", + "extension_serial_console_description": "Få adgang til din serielle konsoludvidelse", + "atx_power_control_get_state_error": "Kunne ikke hente ATX-strømtilstand: {error}", + "atx_power_control_send_action_error": "Kunne ikke sende ATX-strømfunktion {action} : {error}", + "atx_power_control_power_button": "Magt", + "atx_power_control_short_power_button": "Kort tryk", + "atx_power_control_long_power_button": "Langt tryk", + "atx_power_control_reset_button": "Nulstil", + "atx_power_control_power_led": "Strøm-LED", + "atx_power_control_hdd_led": "HDD-LED", + "dc_power_control_get_state_error": "Kunne ikke hente DC-strømtilstand: {error}", + "dc_power_control_set_power_state_error": "Kunne ikke sende DC-strømstatus til {enabled} : {error}", + "dc_power_control_set_restore_state_error": "Kunne ikke sende DC-strømgendannelsesstatus til {state} : {error}", + "dc_power_control_power_on_button": "Tænd", + "dc_power_control_power_off_button": "Sluk", + "dc_power_control_restore_power_state": "Gendan strømtab", + "dc_power_control_power_on_state": "Tænd", + "dc_power_control_power_off_state": "Sluk", + "dc_power_control_voltage": "Spænding", + "dc_power_control_voltage_unit": "V", + "dc_power_control_current": "Strøm", + "dc_power_control_current_unit": "A", + "dc_power_control_power": "Magt", + "dc_power_control_power_unit": "W", + "serial_console_get_state_error": "Kunne ikke hente indstillinger for seriel konsol: {error}", + "serial_console_set_power_state_error": "Kunne ikke indstille seriel konsolindstillinger til {settings} : {error}", + "serial_console_configure_description": "Konfigurer dine serielle konsolindstillinger", + "serial_console_open_console": "Åbn konsol", + "serial_console_baud_rate": "Baudhastighed", + "serial_console_data_bits": "Databits", + "serial_console_stop_bits": "Stopbits", + "serial_console_parity": "Paritet", + "serial_console_parity_even": "Lige paritet", + "serial_console_parity_odd": "Ulige paritet", + "serial_console_parity_none": "Ingen paritet", + "serial_console_parity_mark": "Mark Paritet", + "serial_console_parity_space": "Rumparitet", + "serial_console_get_settings_error": "Kunne ikke hente indstillinger for seriel konsol: {error}", + "serial_console_set_settings_error": "Kunne ikke indstille seriel konsolindstillinger til {settings} : {error}" +} \ No newline at end of file diff --git a/ui/localization/messages/de.json b/ui/localization/messages/de.json new file mode 100644 index 00000000..1d427372 --- /dev/null +++ b/ui/localization/messages/de.json @@ -0,0 +1,66 @@ +{ + "$schema": "https://inlang.com/schema/inlang-message-format", + "jetkvm": "JetKVM", + "oh_no": "Oh nein!", + "something_went_wrong": "Ein Fehler ist aufgetreten. Bitte versuchen Sie es später noch einmal oder wenden Sie sich an den Support.", + "jetkvm_logo": "JetKVM Logo", + "load": "Laden", + "unknown_error": "Unbekannter Fehler", + "action_bar_virtual_media": "Virtuelle Medien", + "action_bar_paste_text": "Text einfügen", + "action_bar_web_terminal": "Web-Terminal", + "action_bar_wake_on_lan": "Wake-on-LAN", + "action_bar_virtual_keyboard": "Virtuelle Tastatur", + "action_bar_extension": "Verlängerung", + "action_bar_connection_stats": "Verbindungsstatistiken", + "action_bar_settings": "Einstellungen", + "action_bar_fullscreen": "Vollbild", + "action_bar_exit_fullscreen": "Vollbildmodus beenden", + "extensions_popover_extensions": "Erweiterungen", + "extension_popover_set_error_notification": "Fehler beim Festlegen der aktiven Erweiterung: {error}", + "extension_popover_unload_extension": "Erweiterung entladen", + "extension_popover_load_and_manage_extensions": "Laden und verwalten Sie Ihre Erweiterungen", + "extensions_atx_power_control": "ATX-Stromsteuerung", + "extensions_atx_power_control_description": "Steuern Sie den Energiezustand Ihrer Maschine über die ATX-Energiesteuerung.", + "extensions_dc_power_control": "Gleichstromsteuerung", + "extensions_dc_power_control_description": "Steuern Sie Ihre DC-Stromerweiterung", + "extension_serial_console": "Serielle Konsole", + "extension_serial_console_description": "Greifen Sie auf Ihre serielle Konsolenerweiterung zu", + "atx_power_control_get_state_error": "ATX-Stromversorgungsstatus konnte nicht abgerufen werden: {error}", + "atx_power_control_send_action_error": "ATX-Stromversorgungsaktion {action} konnte nicht gesendet werden: {error}", + "atx_power_control_power_button": "Leistung", + "atx_power_control_short_power_button": "Kurzes Drücken", + "atx_power_control_long_power_button": "Langes Drücken", + "atx_power_control_reset_button": "Zurücksetzen", + "atx_power_control_power_led": "Betriebs-LED", + "atx_power_control_hdd_led": "Festplatten-LED", + "dc_power_control_get_state_error": "Der Gleichstromstatus konnte nicht abgerufen werden: {error}", + "dc_power_control_set_power_state_error": "Der DC-Stromversorgungsstatus konnte nicht an {enabled} werden: {error}", + "dc_power_control_set_restore_state_error": "Der Status zur Wiederherstellung der Gleichstromversorgung konnte nicht an {state} gesendet werden: {error}", + "dc_power_control_power_on_button": "Einschalten", + "dc_power_control_power_off_button": "Ausschalten", + "dc_power_control_restore_power_state": "Wiederherstellung nach Stromausfall", + "dc_power_control_power_on_state": "Einschalten", + "dc_power_control_power_off_state": "Ausschalten", + "dc_power_control_voltage": "Stromspannung", + "dc_power_control_voltage_unit": "V", + "dc_power_control_current": "Aktuell", + "dc_power_control_current_unit": "A", + "dc_power_control_power": "Leistung", + "dc_power_control_power_unit": "W", + "serial_console_get_state_error": "Die seriellen Konsoleneinstellungen konnten nicht abgerufen werden: {error}", + "serial_console_set_power_state_error": "Die Einstellungen der seriellen Konsole konnten nicht auf {settings} festgelegt werden: {error}", + "serial_console_configure_description": "Konfigurieren Sie die Einstellungen Ihrer seriellen Konsole", + "serial_console_open_console": "Konsole öffnen", + "serial_console_baud_rate": "Baudrate", + "serial_console_data_bits": "Datenbits", + "serial_console_stop_bits": "Stoppbits", + "serial_console_parity": "Parität", + "serial_console_parity_even": "Gerade Parität", + "serial_console_parity_odd": "Ungerade Parität", + "serial_console_parity_none": "Keine Parität", + "serial_console_parity_mark": "Parität markieren", + "serial_console_parity_space": "Raumparität", + "serial_console_get_settings_error": "Die seriellen Konsoleneinstellungen konnten nicht abgerufen werden: {error}", + "serial_console_set_settings_error": "Die Einstellungen der seriellen Konsole konnten nicht auf {settings} festgelegt werden: {error}" +} \ No newline at end of file diff --git a/ui/localization/messages/en.json b/ui/localization/messages/en.json new file mode 100644 index 00000000..ed0c995e --- /dev/null +++ b/ui/localization/messages/en.json @@ -0,0 +1,64 @@ +{ + "$schema": "https://inlang.com/schema/inlang-message-format", + "jetkvm": "JetKVM", + "oh_no": "Oh no!", + "something_went_wrong": "Something went wrong. Please try again later or contact support", + "jetkvm_logo": "JetKVM Logo", + "load": "Load", + "unknown_error": "Unknown error", + "action_bar_virtual_media": "Virtual Media", + "action_bar_paste_text": "Paste text", + "action_bar_web_terminal": "Web Terminal", + "action_bar_wake_on_lan": "Wake on LAN", + "action_bar_virtual_keyboard": "Virtual Keyboard", + "action_bar_extension": "Extension", + "action_bar_connection_stats": "Connection Stats", + "action_bar_settings": "Settings", + "action_bar_fullscreen": "Fullscreen", + "action_bar_exit_fullscreen": "Exit Fullscreen", + "extensions_popover_extensions": "Extensions", + "extension_popover_set_error_notification": "Failed to set active extension: {error}", + "extension_popover_unload_extension": "Unload Extension", + "extension_popover_load_and_manage_extensions": "Load and manage your extensions", + "extensions_atx_power_control": "ATX Power Control", + "extensions_atx_power_control_description": "Control the power state of your machine via ATX power control.", + "extensions_dc_power_control": "DC Power Control", + "extensions_dc_power_control_description": "Control your DC Power extension", + "extension_serial_console": "Serial Console", + "extension_serial_console_description": "Access your serial console extension", + "atx_power_control_get_state_error": "Failed to get ATX power state: {error}", + "atx_power_control_send_action_error": "Failed to send ATX power action {action}: {error}", + "atx_power_control_power_button": "Power", + "atx_power_control_short_power_button": "Short Press", + "atx_power_control_long_power_button": "Long Press", + "atx_power_control_reset_button": "Reset", + "atx_power_control_power_led": "Power LED", + "atx_power_control_hdd_led": "HDD LED", + "dc_power_control_get_state_error": "Failed to get DC power state: {error}", + "dc_power_control_set_power_state_error": "Failed to send DC power state to {enabled}: {error}", + "dc_power_control_set_restore_state_error": "Failed to send DC power restore state to {state}: {error}", + "dc_power_control_power_on_button": "Power On", + "dc_power_control_power_off_button": "Power Off", + "dc_power_control_restore_power_state": "Restore Power Loss", + "dc_power_control_power_on_state": "Power ON", + "dc_power_control_power_off_state": "Power OFF", + "dc_power_control_voltage": "Voltage", + "dc_power_control_voltage_unit": "V", + "dc_power_control_current": "Current", + "dc_power_control_current_unit": "A", + "dc_power_control_power": "Power", + "dc_power_control_power_unit": "W", + "serial_console_get_settings_error": "Failed to get serial console settings: {error}", + "serial_console_set_settings_error": "Failed to set serial console settings to {settings}: {error}", + "serial_console_configure_description": "Configure your serial console settings", + "serial_console_open_console": "Open Console", + "serial_console_baud_rate": "Baud Rate", + "serial_console_data_bits": "Data Bits", + "serial_console_stop_bits": "Stop Bits", + "serial_console_parity": "Parity", + "serial_console_parity_even": "Even Parity", + "serial_console_parity_odd": "Odd Parity", + "serial_console_parity_none": "No Parity", + "serial_console_parity_mark": "Mark Parity", + "serial_console_parity_space": "Space Parity" +} \ No newline at end of file diff --git a/ui/localization/messages/es.json b/ui/localization/messages/es.json new file mode 100644 index 00000000..c182658a --- /dev/null +++ b/ui/localization/messages/es.json @@ -0,0 +1,66 @@ +{ + "$schema": "https://inlang.com/schema/inlang-message-format", + "jetkvm": "JetKVM", + "oh_no": "¡Oh, no!", + "something_went_wrong": "Algo salió mal. Inténtalo de nuevo más tarde o contacta con el servicio de asistencia.", + "jetkvm_logo": "Logotipo de JetKVM", + "load": "Carga", + "unknown_error": "Error desconocido", + "action_bar_virtual_media": "Medios virtuales", + "action_bar_paste_text": "Pegar texto", + "action_bar_web_terminal": "Terminal web", + "action_bar_wake_on_lan": "Activación en LAN", + "action_bar_virtual_keyboard": "Teclado virtual", + "action_bar_extension": "Extensión", + "action_bar_connection_stats": "Estadísticas de conexión", + "action_bar_settings": "Ajustes", + "action_bar_fullscreen": "Pantalla completa", + "action_bar_exit_fullscreen": "Salir de pantalla completa", + "extensions_popover_extensions": "Extensiones", + "extension_popover_set_error_notification": "No se pudo establecer la extensión activa: {error}", + "extension_popover_unload_extension": "Extensión de descarga", + "extension_popover_load_and_manage_extensions": "Cargar y administrar sus extensiones", + "extensions_atx_power_control": "Control de alimentación ATX", + "extensions_atx_power_control_description": "Controle el estado de energía de su máquina a través del control de energía ATX.", + "extensions_dc_power_control": "Control de potencia de CC", + "extensions_dc_power_control_description": "Controle su extensión de alimentación de CC", + "extension_serial_console": "Consola serial", + "extension_serial_console_description": "Acceda a la extensión de su consola serie", + "atx_power_control_get_state_error": "No se pudo obtener el estado de energía ATX: {error}", + "atx_power_control_send_action_error": "No se pudo enviar la acción de alimentación ATX {action} : {error}", + "atx_power_control_power_button": "Fuerza", + "atx_power_control_short_power_button": "Prensa corta", + "atx_power_control_long_power_button": "Pulsación larga", + "atx_power_control_reset_button": "Reiniciar", + "atx_power_control_power_led": "LED de encendido", + "atx_power_control_hdd_led": "LED del disco duro", + "dc_power_control_get_state_error": "No se pudo obtener el estado de la alimentación de CC: {error}", + "dc_power_control_set_power_state_error": "No se pudo enviar el estado de alimentación de CC a {enabled} : {error}", + "dc_power_control_set_restore_state_error": "No se pudo enviar el estado de restauración de energía de CC a {state} : {error}", + "dc_power_control_power_on_button": "Encendido", + "dc_power_control_power_off_button": "Apagado", + "dc_power_control_restore_power_state": "Restaurar pérdida de energía", + "dc_power_control_power_on_state": "Encendido", + "dc_power_control_power_off_state": "Apagado", + "dc_power_control_voltage": "Voltaje", + "dc_power_control_voltage_unit": "V", + "dc_power_control_current": "Amperio", + "dc_power_control_current_unit": "A", + "dc_power_control_power": "Vatio", + "dc_power_control_power_unit": "O", + "serial_console_get_state_error": "No se pudo obtener la configuración de la consola serial: {error}", + "serial_console_set_power_state_error": "No se pudieron establecer los ajustes de la consola serial en {settings} : {error}", + "serial_console_configure_description": "Configure los ajustes de su consola serie", + "serial_console_open_console": "Consola abierta", + "serial_console_baud_rate": "Tasa de Baud", + "serial_console_data_bits": "Bits de datos", + "serial_console_stop_bits": "Bits de parada", + "serial_console_parity": "Paridad", + "serial_console_parity_even": "Paridad uniforme", + "serial_console_parity_odd": "Paridad impar", + "serial_console_parity_none": "Sin paridad", + "serial_console_parity_mark": "Paridad de marca", + "serial_console_parity_space": "Paridad espacial", + "serial_console_get_settings_error": "No se pudo obtener la configuración de la consola serial: {error}", + "serial_console_set_settings_error": "No se pudieron establecer los ajustes de la consola serial en {settings} : {error}" +} \ No newline at end of file diff --git a/ui/localization/messages/fr.json b/ui/localization/messages/fr.json new file mode 100644 index 00000000..400d9bb0 --- /dev/null +++ b/ui/localization/messages/fr.json @@ -0,0 +1,66 @@ +{ + "$schema": "https://inlang.com/schema/inlang-message-format", + "jetkvm": "JetKVM", + "oh_no": "Oh non!", + "something_went_wrong": "Une erreur s'est produite. Veuillez réessayer ultérieurement ou contacter le support.", + "jetkvm_logo": "Logo JetKVM", + "load": "Charger", + "unknown_error": "Erreur inconnue", + "action_bar_virtual_media": "Médias virtuels", + "action_bar_paste_text": "Coller du texte", + "action_bar_web_terminal": "Terminal Web", + "action_bar_wake_on_lan": "Réveil sur LAN", + "action_bar_virtual_keyboard": "Clavier virtuel", + "action_bar_extension": "Extension", + "action_bar_connection_stats": "Statistiques de connexion", + "action_bar_settings": "Paramètres", + "action_bar_fullscreen": "Plein écran", + "action_bar_exit_fullscreen": "Quitter le plein écran", + "extensions_popover_extensions": "Extensions", + "extension_popover_set_error_notification": "Échec de la définition de l'extension active: {error}", + "extension_popover_unload_extension": "Extension de déchargement", + "extension_popover_load_and_manage_extensions": "Chargez et gérez vos extensions", + "extensions_atx_power_control": "Contrôle d'alimentation ATX", + "extensions_atx_power_control_description": "Contrôlez l'état d'alimentation de votre machine via le contrôle d'alimentation ATX.", + "extensions_dc_power_control": "Contrôle de l'alimentation CC", + "extensions_dc_power_control_description": "Contrôlez votre extension d'alimentation CC", + "extension_serial_console": "Console série", + "extension_serial_console_description": "Accédez à votre extension de console série", + "atx_power_control_get_state_error": "Échec de l'obtention de l'état d'alimentation ATX : {error}", + "atx_power_control_send_action_error": "Échec de l'envoi de l'action d'alimentation ATX {action} : {error}", + "atx_power_control_power_button": "Pouvoir", + "atx_power_control_short_power_button": "Appui court", + "atx_power_control_long_power_button": "Appui long", + "atx_power_control_reset_button": "Réinitialiser", + "atx_power_control_power_led": "LED d'alimentation", + "atx_power_control_hdd_led": "Voyant du disque dur", + "dc_power_control_get_state_error": "Échec de l'obtention de l'état d'alimentation CC : {error}", + "dc_power_control_set_power_state_error": "Échec de l'envoi de l'état d'alimentation CC à {enabled} : {error}", + "dc_power_control_set_restore_state_error": "Échec de l'envoi de l'état de restauration de l'alimentation CC à {state} : {error}", + "dc_power_control_power_on_button": "Mise sous tension", + "dc_power_control_power_off_button": "Éteindre", + "dc_power_control_restore_power_state": "Restaurer la perte de puissance", + "dc_power_control_power_on_state": "Mise sous tension", + "dc_power_control_power_off_state": "Éteindre", + "dc_power_control_voltage": "Tension", + "dc_power_control_voltage_unit": "V", + "dc_power_control_current": "Ampère", + "dc_power_control_current_unit": "UN", + "dc_power_control_power": "Pouvoir", + "dc_power_control_power_unit": "W", + "serial_console_get_state_error": "Échec de l'obtention des paramètres de la console série : {error}", + "serial_console_set_power_state_error": "Échec de la définition des paramètres de la console série sur {settings} : {error}", + "serial_console_configure_description": "Configurez les paramètres de votre console série", + "serial_console_open_console": "Ouvrir la console", + "serial_console_baud_rate": "Débit en bauds", + "serial_console_data_bits": "Bits de données", + "serial_console_stop_bits": "Bits d'arrêt", + "serial_console_parity": "Parité", + "serial_console_parity_even": "Parité égale", + "serial_console_parity_odd": "Parité impaire", + "serial_console_parity_none": "Pas de parité", + "serial_console_parity_mark": "Marquer la parité", + "serial_console_parity_space": "Parité spatiale", + "serial_console_get_settings_error": "Échec de l'obtention des paramètres de la console série : {error}", + "serial_console_set_settings_error": "Échec de la définition des paramètres de la console série sur {settings} : {error}" +} \ No newline at end of file diff --git a/ui/localization/messages/it.json b/ui/localization/messages/it.json new file mode 100644 index 00000000..12a0b1e3 --- /dev/null +++ b/ui/localization/messages/it.json @@ -0,0 +1,66 @@ +{ + "$schema": "https://inlang.com/schema/inlang-message-format", + "jetkvm": "JetKVM", + "oh_no": "Oh no!", + "something_went_wrong": "Qualcosa è andato storto. Riprova più tardi o contatta l'assistenza.", + "jetkvm_logo": "Logo JetKVM", + "load": "Carico", + "unknown_error": "Errore sconosciuto", + "action_bar_virtual_media": "Media virtuali", + "action_bar_paste_text": "Incolla il testo", + "action_bar_web_terminal": "Terminale Web", + "action_bar_wake_on_lan": "Wake on LAN", + "action_bar_virtual_keyboard": "Tastiera virtuale", + "action_bar_extension": "Estensione", + "action_bar_connection_stats": "Statistiche di connessione", + "action_bar_settings": "Impostazioni", + "action_bar_fullscreen": "A schermo intero", + "action_bar_exit_fullscreen": "Esci dalla modalità a schermo intero", + "extensions_popover_extensions": "Estensioni", + "extension_popover_set_error_notification": "Impossibile impostare l'estensione attiva: {error}", + "extension_popover_unload_extension": "Estensione di scaricamento", + "extension_popover_load_and_manage_extensions": "Carica e gestisci le tue estensioni", + "extensions_atx_power_control": "Controllo di potenza ATX", + "extensions_atx_power_control_description": "Controlla lo stato di alimentazione del tuo computer tramite il controllo di alimentazione ATX.", + "extensions_dc_power_control": "Controllo di potenza CC", + "extensions_dc_power_control_description": "Controlla la tua estensione di alimentazione CC", + "extension_serial_console": "Console seriale", + "extension_serial_console_description": "Accedi all'estensione della tua console seriale", + "atx_power_control_get_state_error": "Impossibile ottenere lo stato di alimentazione ATX: {error}", + "atx_power_control_send_action_error": "Impossibile inviare l'azione di alimentazione ATX {action} : {error}", + "atx_power_control_power_button": "Energia", + "atx_power_control_short_power_button": "Pressione breve", + "atx_power_control_long_power_button": "Pressione lunga", + "atx_power_control_reset_button": "Reset", + "atx_power_control_power_led": "LED di potenza", + "atx_power_control_hdd_led": "LED dell'HDD", + "dc_power_control_get_state_error": "Impossibile ottenere lo stato di alimentazione CC: {error}", + "dc_power_control_set_power_state_error": "Impossibile inviare lo stato di alimentazione CC a {enabled} : {error}", + "dc_power_control_set_restore_state_error": "Impossibile inviare lo stato di ripristino dell'alimentazione CC a {state} : {error}", + "dc_power_control_power_on_button": "Accensione", + "dc_power_control_power_off_button": "Spegnimento", + "dc_power_control_restore_power_state": "Ripristinare la perdita di potenza", + "dc_power_control_power_on_state": "Accensione", + "dc_power_control_power_off_state": "Spegnimento", + "dc_power_control_voltage": "Voltaggio", + "dc_power_control_voltage_unit": "V", + "dc_power_control_current": "Attuale", + "dc_power_control_current_unit": "UN", + "dc_power_control_power": "Energia", + "dc_power_control_power_unit": "O", + "serial_console_get_state_error": "Impossibile ottenere le impostazioni della console seriale: {error}", + "serial_console_set_power_state_error": "Impossibile impostare le impostazioni della console seriale su {settings} : {error}", + "serial_console_configure_description": "Configura le impostazioni della tua console seriale", + "serial_console_open_console": "Apri console", + "serial_console_baud_rate": "Velocità in baud", + "serial_console_data_bits": "Bit di dati", + "serial_console_stop_bits": "Bit di stop", + "serial_console_parity": "Parità", + "serial_console_parity_even": "Parità pari", + "serial_console_parity_odd": "Parità dispari", + "serial_console_parity_none": "Nessuna parità", + "serial_console_parity_mark": "Segna la parità", + "serial_console_parity_space": "Parità spaziale", + "serial_console_get_settings_error": "Impossibile ottenere le impostazioni della console seriale: {error}", + "serial_console_set_settings_error": "Impossibile impostare le impostazioni della console seriale su {settings} : {error}" +} \ No newline at end of file diff --git a/ui/localization/messages/nb.json b/ui/localization/messages/nb.json new file mode 100644 index 00000000..bf68e4f9 --- /dev/null +++ b/ui/localization/messages/nb.json @@ -0,0 +1,66 @@ +{ + "$schema": "https://inlang.com/schema/inlang-message-format", + "jetkvm": "JetKVM", + "oh_no": "Å nei!", + "something_went_wrong": "Noe gikk galt. Prøv igjen senere, eller kontakt kundestøtte.", + "jetkvm_logo": "JetKVM-logo", + "load": "Laste", + "unknown_error": "Ukjent feil", + "action_bar_virtual_media": "Virtuelle medier", + "action_bar_paste_text": "Lim inn tekst", + "action_bar_web_terminal": "Nettterminal", + "action_bar_wake_on_lan": "Vekk på LAN", + "action_bar_virtual_keyboard": "Virtuelt tastatur", + "action_bar_extension": "Forlengelse", + "action_bar_connection_stats": "Tilkoblingsstatistikk", + "action_bar_settings": "Innstillinger", + "action_bar_fullscreen": "Fullskjerm", + "action_bar_exit_fullscreen": "Avslutt fullskjerm", + "extensions_popover_extensions": "Utvidelser", + "extension_popover_set_error_notification": "Klarte ikke å angi aktiv utvidelse: {error}", + "extension_popover_unload_extension": "Fjern utvidelse", + "extension_popover_load_and_manage_extensions": "Last inn og administrer utvidelsene dine", + "extensions_atx_power_control": "ATX-strømstyring", + "extensions_atx_power_control_description": "Kontroller maskinens strømstatus via ATX-strømkontroll.", + "extensions_dc_power_control": "DC-strømkontroll", + "extensions_dc_power_control_description": "Kontroller DC-strømutvidelsen din", + "extension_serial_console": "Seriell konsoll", + "extension_serial_console_description": "Få tilgang til seriekonsollutvidelsen din", + "atx_power_control_get_state_error": "Klarte ikke å hente ATX-strømstatus: {error}", + "atx_power_control_send_action_error": "Kunne ikke sende ATX-strømhandling {action} : {error}", + "atx_power_control_power_button": "Makt", + "atx_power_control_short_power_button": "Kort trykk", + "atx_power_control_long_power_button": "Langt trykk", + "atx_power_control_reset_button": "Tilbakestill", + "atx_power_control_power_led": "Strøm-LED", + "atx_power_control_hdd_led": "HDD-LED", + "dc_power_control_get_state_error": "Klarte ikke å hente likestrømsstatus: {error}", + "dc_power_control_set_power_state_error": "Kunne ikke sende likestrømsstatus til {enabled} : {error}", + "dc_power_control_set_restore_state_error": "Kunne ikke sende gjenopprettingsstatus for likestrøm til {state} : {error}", + "dc_power_control_power_on_button": "Slå på", + "dc_power_control_power_off_button": "Slå av", + "dc_power_control_restore_power_state": "Gjenopprett strømtap", + "dc_power_control_power_on_state": "Slå PÅ", + "dc_power_control_power_off_state": "Slå av", + "dc_power_control_voltage": "Spenning", + "dc_power_control_voltage_unit": "V", + "dc_power_control_current": "Nåværende", + "dc_power_control_current_unit": "EN", + "dc_power_control_power": "Makt", + "dc_power_control_power_unit": "V", + "serial_console_get_state_error": "Klarte ikke å hente innstillinger for seriell konsoll: {error}", + "serial_console_set_power_state_error": "Klarte ikke å sette innstillingene for seriell konsoll til {settings} : {error}", + "serial_console_configure_description": "Konfigurer innstillingene for seriekonsollen", + "serial_console_open_console": "Åpne konsollen", + "serial_console_baud_rate": "Baudhastighet", + "serial_console_data_bits": "Databiter", + "serial_console_stop_bits": "Stoppbiter", + "serial_console_parity": "Paritet", + "serial_console_parity_even": "Paritet", + "serial_console_parity_odd": "Oddeparitet", + "serial_console_parity_none": "Ingen paritet", + "serial_console_parity_mark": "Mark Paritet", + "serial_console_parity_space": "Romparitet", + "serial_console_get_settings_error": "Klarte ikke å hente innstillinger for seriell konsoll: {error}", + "serial_console_set_settings_error": "Klarte ikke å sette innstillingene for seriell konsoll til {settings} : {error}" +} \ No newline at end of file diff --git a/ui/localization/messages/sv.json b/ui/localization/messages/sv.json new file mode 100644 index 00000000..2ecace3f --- /dev/null +++ b/ui/localization/messages/sv.json @@ -0,0 +1,66 @@ +{ + "$schema": "https://inlang.com/schema/inlang-message-format", + "jetkvm": "JetKVM", + "oh_no": "nej då!", + "something_went_wrong": "Något gick fel. Försök igen senare eller kontakta supporten.", + "jetkvm_logo": "JetKVM-logotyp", + "load": "Ladda", + "unknown_error": "Okänt fel", + "action_bar_virtual_media": "Virtuella medier", + "action_bar_paste_text": "Klistra in text", + "action_bar_web_terminal": "Webbterminal", + "action_bar_wake_on_lan": "Vakna på LAN", + "action_bar_virtual_keyboard": "Virtuellt tangentbord", + "action_bar_extension": "Förlängning", + "action_bar_connection_stats": "Anslutningsstatistik", + "action_bar_settings": "Inställningar", + "action_bar_fullscreen": "Helskärm", + "action_bar_exit_fullscreen": "Avsluta helskärm", + "extensions_popover_extensions": "Tillägg", + "extension_popover_set_error_notification": "Misslyckades med att ange aktivt tillägg: {error}", + "extension_popover_unload_extension": "Avlasta tillägg", + "extension_popover_load_and_manage_extensions": "Ladda och hantera dina tillägg", + "extensions_atx_power_control": "ATX-strömkontroll", + "extensions_atx_power_control_description": "Styr din maskins strömförsörjning via ATX-strömkontroll.", + "extensions_dc_power_control": "DC-strömstyrning", + "extensions_dc_power_control_description": "Styr din DC-strömförlängning", + "extension_serial_console": "Seriell konsol", + "extension_serial_console_description": "Åtkomst till din seriella konsoltillägg", + "atx_power_control_get_state_error": "Misslyckades med att hämta ATX-strömstatus: {error}", + "atx_power_control_send_action_error": "Misslyckades med att skicka ATX-strömåtgärd {action} : {error}", + "atx_power_control_power_button": "Driva", + "atx_power_control_short_power_button": "Kort tryck", + "atx_power_control_long_power_button": "Långt tryck", + "atx_power_control_reset_button": "Återställa", + "atx_power_control_power_led": "Ström-LED", + "atx_power_control_hdd_led": "Hårddisk-LED", + "dc_power_control_get_state_error": "Misslyckades med att hämta likströmsstatus: {error}", + "dc_power_control_set_power_state_error": "Misslyckades med att skicka likströmsstatus till {enabled} : {error}", + "dc_power_control_set_restore_state_error": "Misslyckades med att skicka återställningsstatus för likström till {state} : {error}", + "dc_power_control_power_on_button": "Slå på", + "dc_power_control_power_off_button": "Stäng av", + "dc_power_control_restore_power_state": "Återställ strömförlust", + "dc_power_control_power_on_state": "Slå på", + "dc_power_control_power_off_state": "Stäng av", + "dc_power_control_voltage": "Spänning", + "dc_power_control_voltage_unit": "V", + "dc_power_control_current": "Nuvarande", + "dc_power_control_current_unit": "En", + "dc_power_control_power": "Driva", + "dc_power_control_power_unit": "V", + "serial_console_get_state_error": "Misslyckades med att hämta inställningar för seriekonsolen: {error}", + "serial_console_set_power_state_error": "Misslyckades med att ställa in seriekonsolinställningarna till {settings} : {error}", + "serial_console_configure_description": "Konfigurera dina seriella konsolinställningar", + "serial_console_open_console": "Öppna konsolen", + "serial_console_baud_rate": "Baudhastighet", + "serial_console_data_bits": "Databitar", + "serial_console_stop_bits": "Stoppbitar", + "serial_console_parity": "Paritet", + "serial_console_parity_even": "Jämn paritet", + "serial_console_parity_odd": "Udda paritet", + "serial_console_parity_none": "Ingen paritet", + "serial_console_parity_mark": "Markera paritet", + "serial_console_parity_space": "Rymdparitet", + "serial_console_get_settings_error": "Misslyckades med att hämta inställningar för seriekonsolen: {error}", + "serial_console_set_settings_error": "Misslyckades med att ställa in seriekonsolinställningarna till {settings} : {error}" +} \ No newline at end of file diff --git a/ui/localization/messages/zh.json b/ui/localization/messages/zh.json new file mode 100644 index 00000000..7b0298f2 --- /dev/null +++ b/ui/localization/messages/zh.json @@ -0,0 +1,66 @@ +{ + "$schema": "https://inlang.com/schema/inlang-message-format", + "jetkvm": "JetKVM", + "oh_no": "噢不!", + "something_went_wrong": "出了点问题。请稍后重试或联系客服", + "jetkvm_logo": "JetKVM 徽标", + "load": "加载", + "unknown_error": "未知错误", + "action_bar_virtual_media": "虚拟媒体", + "action_bar_paste_text": "粘贴文本", + "action_bar_web_terminal": "网页终端", + "action_bar_wake_on_lan": "局域网唤醒", + "action_bar_virtual_keyboard": "虚拟键盘", + "action_bar_extension": "扩大", + "action_bar_connection_stats": "连接统计", + "action_bar_settings": "设置", + "action_bar_fullscreen": "全屏", + "action_bar_exit_fullscreen": "退出全屏", + "extensions_popover_extensions": "扩展", + "extension_popover_set_error_notification": "无法设置活动扩展:{error}", + "extension_popover_unload_extension": "卸载扩展", + "extension_popover_load_and_manage_extensions": "加载和管理您的扩展", + "extensions_atx_power_control": "ATX电源控制", + "extensions_atx_power_control_description": "通过 ATX 电源控制来控制机器的电源状态。", + "extensions_dc_power_control": "直流电源控制", + "extensions_dc_power_control_description": "控制您的直流电源扩展", + "extension_serial_console": "串行控制台", + "extension_serial_console_description": "访问串行控制台扩展", + "atx_power_control_get_state_error": "无法获取 ATX 电源状态:{error}", + "atx_power_control_send_action_error": "无法发送 ATX 电源操作{action} : {error}", + "atx_power_control_power_button": "力量", + "atx_power_control_short_power_button": "短按", + "atx_power_control_long_power_button": "长按", + "atx_power_control_reset_button": "重置", + "atx_power_control_power_led": "电源 LED", + "atx_power_control_hdd_led": "硬盘指示灯", + "dc_power_control_get_state_error": "无法获取直流电源状态:{error}", + "dc_power_control_set_power_state_error": "无法将直流电源状态发送到 {enabled} : {error}", + "dc_power_control_set_restore_state_error": "无法将直流电源恢复状态发送到 {state} : {error}", + "dc_power_control_power_on_button": "开机", + "dc_power_control_power_off_button": "关闭电源", + "dc_power_control_restore_power_state": "恢复断电", + "dc_power_control_power_on_state": "开启电源", + "dc_power_control_power_off_state": "关闭电源", + "dc_power_control_voltage": "电压", + "dc_power_control_voltage_unit": "五", + "dc_power_control_current": "安培", + "dc_power_control_current_unit": "一个", + "dc_power_control_power": "瓦特", + "dc_power_control_power_unit": "西", + "serial_console_get_state_error": "无法获取串行控制台设置: {error}", + "serial_console_set_power_state_error": "无法将串行控制台设置设置为{settings} : {error}", + "serial_console_configure_description": "配置串行控制台设置", + "serial_console_open_console": "打开控制台", + "serial_console_baud_rate": "波特率", + "serial_console_data_bits": "数据位", + "serial_console_stop_bits": "停止位", + "serial_console_parity": "平价", + "serial_console_parity_even": "偶校验", + "serial_console_parity_odd": "奇校验", + "serial_console_parity_none": "无奇偶校验", + "serial_console_parity_mark": "马克·帕里蒂", + "serial_console_parity_space": "空间平价", + "serial_console_get_settings_error": "无法获取串行控制台设置: {error}", + "serial_console_set_settings_error": "无法将串行控制台设置设置为{settings} : {error}" +} \ No newline at end of file diff --git a/ui/package-lock.json b/ui/package-lock.json index 7d4faf36..b900f0a8 100644 --- a/ui/package-lock.json +++ b/ui/package-lock.json @@ -1,12 +1,12 @@ { "name": "kvm-ui", - "version": "2025.10.01.1900", + "version": "2025.10.07.1700", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "kvm-ui", - "version": "2025.10.01.1900", + "version": "2025.10.07.1700", "dependencies": { "@headlessui/react": "^2.2.9", "@headlessui/tailwindcss": "^0.2.2", @@ -25,13 +25,13 @@ "framer-motion": "^12.23.22", "lodash.throttle": "^4.1.1", "mini-svg-data-uri": "^1.4.4", - "react": "^19.1.1", + "react": "^19.2.0", "react-animate-height": "^3.2.3", - "react-dom": "^19.1.1", + "react-dom": "^19.2.0", "react-hot-toast": "^2.6.0", "react-icons": "^5.5.0", "react-router": "^7.9.3", - "react-simple-keyboard": "^3.8.125", + "react-simple-keyboard": "^3.8.126", "react-use-websocket": "^4.13.0", "react-xtermjs": "^1.0.10", "recharts": "^3.2.1", @@ -43,32 +43,37 @@ "devDependencies": { "@eslint/compat": "^1.4.0", "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "^9.36.0", + "@eslint/js": "^9.37.0", + "@inlang/cli": "^3.0.12", + "@inlang/paraglide-js": "^2.4.0", + "@inlang/plugin-m-function-matcher": "^2.1.0", + "@inlang/plugin-message-format": "^4.0.0", + "@inlang/sdk": "^2.4.9", "@tailwindcss/forms": "^0.5.10", "@tailwindcss/postcss": "^4.1.14", "@tailwindcss/typography": "^0.5.19", "@tailwindcss/vite": "^4.1.14", - "@types/react": "^19.1.17", - "@types/react-dom": "^19.1.10", + "@types/react": "^19.2.2", + "@types/react-dom": "^19.2.1", "@types/semver": "^7.7.1", "@types/validator": "^13.15.3", - "@typescript-eslint/eslint-plugin": "^8.45.0", - "@typescript-eslint/parser": "^8.45.0", + "@typescript-eslint/eslint-plugin": "^8.46.0", + "@typescript-eslint/parser": "^8.46.0", "@vitejs/plugin-react-swc": "^4.1.0", "autoprefixer": "^10.4.21", - "eslint": "^9.36.0", + "eslint": "^9.37.0", "eslint-config-prettier": "^10.1.8", "eslint-plugin-import": "^2.32.0", "eslint-plugin-react": "^7.37.5", - "eslint-plugin-react-hooks": "^5.2.0", - "eslint-plugin-react-refresh": "^0.4.22", + "eslint-plugin-react-hooks": "^6.1.1", + "eslint-plugin-react-refresh": "^0.4.23", "globals": "^16.4.0", "postcss": "^8.5.6", "prettier": "^3.6.2", "prettier-plugin-tailwindcss": "^0.6.14", "tailwindcss": "^4.1.14", "typescript": "^5.9.3", - "vite": "^7.1.7", + "vite": "^7.1.9", "vite-tsconfig-paths": "^5.1.4" }, "engines": { @@ -88,6 +93,266 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@babel/code-frame": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.4.tgz", + "integrity": "sha512-YsmSKC29MJwf0gF8Rjjrg5LQCmyh+j/nD8/eP7f+BeoQTKYqs9RoWbjGOdy0+1Ekr68RJZMUOPVQaQisnIo4Rw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.4.tgz", + "integrity": "sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.3", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-module-transforms": "^7.28.3", + "@babel/helpers": "^7.28.4", + "@babel/parser": "^7.28.4", + "@babel/template": "^7.27.2", + "@babel/traverse": "^7.28.4", + "@babel/types": "^7.28.4", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/generator": { + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.3.tgz", + "integrity": "sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.28.3", + "@babel/types": "^7.28.2", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", + "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.27.2", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", + "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz", + "integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1", + "@babel/traverse": "^7.28.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", + "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.4.tgz", + "integrity": "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.4" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.4.tgz", + "integrity": "sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.4" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/template": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", + "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/parser": "^7.27.2", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.4.tgz", + "integrity": "sha512-YEzuboP2qvQavAcjgQNVgsvHIDv6ZpwXvcvjmyySP2DIMuByS/6ioU5G9pYrWHM6T2YDfc7xga9iNzYOs12CFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.3", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.28.4", + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.4", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.4.tgz", + "integrity": "sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@esbuild/aix-ppc64": { "version": "0.25.10", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.10.tgz", @@ -567,10 +832,13 @@ } }, "node_modules/@eslint/config-helpers": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.1.tgz", - "integrity": "sha512-xR93k9WhrDYpXHORXpxVL5oHj3Era7wo6k/Wd8/IsQNnZUTzkGS29lyn3nAT05v6ltUuTFVCCYDEGfy2Or/sPA==", + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.0.tgz", + "integrity": "sha512-WUFvV4WoIwW8Bv0KeKCIIEgdSiFOsulyN0xrMu+7z43q/hkOLXjvb5u7UC9jDxvRzcrbEmuZBX5yJZz1741jog==", "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.16.0" + }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } @@ -579,7 +847,6 @@ "version": "0.16.0", "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.16.0.tgz", "integrity": "sha512-nmC8/totwobIiFcGkDza3GIKfAw1+hLiYVrh3I1nIomQ8PEr5cxg34jnkmGawul/ep52wGRAcyeDCNtWKSOj4Q==", - "dev": true, "license": "Apache-2.0", "dependencies": { "@types/json-schema": "^7.0.15" @@ -624,9 +891,9 @@ } }, "node_modules/@eslint/js": { - "version": "9.36.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.36.0.tgz", - "integrity": "sha512-uhCbYtYynH30iZErszX78U+nR3pJU3RHGQ57NXy5QupD4SBVwDeU8TNBy+MjMngc1UyIW9noKqsRqfjQTBU2dw==", + "version": "9.37.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.37.0.tgz", + "integrity": "sha512-jaS+NJ+hximswBG6pjNX0uEJZkrT0zwpVi3BA3vX22aFGjJjmgSTSmPpZCRKmoBL5VY/M6p0xsSJx7rk7sy5gg==", "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -645,30 +912,18 @@ } }, "node_modules/@eslint/plugin-kit": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.5.tgz", - "integrity": "sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w==", + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.0.tgz", + "integrity": "sha512-sB5uyeq+dwCWyPi31B2gQlVlo+j5brPlWx4yZBrEaRo/nhdDE8Xke1gsGgtiBdaBTxuTkceLVuVt/pclrasb0A==", "license": "Apache-2.0", "dependencies": { - "@eslint/core": "^0.15.2", + "@eslint/core": "^0.16.0", "levn": "^0.4.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@eslint/plugin-kit/node_modules/@eslint/core": { - "version": "0.15.2", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.2.tgz", - "integrity": "sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg==", - "license": "Apache-2.0", - "dependencies": { - "@types/json-schema": "^7.0.15" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, "node_modules/@floating-ui/core": { "version": "1.7.3", "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.3.tgz", @@ -811,6 +1066,87 @@ "url": "https://github.com/sponsors/nzakas" } }, + "node_modules/@inlang/cli": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/@inlang/cli/-/cli-3.0.12.tgz", + "integrity": "sha512-0FZJtgrt1Ol4iwKtA0VICrsHcA3stWTSP2jq8mpTgjTlFU63gr5JcyFljUT8Dp5nDIJYmdh3kJ0a8PhW0X8clQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@inlang/sdk": "2.4.9", + "esbuild-wasm": "^0.19.2" + }, + "bin": { + "inlang": "bin/run.js" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@inlang/paraglide-js": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@inlang/paraglide-js/-/paraglide-js-2.4.0.tgz", + "integrity": "sha512-T/m9uoev574/1JrhCnPcgK1xnAwkVMgaDev4LFthnmID8ubX2xjboSGO3IztwXWwO0aJoT1UJr89JCwjbwgnJQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inlang/recommend-sherlock": "0.2.1", + "@inlang/sdk": "2.4.9", + "commander": "11.1.0", + "consola": "3.4.0", + "json5": "2.2.3", + "unplugin": "^2.1.2", + "urlpattern-polyfill": "^10.0.0" + }, + "bin": { + "paraglide-js": "bin/run.js" + } + }, + "node_modules/@inlang/plugin-m-function-matcher": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@inlang/plugin-m-function-matcher/-/plugin-m-function-matcher-2.1.0.tgz", + "integrity": "sha512-IAbG7rOl+rlTiZY7qj92we6lmII693lVthPtY9bFDkZ/Ig7FPSpae/TfLzqjf2KcR1nDdx1zRpSo6roDPeM85g==", + "dev": true, + "dependencies": { + "@inlang/sdk": "2.4.9" + } + }, + "node_modules/@inlang/plugin-message-format": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@inlang/plugin-message-format/-/plugin-message-format-4.0.0.tgz", + "integrity": "sha512-zNpLxLTt+bDd3JLXj1ONzo+Q6AOzz2MfcgGo8XB6/bweGhFIndK3GU/q0iU4o7VI4KS1+OHNLpKwFcrAifwERQ==", + "dev": true, + "dependencies": { + "flat": "^6.0.1" + } + }, + "node_modules/@inlang/recommend-sherlock": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@inlang/recommend-sherlock/-/recommend-sherlock-0.2.1.tgz", + "integrity": "sha512-ckv8HvHy/iTqaVAEKrr+gnl+p3XFNwe5D2+6w6wJk2ORV2XkcRkKOJ/XsTUJbPSiyi4PI+p+T3bqbmNx/rDUlg==", + "dev": true, + "license": "MIT", + "dependencies": { + "comment-json": "^4.2.3" + } + }, + "node_modules/@inlang/sdk": { + "version": "2.4.9", + "resolved": "https://registry.npmjs.org/@inlang/sdk/-/sdk-2.4.9.tgz", + "integrity": "sha512-cvz/C1rF5WBxzHbEoiBoI6Sz6q6M+TdxfWkEGBYTD77opY8i8WN01prUWXEM87GPF4SZcyIySez9U0Ccm12oFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@lix-js/sdk": "0.4.7", + "@sinclair/typebox": "^0.31.17", + "kysely": "^0.27.4", + "sqlite-wasm-kysely": "0.3.0", + "uuid": "^10.0.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/@isaacs/fs-minipass": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", @@ -874,6 +1210,32 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@lix-js/sdk": { + "version": "0.4.7", + "resolved": "https://registry.npmjs.org/@lix-js/sdk/-/sdk-0.4.7.tgz", + "integrity": "sha512-pRbW+joG12L0ULfMiWYosIW0plmW4AsUdiPCp+Z8rAsElJ+wJ6in58zhD3UwUcd4BNcpldEGjg6PdA7e0RgsDQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@lix-js/server-protocol-schema": "0.1.1", + "dedent": "1.5.1", + "human-id": "^4.1.1", + "js-sha256": "^0.11.0", + "kysely": "^0.27.4", + "sqlite-wasm-kysely": "0.3.0", + "uuid": "^10.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@lix-js/server-protocol-schema": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@lix-js/server-protocol-schema/-/server-protocol-schema-0.1.1.tgz", + "integrity": "sha512-jBeALB6prAbtr5q4vTuxnRZZv1M2rKe8iNqRQhFJ4Tv7150unEa0vKyz0hs8Gl3fUGsWaNJBh3J8++fpbrpRBQ==", + "dev": true, + "license": "Apache-2.0" + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -913,14 +1275,14 @@ } }, "node_modules/@react-aria/focus": { - "version": "3.21.1", - "resolved": "https://registry.npmjs.org/@react-aria/focus/-/focus-3.21.1.tgz", - "integrity": "sha512-hmH1IhHlcQ2lSIxmki1biWzMbGgnhdxJUM0MFfzc71Rv6YAzhlx4kX3GYn4VNcjCeb6cdPv4RZ5vunV4kgMZYQ==", + "version": "3.21.2", + "resolved": "https://registry.npmjs.org/@react-aria/focus/-/focus-3.21.2.tgz", + "integrity": "sha512-JWaCR7wJVggj+ldmM/cb/DXFg47CXR55lznJhZBh4XVqJjMKwaOOqpT5vNN7kpC1wUpXicGNuDnJDN1S/+6dhQ==", "license": "Apache-2.0", "dependencies": { - "@react-aria/interactions": "^3.25.5", - "@react-aria/utils": "^3.30.1", - "@react-types/shared": "^3.32.0", + "@react-aria/interactions": "^3.25.6", + "@react-aria/utils": "^3.31.0", + "@react-types/shared": "^3.32.1", "@swc/helpers": "^0.5.0", "clsx": "^2.0.0" }, @@ -930,15 +1292,15 @@ } }, "node_modules/@react-aria/interactions": { - "version": "3.25.5", - "resolved": "https://registry.npmjs.org/@react-aria/interactions/-/interactions-3.25.5.tgz", - "integrity": "sha512-EweYHOEvMwef/wsiEqV73KurX/OqnmbzKQa2fLxdULbec5+yDj6wVGaRHIzM4NiijIDe+bldEl5DG05CAKOAHA==", + "version": "3.25.6", + "resolved": "https://registry.npmjs.org/@react-aria/interactions/-/interactions-3.25.6.tgz", + "integrity": "sha512-5UgwZmohpixwNMVkMvn9K1ceJe6TzlRlAfuYoQDUuOkk62/JVJNDLAPKIf5YMRc7d2B0rmfgaZLMtbREb0Zvkw==", "license": "Apache-2.0", "dependencies": { "@react-aria/ssr": "^3.9.10", - "@react-aria/utils": "^3.30.1", + "@react-aria/utils": "^3.31.0", "@react-stately/flags": "^3.1.2", - "@react-types/shared": "^3.32.0", + "@react-types/shared": "^3.32.1", "@swc/helpers": "^0.5.0" }, "peerDependencies": { @@ -962,15 +1324,15 @@ } }, "node_modules/@react-aria/utils": { - "version": "3.30.1", - "resolved": "https://registry.npmjs.org/@react-aria/utils/-/utils-3.30.1.tgz", - "integrity": "sha512-zETcbDd6Vf9GbLndO6RiWJadIZsBU2MMm23rBACXLmpRztkrIqPEb2RVdlLaq1+GklDx0Ii6PfveVjx+8S5U6A==", + "version": "3.31.0", + "resolved": "https://registry.npmjs.org/@react-aria/utils/-/utils-3.31.0.tgz", + "integrity": "sha512-ABOzCsZrWzf78ysswmguJbx3McQUja7yeGj6/vZo4JVsZNlxAN+E9rs381ExBRI0KzVo6iBTeX5De8eMZPJXig==", "license": "Apache-2.0", "dependencies": { "@react-aria/ssr": "^3.9.10", "@react-stately/flags": "^3.1.2", "@react-stately/utils": "^3.10.8", - "@react-types/shared": "^3.32.0", + "@react-types/shared": "^3.32.1", "@swc/helpers": "^0.5.0", "clsx": "^2.0.0" }, @@ -1001,9 +1363,9 @@ } }, "node_modules/@react-types/shared": { - "version": "3.32.0", - "resolved": "https://registry.npmjs.org/@react-types/shared/-/shared-3.32.0.tgz", - "integrity": "sha512-t+cligIJsZYFMSPFMvsJMjzlzde06tZMOIOFa1OV5Z0BcMowrb2g4mB57j/9nP28iJIRYn10xCniQts+qadrqQ==", + "version": "3.32.1", + "resolved": "https://registry.npmjs.org/@react-types/shared/-/shared-3.32.1.tgz", + "integrity": "sha512-famxyD5emrGGpFuUlgOP6fVW2h/ZaF405G5KDi3zPHzyjAWys/8W6NAVJtNbkCkhedmvL0xOhvt8feGXyXaw5w==", "license": "Apache-2.0", "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" @@ -1043,9 +1405,9 @@ "license": "MIT" }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.52.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.52.3.tgz", - "integrity": "sha512-h6cqHGZ6VdnwliFG1NXvMPTy/9PS3h8oLh7ImwR+kl+oYnQizgjxsONmmPSb2C66RksfkfIxEVtDSEcJiO0tqw==", + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.52.4.tgz", + "integrity": "sha512-BTm2qKNnWIQ5auf4deoetINJm2JzvihvGb9R6K/ETwKLql/Bb3Eg2H1FBp1gUb4YGbydMA3jcmQTR73q7J+GAA==", "cpu": [ "arm" ], @@ -1056,9 +1418,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.52.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.52.3.tgz", - "integrity": "sha512-wd+u7SLT/u6knklV/ifG7gr5Qy4GUbH2hMWcDauPFJzmCZUAJ8L2bTkVXC2niOIxp8lk3iH/QX8kSrUxVZrOVw==", + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.52.4.tgz", + "integrity": "sha512-P9LDQiC5vpgGFgz7GSM6dKPCiqR3XYN1WwJKA4/BUVDjHpYsf3iBEmVz62uyq20NGYbiGPR5cNHI7T1HqxNs2w==", "cpu": [ "arm64" ], @@ -1069,9 +1431,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.52.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.52.3.tgz", - "integrity": "sha512-lj9ViATR1SsqycwFkJCtYfQTheBdvlWJqzqxwc9f2qrcVrQaF/gCuBRTiTolkRWS6KvNxSk4KHZWG7tDktLgjg==", + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.52.4.tgz", + "integrity": "sha512-QRWSW+bVccAvZF6cbNZBJwAehmvG9NwfWHwMy4GbWi/BQIA/laTIktebT2ipVjNncqE6GLPxOok5hsECgAxGZg==", "cpu": [ "arm64" ], @@ -1082,9 +1444,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.52.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.52.3.tgz", - "integrity": "sha512-+Dyo7O1KUmIsbzx1l+4V4tvEVnVQqMOIYtrxK7ncLSknl1xnMHLgn7gddJVrYPNZfEB8CIi3hK8gq8bDhb3h5A==", + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.52.4.tgz", + "integrity": "sha512-hZgP05pResAkRJxL1b+7yxCnXPGsXU0fG9Yfd6dUaoGk+FhdPKCJ5L1Sumyxn8kvw8Qi5PvQ8ulenUbRjzeCTw==", "cpu": [ "x64" ], @@ -1095,9 +1457,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.52.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.52.3.tgz", - "integrity": "sha512-u9Xg2FavYbD30g3DSfNhxgNrxhi6xVG4Y6i9Ur1C7xUuGDW3banRbXj+qgnIrwRN4KeJ396jchwy9bCIzbyBEQ==", + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.52.4.tgz", + "integrity": "sha512-xmc30VshuBNUd58Xk4TKAEcRZHaXlV+tCxIXELiE9sQuK3kG8ZFgSPi57UBJt8/ogfhAF5Oz4ZSUBN77weM+mQ==", "cpu": [ "arm64" ], @@ -1108,9 +1470,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.52.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.52.3.tgz", - "integrity": "sha512-5M8kyi/OX96wtD5qJR89a/3x5x8x5inXBZO04JWhkQb2JWavOWfjgkdvUqibGJeNNaz1/Z1PPza5/tAPXICI6A==", + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.52.4.tgz", + "integrity": "sha512-WdSLpZFjOEqNZGmHflxyifolwAiZmDQzuOzIq9L27ButpCVpD7KzTRtEG1I0wMPFyiyUdOO+4t8GvrnBLQSwpw==", "cpu": [ "x64" ], @@ -1121,9 +1483,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.52.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.52.3.tgz", - "integrity": "sha512-IoerZJ4l1wRMopEHRKOO16e04iXRDyZFZnNZKrWeNquh5d6bucjezgd+OxG03mOMTnS1x7hilzb3uURPkJ0OfA==", + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.52.4.tgz", + "integrity": "sha512-xRiOu9Of1FZ4SxVbB0iEDXc4ddIcjCv2aj03dmW8UrZIW7aIQ9jVJdLBIhxBI+MaTnGAKyvMwPwQnoOEvP7FgQ==", "cpu": [ "arm" ], @@ -1134,9 +1496,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.52.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.52.3.tgz", - "integrity": "sha512-ZYdtqgHTDfvrJHSh3W22TvjWxwOgc3ThK/XjgcNGP2DIwFIPeAPNsQxrJO5XqleSlgDux2VAoWQ5iJrtaC1TbA==", + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.52.4.tgz", + "integrity": "sha512-FbhM2p9TJAmEIEhIgzR4soUcsW49e9veAQCziwbR+XWB2zqJ12b4i/+hel9yLiD8pLncDH4fKIPIbt5238341Q==", "cpu": [ "arm" ], @@ -1147,9 +1509,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.52.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.52.3.tgz", - "integrity": "sha512-NcViG7A0YtuFDA6xWSgmFb6iPFzHlf5vcqb2p0lGEbT+gjrEEz8nC/EeDHvx6mnGXnGCC1SeVV+8u+smj0CeGQ==", + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.52.4.tgz", + "integrity": "sha512-4n4gVwhPHR9q/g8lKCyz0yuaD0MvDf7dV4f9tHt0C73Mp8h38UCtSCSE6R9iBlTbXlmA8CjpsZoujhszefqueg==", "cpu": [ "arm64" ], @@ -1160,9 +1522,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.52.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.52.3.tgz", - "integrity": "sha512-d3pY7LWno6SYNXRm6Ebsq0DJGoiLXTb83AIPCXl9fmtIQs/rXoS8SJxxUNtFbJ5MiOvs+7y34np77+9l4nfFMw==", + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.52.4.tgz", + "integrity": "sha512-u0n17nGA0nvi/11gcZKsjkLj1QIpAuPFQbR48Subo7SmZJnGxDpspyw2kbpuoQnyK+9pwf3pAoEXerJs/8Mi9g==", "cpu": [ "arm64" ], @@ -1173,9 +1535,9 @@ ] }, "node_modules/@rollup/rollup-linux-loong64-gnu": { - "version": "4.52.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.52.3.tgz", - "integrity": "sha512-3y5GA0JkBuirLqmjwAKwB0keDlI6JfGYduMlJD/Rl7fvb4Ni8iKdQs1eiunMZJhwDWdCvrcqXRY++VEBbvk6Eg==", + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.52.4.tgz", + "integrity": "sha512-0G2c2lpYtbTuXo8KEJkDkClE/+/2AFPdPAbmaHoE870foRFs4pBrDehilMcrSScrN/fB/1HTaWO4bqw+ewBzMQ==", "cpu": [ "loong64" ], @@ -1186,9 +1548,9 @@ ] }, "node_modules/@rollup/rollup-linux-ppc64-gnu": { - "version": "4.52.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.52.3.tgz", - "integrity": "sha512-AUUH65a0p3Q0Yfm5oD2KVgzTKgwPyp9DSXc3UA7DtxhEb/WSPfbG4wqXeSN62OG5gSo18em4xv6dbfcUGXcagw==", + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.52.4.tgz", + "integrity": "sha512-teSACug1GyZHmPDv14VNbvZFX779UqWTsd7KtTM9JIZRDI5NUwYSIS30kzI8m06gOPB//jtpqlhmraQ68b5X2g==", "cpu": [ "ppc64" ], @@ -1199,9 +1561,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.52.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.52.3.tgz", - "integrity": "sha512-1makPhFFVBqZE+XFg3Dkq+IkQ7JvmUrwwqaYBL2CE+ZpxPaqkGaiWFEWVGyvTwZace6WLJHwjVh/+CXbKDGPmg==", + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.52.4.tgz", + "integrity": "sha512-/MOEW3aHjjs1p4Pw1Xk4+3egRevx8Ji9N6HUIA1Ifh8Q+cg9dremvFCUbOX2Zebz80BwJIgCBUemjqhU5XI5Eg==", "cpu": [ "riscv64" ], @@ -1212,9 +1574,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.52.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.52.3.tgz", - "integrity": "sha512-OOFJa28dxfl8kLOPMUOQBCO6z3X2SAfzIE276fwT52uXDWUS178KWq0pL7d6p1kz7pkzA0yQwtqL0dEPoVcRWg==", + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.52.4.tgz", + "integrity": "sha512-1HHmsRyh845QDpEWzOFtMCph5Ts+9+yllCrREuBR/vg2RogAQGGBRC8lDPrPOMnrdOJ+mt1WLMOC2Kao/UwcvA==", "cpu": [ "riscv64" ], @@ -1225,9 +1587,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.52.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.52.3.tgz", - "integrity": "sha512-jMdsML2VI5l+V7cKfZx3ak+SLlJ8fKvLJ0Eoa4b9/vCUrzXKgoKxvHqvJ/mkWhFiyp88nCkM5S2v6nIwRtPcgg==", + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.52.4.tgz", + "integrity": "sha512-seoeZp4L/6D1MUyjWkOMRU6/iLmCU2EjbMTyAG4oIOs1/I82Y5lTeaxW0KBfkUdHAWN7j25bpkt0rjnOgAcQcA==", "cpu": [ "s390x" ], @@ -1238,9 +1600,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.52.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.52.3.tgz", - "integrity": "sha512-tPgGd6bY2M2LJTA1uGq8fkSPK8ZLYjDjY+ZLK9WHncCnfIz29LIXIqUgzCR0hIefzy6Hpbe8Th5WOSwTM8E7LA==", + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.52.4.tgz", + "integrity": "sha512-Wi6AXf0k0L7E2gteNsNHUs7UMwCIhsCTs6+tqQ5GPwVRWMaflqGec4Sd8n6+FNFDw9vGcReqk2KzBDhCa1DLYg==", "cpu": [ "x64" ], @@ -1251,9 +1613,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.52.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.52.3.tgz", - "integrity": "sha512-BCFkJjgk+WFzP+tcSMXq77ymAPIxsX9lFJWs+2JzuZTLtksJ2o5hvgTdIcZ5+oKzUDMwI0PfWzRBYAydAHF2Mw==", + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.52.4.tgz", + "integrity": "sha512-dtBZYjDmCQ9hW+WgEkaffvRRCKm767wWhxsFW3Lw86VXz/uJRuD438/XvbZT//B96Vs8oTA8Q4A0AfHbrxP9zw==", "cpu": [ "x64" ], @@ -1264,9 +1626,9 @@ ] }, "node_modules/@rollup/rollup-openharmony-arm64": { - "version": "4.52.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.52.3.tgz", - "integrity": "sha512-KTD/EqjZF3yvRaWUJdD1cW+IQBk4fbQaHYJUmP8N4XoKFZilVL8cobFSTDnjTtxWJQ3JYaMgF4nObY/+nYkumA==", + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.52.4.tgz", + "integrity": "sha512-1ox+GqgRWqaB1RnyZXL8PD6E5f7YyRUJYnCqKpNzxzP0TkaUh112NDrR9Tt+C8rJ4x5G9Mk8PQR3o7Ku2RKqKA==", "cpu": [ "arm64" ], @@ -1277,9 +1639,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.52.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.52.3.tgz", - "integrity": "sha512-+zteHZdoUYLkyYKObGHieibUFLbttX2r+58l27XZauq0tcWYYuKUwY2wjeCN9oK1Um2YgH2ibd6cnX/wFD7DuA==", + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.52.4.tgz", + "integrity": "sha512-8GKr640PdFNXwzIE0IrkMWUNUomILLkfeHjXBi/nUvFlpZP+FA8BKGKpacjW6OUUHaNI6sUURxR2U2g78FOHWQ==", "cpu": [ "arm64" ], @@ -1290,9 +1652,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.52.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.52.3.tgz", - "integrity": "sha512-of1iHkTQSo3kr6dTIRX6t81uj/c/b15HXVsPcEElN5sS859qHrOepM5p9G41Hah+CTqSh2r8Bm56dL2z9UQQ7g==", + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.52.4.tgz", + "integrity": "sha512-AIy/jdJ7WtJ/F6EcfOb2GjR9UweO0n43jNObQMb6oGxkYTfLcnN7vYYpG+CN3lLxrQkzWnMOoNSHTW54pgbVxw==", "cpu": [ "ia32" ], @@ -1303,9 +1665,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-gnu": { - "version": "4.52.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.52.3.tgz", - "integrity": "sha512-s0hybmlHb56mWVZQj8ra9048/WZTPLILKxcvcq+8awSZmyiSUZjjem1AhU3Tf4ZKpYhK4mg36HtHDOe8QJS5PQ==", + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.52.4.tgz", + "integrity": "sha512-UF9KfsH9yEam0UjTwAgdK0anlQ7c8/pWPU2yVjyWcF1I1thABt6WXE47cI71pGiZ8wGvxohBoLnxM04L/wj8mQ==", "cpu": [ "x64" ], @@ -1316,9 +1678,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.52.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.52.3.tgz", - "integrity": "sha512-zGIbEVVXVtauFgl3MRwGWEN36P5ZGenHRMgNw88X5wEhEBpq0XrMEZwOn07+ICrwM17XO5xfMZqh0OldCH5VTA==", + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.52.4.tgz", + "integrity": "sha512-bf9PtUa0u8IXDVxzRToFQKsNCRz9qLYfR/MpECxl4mRoWYjAeFjgxj1XdZr2M/GNVpT05p+LgQOHopYDlUu6/w==", "cpu": [ "x64" ], @@ -1334,6 +1696,23 @@ "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==", "license": "MIT" }, + "node_modules/@sinclair/typebox": { + "version": "0.31.28", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.31.28.tgz", + "integrity": "sha512-/s55Jujywdw/Jpan+vsy6JZs1z2ZTGxTmbZTPiuSL2wz9mfzA2gN1zzaqmvfi4pq+uOt7Du85fkiwv5ymW84aQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@sqlite.org/sqlite-wasm": { + "version": "3.48.0-build4", + "resolved": "https://registry.npmjs.org/@sqlite.org/sqlite-wasm/-/sqlite-wasm-3.48.0-build4.tgz", + "integrity": "sha512-hI6twvUkzOmyGZhQMza1gpfqErZxXRw6JEsiVjUbo7tFanVD+8Oil0Ih3l2nGzHdxPI41zFmfUQG7GHqhciKZQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "sqlite-wasm": "bin/index.js" + } + }, "node_modules/@standard-schema/spec": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.0.0.tgz", @@ -2007,21 +2386,21 @@ "license": "MIT" }, "node_modules/@types/react": { - "version": "19.1.17", - "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.17.tgz", - "integrity": "sha512-Qec1E3mhALmaspIrhWt9jkQMNdw6bReVu64mjvhbhq2NFPftLPVr+l1SZgmw/66WwBNpDh7ao5AT6gF5v41PFA==", + "version": "19.2.2", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.2.tgz", + "integrity": "sha512-6mDvHUFSjyT2B2yeNx2nUgMxh9LtOWvkhIU3uePn2I2oyNymUAX1NIsdgviM4CH+JSrp2D2hsMvJOkxY+0wNRA==", "license": "MIT", "dependencies": { "csstype": "^3.0.2" } }, "node_modules/@types/react-dom": { - "version": "19.1.11", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.1.11.tgz", - "integrity": "sha512-3BKc/yGdNTYQVVw4idqHtSOcFsgGuBbMveKCOgF8wQ5QtrYOc3jDIlzg3jef04zcXFIHLelyGlj0T+BJ8+KN+w==", + "version": "19.2.1", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.1.tgz", + "integrity": "sha512-/EEvYBdT3BflCWvTMO7YkYBHVE9Ci6XdqZciZANQgKpaiDRGOLIlRo91jbTNRQjgPFWVaRxcYc0luVNFitz57A==", "license": "MIT", "peerDependencies": { - "@types/react": "^19.0.0" + "@types/react": "^19.2.0" } }, "node_modules/@types/semver": { @@ -2045,17 +2424,17 @@ "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.45.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.45.0.tgz", - "integrity": "sha512-HC3y9CVuevvWCl/oyZuI47dOeDF9ztdMEfMH8/DW/Mhwa9cCLnK1oD7JoTVGW/u7kFzNZUKUoyJEqkaJh5y3Wg==", + "version": "8.46.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.46.0.tgz", + "integrity": "sha512-hA8gxBq4ukonVXPy0OKhiaUh/68D0E88GSmtC1iAEnGaieuDi38LhS7jdCHRLi6ErJBNDGCzvh5EnzdPwUc0DA==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.45.0", - "@typescript-eslint/type-utils": "8.45.0", - "@typescript-eslint/utils": "8.45.0", - "@typescript-eslint/visitor-keys": "8.45.0", + "@typescript-eslint/scope-manager": "8.46.0", + "@typescript-eslint/type-utils": "8.46.0", + "@typescript-eslint/utils": "8.46.0", + "@typescript-eslint/visitor-keys": "8.46.0", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", @@ -2069,7 +2448,7 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.45.0", + "@typescript-eslint/parser": "^8.46.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } @@ -2085,16 +2464,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.45.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.45.0.tgz", - "integrity": "sha512-TGf22kon8KW+DeKaUmOibKWktRY8b2NSAZNdtWh798COm1NWx8+xJ6iFBtk3IvLdv6+LGLJLRlyhrhEDZWargQ==", + "version": "8.46.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.46.0.tgz", + "integrity": "sha512-n1H6IcDhmmUEG7TNVSspGmiHHutt7iVKtZwRppD7e04wha5MrkV1h3pti9xQLcCMt6YWsncpoT0HMjkH1FNwWQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.45.0", - "@typescript-eslint/types": "8.45.0", - "@typescript-eslint/typescript-estree": "8.45.0", - "@typescript-eslint/visitor-keys": "8.45.0", + "@typescript-eslint/scope-manager": "8.46.0", + "@typescript-eslint/types": "8.46.0", + "@typescript-eslint/typescript-estree": "8.46.0", + "@typescript-eslint/visitor-keys": "8.46.0", "debug": "^4.3.4" }, "engines": { @@ -2110,14 +2489,14 @@ } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.45.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.45.0.tgz", - "integrity": "sha512-3pcVHwMG/iA8afdGLMuTibGR7pDsn9RjDev6CCB+naRsSYs2pns5QbinF4Xqw6YC/Sj3lMrm/Im0eMfaa61WUg==", + "version": "8.46.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.46.0.tgz", + "integrity": "sha512-OEhec0mH+U5Je2NZOeK1AbVCdm0ChyapAyTeXVIYTPXDJ3F07+cu87PPXcGoYqZ7M9YJVvFnfpGg1UmCIqM+QQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.45.0", - "@typescript-eslint/types": "^8.45.0", + "@typescript-eslint/tsconfig-utils": "^8.46.0", + "@typescript-eslint/types": "^8.46.0", "debug": "^4.3.4" }, "engines": { @@ -2132,14 +2511,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.45.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.45.0.tgz", - "integrity": "sha512-clmm8XSNj/1dGvJeO6VGH7EUSeA0FMs+5au/u3lrA3KfG8iJ4u8ym9/j2tTEoacAffdW1TVUzXO30W1JTJS7dA==", + "version": "8.46.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.46.0.tgz", + "integrity": "sha512-lWETPa9XGcBes4jqAMYD9fW0j4n6hrPtTJwWDmtqgFO/4HF4jmdH/Q6wggTw5qIT5TXjKzbt7GsZUBnWoO3dqw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.45.0", - "@typescript-eslint/visitor-keys": "8.45.0" + "@typescript-eslint/types": "8.46.0", + "@typescript-eslint/visitor-keys": "8.46.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2150,9 +2529,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.45.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.45.0.tgz", - "integrity": "sha512-aFdr+c37sc+jqNMGhH+ajxPXwjv9UtFZk79k8pLoJ6p4y0snmYpPA52GuWHgt2ZF4gRRW6odsEj41uZLojDt5w==", + "version": "8.46.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.46.0.tgz", + "integrity": "sha512-WrYXKGAHY836/N7zoK/kzi6p8tXFhasHh8ocFL9VZSAkvH956gfeRfcnhs3xzRy8qQ/dq3q44v1jvQieMFg2cw==", "dev": true, "license": "MIT", "engines": { @@ -2167,15 +2546,15 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.45.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.45.0.tgz", - "integrity": "sha512-bpjepLlHceKgyMEPglAeULX1vixJDgaKocp0RVJ5u4wLJIMNuKtUXIczpJCPcn2waII0yuvks/5m5/h3ZQKs0A==", + "version": "8.46.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.46.0.tgz", + "integrity": "sha512-hy+lvYV1lZpVs2jRaEYvgCblZxUoJiPyCemwbQZ+NGulWkQRy0HRPYAoef/CNSzaLt+MLvMptZsHXHlkEilaeg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.45.0", - "@typescript-eslint/typescript-estree": "8.45.0", - "@typescript-eslint/utils": "8.45.0", + "@typescript-eslint/types": "8.46.0", + "@typescript-eslint/typescript-estree": "8.46.0", + "@typescript-eslint/utils": "8.46.0", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" }, @@ -2192,9 +2571,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.45.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.45.0.tgz", - "integrity": "sha512-WugXLuOIq67BMgQInIxxnsSyRLFxdkJEJu8r4ngLR56q/4Q5LrbfkFRH27vMTjxEK8Pyz7QfzuZe/G15qQnVRA==", + "version": "8.46.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.46.0.tgz", + "integrity": "sha512-bHGGJyVjSE4dJJIO5yyEWt/cHyNwga/zXGJbJJ8TiO01aVREK6gCTu3L+5wrkb1FbDkQ+TKjMNe9R/QQQP9+rA==", "dev": true, "license": "MIT", "engines": { @@ -2206,16 +2585,16 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.45.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.45.0.tgz", - "integrity": "sha512-GfE1NfVbLam6XQ0LcERKwdTTPlLvHvXXhOeUGC1OXi4eQBoyy1iVsW+uzJ/J9jtCz6/7GCQ9MtrQ0fml/jWCnA==", + "version": "8.46.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.46.0.tgz", + "integrity": "sha512-ekDCUfVpAKWJbRfm8T1YRrCot1KFxZn21oV76v5Fj4tr7ELyk84OS+ouvYdcDAwZL89WpEkEj2DKQ+qg//+ucg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.45.0", - "@typescript-eslint/tsconfig-utils": "8.45.0", - "@typescript-eslint/types": "8.45.0", - "@typescript-eslint/visitor-keys": "8.45.0", + "@typescript-eslint/project-service": "8.46.0", + "@typescript-eslint/tsconfig-utils": "8.46.0", + "@typescript-eslint/types": "8.46.0", + "@typescript-eslint/visitor-keys": "8.46.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -2261,16 +2640,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.45.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.45.0.tgz", - "integrity": "sha512-bxi1ht+tLYg4+XV2knz/F7RVhU0k6VrSMc9sb8DQ6fyCTrGQLHfo7lDtN0QJjZjKkLA2ThrKuCdHEvLReqtIGg==", + "version": "8.46.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.46.0.tgz", + "integrity": "sha512-nD6yGWPj1xiOm4Gk0k6hLSZz2XkNXhuYmyIrOWcHoPuAhjT9i5bAG+xbWPgFeNR8HPHHtpNKdYUXJl/D3x7f5g==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", - "@typescript-eslint/scope-manager": "8.45.0", - "@typescript-eslint/types": "8.45.0", - "@typescript-eslint/typescript-estree": "8.45.0" + "@typescript-eslint/scope-manager": "8.46.0", + "@typescript-eslint/types": "8.46.0", + "@typescript-eslint/typescript-estree": "8.46.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2285,13 +2664,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.45.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.45.0.tgz", - "integrity": "sha512-qsaFBA3e09MIDAGFUrTk+dzqtfv1XPVz8t8d1f0ybTzrCY7BKiMC5cjrl1O/P7UmHsNyW90EYSkU/ZWpmXelag==", + "version": "8.46.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.46.0.tgz", + "integrity": "sha512-FrvMpAK+hTbFy7vH5j1+tMYHMSKLE6RzluFJlkFNKD0p9YsUT75JlBSmr5so3QRzvMwU5/bIEdeNrxm8du8l3Q==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.45.0", + "@typescript-eslint/types": "8.46.0", "eslint-visitor-keys": "^4.2.1" }, "engines": { @@ -2494,6 +2873,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/array-timsort": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-timsort/-/array-timsort-1.0.3.tgz", + "integrity": "sha512-/+3GRL7dDAGEfM6TseQk/U+mi18TU2Ms9I3UlLdUMhz2hbvGNTKdj9xniwXfUqgYhHxRx0+8UnKkvlNwVU+cWQ==", + "dev": true, + "license": "MIT" + }, "node_modules/array.prototype.findlast": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz", @@ -2679,9 +3065,9 @@ "license": "MIT" }, "node_modules/baseline-browser-mapping": { - "version": "2.8.10", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.10.tgz", - "integrity": "sha512-uLfgBi+7IBNay8ECBO2mVMGZAc1VgZWEChxm4lv+TobGdG82LnXMjuNGo/BSSZZL4UmkWhxEHP2f5ziLNwGWMA==", + "version": "2.8.13", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.13.tgz", + "integrity": "sha512-7s16KR8io8nIBWQyCYhmFhd+ebIzb9VKTzki+wOJXHTxTnV6+mFGH3+Jwn1zoKaY9/H9T/0BcKCZnzXljPnpSQ==", "dev": true, "license": "Apache-2.0", "bin": { @@ -2802,9 +3188,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001746", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001746.tgz", - "integrity": "sha512-eA7Ys/DGw+pnkWWSE/id29f2IcPHVoE8wxtvE5JdvD2V28VTDPy1yEeo11Guz0sJ4ZeGRcm3uaTcAqK1LXaphA==", + "version": "1.0.30001748", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001748.tgz", + "integrity": "sha512-5P5UgAr0+aBmNiplks08JLw+AW/XG/SurlgZLgB1dDLfAw7EfRGxIwzPHxdSCGY/BTKDqIVyJL87cCN6s0ZR0w==", "dev": true, "funding": [ { @@ -2875,12 +3261,54 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "license": "MIT" }, + "node_modules/commander": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz", + "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16" + } + }, + "node_modules/comment-json": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/comment-json/-/comment-json-4.4.1.tgz", + "integrity": "sha512-r1To31BQD5060QdkC+Iheai7gHwoSZobzunqkf2/kQ6xIAfJyrKNAFUwdKvkK7Qgu7pVTKQEa7ok7Ed3ycAJgg==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-timsort": "^1.0.3", + "core-util-is": "^1.0.3", + "esprima": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "license": "MIT" }, + "node_modules/consola": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/consola/-/consola-3.4.0.tgz", + "integrity": "sha512-EiPU8G6dQG0GFHNR8ljnZFki/8a+cQwEQ+7wpxdChl02Q8HXlwEZWD5lqAF8vC2sEC3Tehr8hy7vErz88LHyUA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.18.0 || >=16.10.0" + } + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, "node_modules/cookie": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.0.2.tgz", @@ -2890,6 +3318,13 @@ "node": ">=18" } }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true, + "license": "MIT" + }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -3144,6 +3579,21 @@ "integrity": "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==", "license": "MIT" }, + "node_modules/dedent": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.1.tgz", + "integrity": "sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "babel-plugin-macros": "^3.1.0" + }, + "peerDependenciesMeta": { + "babel-plugin-macros": { + "optional": true + } + } + }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", @@ -3185,9 +3635,9 @@ } }, "node_modules/detect-libc": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.1.tgz", - "integrity": "sha512-ecqj/sy1jcK1uWrwpR67UhYrIFQ+5WlGxth34WquCbamhFA6hkkwiu37o6J5xCHdo1oixJRfVRw+ywV+Hq/0Aw==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", "devOptional": true, "license": "Apache-2.0", "engines": { @@ -3221,9 +3671,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.228", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.228.tgz", - "integrity": "sha512-nxkiyuqAn4MJ1QbobwqJILiDtu/jk14hEAWaMiJmNPh1Z+jqoFlBFZjdXwLWGeVSeu9hGLg6+2G9yJaW8rBIFA==", + "version": "1.5.232", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.232.tgz", + "integrity": "sha512-ENirSe7wf8WzyPCibqKUG1Cg43cPaxH4wRR7AJsX7MCABCHBIOFqvaYODSLKUuZdraxUTHRE/0A2Aq8BYKEHOg==", "dev": true, "license": "ISC" }, @@ -3462,6 +3912,19 @@ "@esbuild/win32-x64": "0.25.10" } }, + "node_modules/esbuild-wasm": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/esbuild-wasm/-/esbuild-wasm-0.19.12.tgz", + "integrity": "sha512-Zmc4hk6FibJZBcTx5/8K/4jT3/oG1vkGTEeKJUQFCUQKimD6Q7+adp/bdVQyYJFolMKaXkQnVZdV4O5ZaTYmyQ==", + "dev": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/escalade": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", @@ -3485,19 +3948,19 @@ } }, "node_modules/eslint": { - "version": "9.36.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.36.0.tgz", - "integrity": "sha512-hB4FIzXovouYzwzECDcUkJ4OcfOEkXTv2zRY6B9bkwjx/cprAq0uvm1nl7zvQ0/TsUk0zQiN4uPfJpB9m+rPMQ==", + "version": "9.37.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.37.0.tgz", + "integrity": "sha512-XyLmROnACWqSxiGYArdef1fItQd47weqB7iwtfr9JHwRrqIXZdcFMvvEcL9xHCmL0SNsOvF0c42lWyM1U5dgig==", "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.21.0", - "@eslint/config-helpers": "^0.3.1", - "@eslint/core": "^0.15.2", + "@eslint/config-helpers": "^0.4.0", + "@eslint/core": "^0.16.0", "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.36.0", - "@eslint/plugin-kit": "^0.3.5", + "@eslint/js": "9.37.0", + "@eslint/plugin-kit": "^0.4.0", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", @@ -3703,22 +4166,28 @@ } }, "node_modules/eslint-plugin-react-hooks": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.2.0.tgz", - "integrity": "sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==", + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-6.1.1.tgz", + "integrity": "sha512-St9EKZzOAQF704nt2oJvAKZHjhrpg25ClQoaAlHmPZuajFldVLqRDW4VBNAS01NzeiQF0m0qhG1ZA807K6aVaQ==", "dev": true, "license": "MIT", + "dependencies": { + "@babel/core": "^7.24.4", + "@babel/parser": "^7.24.4", + "zod": "^3.22.4 || ^4.0.0", + "zod-validation-error": "^3.0.3 || ^4.0.0" + }, "engines": { - "node": ">=10" + "node": ">=18" }, "peerDependencies": { "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" } }, "node_modules/eslint-plugin-react-refresh": { - "version": "0.4.22", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.22.tgz", - "integrity": "sha512-atkAG6QaJMGoTLc4MDAP+rqZcfwQuTIh2IqHWFLy2TEjxr0MOK+5BSG4RzL2564AAPpZkDRsZXAUz68kjnU6Ug==", + "version": "0.4.23", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.23.tgz", + "integrity": "sha512-G4j+rv0NmbIR45kni5xJOrYvCtyD3/7LjpVH8MPPcudXDcNu8gv+4ATTDXTtbRR8rTCM5HxECvCSsRmxKnWDsA==", "dev": true, "license": "MIT", "peerDependencies": { @@ -3781,18 +4250,6 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/eslint/node_modules/@eslint/core": { - "version": "0.15.2", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.2.tgz", - "integrity": "sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg==", - "license": "Apache-2.0", - "dependencies": { - "@types/json-schema": "^7.0.15" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, "node_modules/eslint/node_modules/eslint-visitor-keys": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", @@ -3834,6 +4291,20 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/esquery": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", @@ -3981,6 +4452,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/flat": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/flat/-/flat-6.0.1.tgz", + "integrity": "sha512-/3FfIa8mbrg3xE7+wAhWeV+bd7L2Mof+xtZb5dRDKZ+wDvYJK4WDYeIOuOhre5Yv5aQObZrlbRmk3RTSiuQBtw==", + "dev": true, + "license": "BSD-3-Clause", + "bin": { + "flat": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/flat-cache": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", @@ -4142,6 +4626,16 @@ "node": ">= 0.4" } }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/get-intrinsic": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", @@ -4245,9 +4739,9 @@ "license": "MIT" }, "node_modules/goober": { - "version": "2.1.16", - "resolved": "https://registry.npmjs.org/goober/-/goober-2.1.16.tgz", - "integrity": "sha512-erjk19y1U33+XAMe1VTvIONHYoSqE4iS7BYUZfHaqeohLmnC0FdxEh7rQU+6MZ4OajItzjZFSRtVANrQwNq6/g==", + "version": "2.1.18", + "resolved": "https://registry.npmjs.org/goober/-/goober-2.1.18.tgz", + "integrity": "sha512-2vFqsaDVIT9Gz7N6kAL++pLpp41l3PfDuusHcjnGLfR6+huZkl6ziX+zgVC3ZxpqWhzH6pyDdGrCeDhMIvwaxw==", "license": "MIT", "peerDependencies": { "csstype": "^3.0.10" @@ -4366,6 +4860,16 @@ "node": ">= 0.4" } }, + "node_modules/human-id": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/human-id/-/human-id-4.1.2.tgz", + "integrity": "sha512-v/J+4Z/1eIJovEBdlV5TYj1IR+ZiohcYGRY+qN/oC9dAfKzVT023N/Bgw37hrKCoVRBvk3bqyzpr2PP5YeTMSg==", + "dev": true, + "license": "MIT", + "bin": { + "human-id": "dist/cli.js" + } + }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -4847,6 +5351,13 @@ "integrity": "sha512-hNngCeKxIUQiEUN3GPJOkz4wF/YvdUdbNL9hsBcMQTkKzboD7T/q3OYOuuPZLUE6dBxSGpwhk5mwuDud7JVAow==", "license": "BSD-3-Clause" }, + "node_modules/js-sha256": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/js-sha256/-/js-sha256-0.11.1.tgz", + "integrity": "sha512-o6WSo/LUvY2uC4j7mO50a2ms7E/EAdbP0swigLV+nzHKTTaYnaLIWJ02VdXrsJX0vGedDESQnLsOekr94ryfjg==", + "dev": true, + "license": "MIT" + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -4866,6 +5377,19 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/json-buffer": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", @@ -4885,15 +5409,16 @@ "license": "MIT" }, "node_modules/json5": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", - "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, "license": "MIT", - "dependencies": { - "minimist": "^1.2.0" - }, "bin": { "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" } }, "node_modules/jsx-ast-utils": { @@ -4921,6 +5446,16 @@ "json-buffer": "3.0.1" } }, + "node_modules/kysely": { + "version": "0.27.6", + "resolved": "https://registry.npmjs.org/kysely/-/kysely-0.27.6.tgz", + "integrity": "sha512-FIyV/64EkKhJmjgC0g2hygpBv5RNWVPyNCqSAD7eTCv6eFWNIi4PN1UvdSJGicN/o35bnevgis4Y0UDC0qi8jQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -5209,6 +5744,16 @@ "loose-envify": "cli.js" } }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, "node_modules/magic-string": { "version": "0.30.19", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.19.tgz", @@ -5351,9 +5896,9 @@ "license": "MIT" }, "node_modules/node-releases": { - "version": "2.0.21", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.21.tgz", - "integrity": "sha512-5b0pgg78U3hwXkCM8Z9b2FJdPZlr9Psr9V2gQPESdGHqbntyFJKFW4r5TeWGFzafGY3hzs1JC62VEQMbl1JFkw==", + "version": "2.0.23", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.23.tgz", + "integrity": "sha512-cCmFDMSm26S6tQSDpBCg/NR8NENrVPhAJSf+XbxBG4rPFaaonlEoE9wHQmun+cls499TQGSb7ZyPBRlzgKfpeg==", "dev": true, "license": "MIT" }, @@ -5823,9 +6368,9 @@ "license": "MIT" }, "node_modules/react": { - "version": "19.1.1", - "resolved": "https://registry.npmjs.org/react/-/react-19.1.1.tgz", - "integrity": "sha512-w8nqGImo45dmMIfljjMwOGtbmC/mk4CMYhWIicdSflH91J9TyCyczcPFXJzrZ/ZXcgGRFeP6BU0BEJTw6tZdfQ==", + "version": "19.2.0", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.0.tgz", + "integrity": "sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ==", "license": "MIT", "engines": { "node": ">=0.10.0" @@ -5845,15 +6390,15 @@ } }, "node_modules/react-dom": { - "version": "19.1.1", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.1.tgz", - "integrity": "sha512-Dlq/5LAZgF0Gaz6yiqZCf6VCcZs1ghAJyrsu84Q/GT0gV+mCxbfmKNoGRKBYMJ8IEdGPqu49YWXD02GCknEDkw==", + "version": "19.2.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.0.tgz", + "integrity": "sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ==", "license": "MIT", "dependencies": { - "scheduler": "^0.26.0" + "scheduler": "^0.27.0" }, "peerDependencies": { - "react": "^19.1.1" + "react": "^19.2.0" } }, "node_modules/react-hot-toast": { @@ -5883,9 +6428,9 @@ } }, "node_modules/react-is": { - "version": "19.1.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.1.1.tgz", - "integrity": "sha512-tr41fA15Vn8p4X9ntI+yCyeGSf1TlYaY5vlTZfQmeLBrFo3psOPX6HhTDnFNL9uj3EhP0KAQ80cugCl4b4BERA==", + "version": "19.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.2.0.tgz", + "integrity": "sha512-x3Ax3kNSMIIkyVYhWPyO09bu0uttcAIoecO/um/rKGQ4EltYWVYtyiGkS/3xMynrbVQdS69Jhlv8FXUEZehlzA==", "license": "MIT", "peer": true }, @@ -5935,9 +6480,9 @@ } }, "node_modules/react-simple-keyboard": { - "version": "3.8.125", - "resolved": "https://registry.npmjs.org/react-simple-keyboard/-/react-simple-keyboard-3.8.125.tgz", - "integrity": "sha512-8+PbmGA2auM7V57hapHsKV7IJcJVl0QNNW09RJQ7xCiohHuZNKvqrxGvisxhhr7X8C8TKulxbqdxjZbFelwO7w==", + "version": "3.8.126", + "resolved": "https://registry.npmjs.org/react-simple-keyboard/-/react-simple-keyboard-3.8.126.tgz", + "integrity": "sha512-eULRTm9Rrvo72+WnU9h3fTooSLMgJ52Yi5VvVm0SirMKQnigRgOlcx1VJeVdFVN4JfJEuSN1voV+Vsmeheg3vA==", "license": "MIT", "peerDependencies": { "react": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", @@ -6090,9 +6635,9 @@ } }, "node_modules/rollup": { - "version": "4.52.3", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.52.3.tgz", - "integrity": "sha512-RIDh866U8agLgiIcdpB+COKnlCreHJLfIhWC3LVflku5YHfpnsIKigRZeFfMfCc4dVcqNVfQQ5gO/afOck064A==", + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.52.4.tgz", + "integrity": "sha512-CLEVl+MnPAiKh5pl4dEWSyMTpuflgNQiLGhMv8ezD5W/qP8AKvmYpCOKRRNOh7oRKnauBZ4SyeYkMS+1VSyKwQ==", "license": "MIT", "dependencies": { "@types/estree": "1.0.8" @@ -6105,28 +6650,28 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.52.3", - "@rollup/rollup-android-arm64": "4.52.3", - "@rollup/rollup-darwin-arm64": "4.52.3", - "@rollup/rollup-darwin-x64": "4.52.3", - "@rollup/rollup-freebsd-arm64": "4.52.3", - "@rollup/rollup-freebsd-x64": "4.52.3", - "@rollup/rollup-linux-arm-gnueabihf": "4.52.3", - "@rollup/rollup-linux-arm-musleabihf": "4.52.3", - "@rollup/rollup-linux-arm64-gnu": "4.52.3", - "@rollup/rollup-linux-arm64-musl": "4.52.3", - "@rollup/rollup-linux-loong64-gnu": "4.52.3", - "@rollup/rollup-linux-ppc64-gnu": "4.52.3", - "@rollup/rollup-linux-riscv64-gnu": "4.52.3", - "@rollup/rollup-linux-riscv64-musl": "4.52.3", - "@rollup/rollup-linux-s390x-gnu": "4.52.3", - "@rollup/rollup-linux-x64-gnu": "4.52.3", - "@rollup/rollup-linux-x64-musl": "4.52.3", - "@rollup/rollup-openharmony-arm64": "4.52.3", - "@rollup/rollup-win32-arm64-msvc": "4.52.3", - "@rollup/rollup-win32-ia32-msvc": "4.52.3", - "@rollup/rollup-win32-x64-gnu": "4.52.3", - "@rollup/rollup-win32-x64-msvc": "4.52.3", + "@rollup/rollup-android-arm-eabi": "4.52.4", + "@rollup/rollup-android-arm64": "4.52.4", + "@rollup/rollup-darwin-arm64": "4.52.4", + "@rollup/rollup-darwin-x64": "4.52.4", + "@rollup/rollup-freebsd-arm64": "4.52.4", + "@rollup/rollup-freebsd-x64": "4.52.4", + "@rollup/rollup-linux-arm-gnueabihf": "4.52.4", + "@rollup/rollup-linux-arm-musleabihf": "4.52.4", + "@rollup/rollup-linux-arm64-gnu": "4.52.4", + "@rollup/rollup-linux-arm64-musl": "4.52.4", + "@rollup/rollup-linux-loong64-gnu": "4.52.4", + "@rollup/rollup-linux-ppc64-gnu": "4.52.4", + "@rollup/rollup-linux-riscv64-gnu": "4.52.4", + "@rollup/rollup-linux-riscv64-musl": "4.52.4", + "@rollup/rollup-linux-s390x-gnu": "4.52.4", + "@rollup/rollup-linux-x64-gnu": "4.52.4", + "@rollup/rollup-linux-x64-musl": "4.52.4", + "@rollup/rollup-openharmony-arm64": "4.52.4", + "@rollup/rollup-win32-arm64-msvc": "4.52.4", + "@rollup/rollup-win32-ia32-msvc": "4.52.4", + "@rollup/rollup-win32-x64-gnu": "4.52.4", + "@rollup/rollup-win32-x64-msvc": "4.52.4", "fsevents": "~2.3.2" } }, @@ -6207,9 +6752,9 @@ } }, "node_modules/scheduler": { - "version": "0.26.0", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz", - "integrity": "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==", + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", + "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", "license": "MIT" }, "node_modules/semver": { @@ -6379,6 +6924,18 @@ "node": ">=0.10.0" } }, + "node_modules/sqlite-wasm-kysely": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/sqlite-wasm-kysely/-/sqlite-wasm-kysely-0.3.0.tgz", + "integrity": "sha512-TzjBNv7KwRw6E3pdKdlRyZiTmUIE0UttT/Sl56MVwVARl/u5gp978KepazCJZewFUnlWHz9i3NQd4kOtP/Afdg==", + "dev": true, + "dependencies": { + "@sqlite.org/sqlite-wasm": "^3.48.0-build2" + }, + "peerDependencies": { + "kysely": "*" + } + }, "node_modules/stop-iteration-iterator": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", @@ -6555,9 +7112,9 @@ "license": "MIT" }, "node_modules/tapable": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.3.tgz", - "integrity": "sha512-ZL6DDuAlRlLGghwcfmSn9sK3Hr6ArtyudlSAiCqQ6IfE+b+HHbydbYDIG15IfS5do+7XQQBdBiubF/cV2dnDzg==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz", + "integrity": "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==", "dev": true, "license": "MIT", "engines": { @@ -6585,6 +7142,16 @@ "node": ">=18" } }, + "node_modules/tar/node_modules/yallist": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", + "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, "node_modules/tiny-invariant": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz", @@ -6695,6 +7262,18 @@ "strip-bom": "^3.0.0" } }, + "node_modules/tsconfig-paths/node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "license": "MIT", + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, "node_modules/tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", @@ -6819,6 +7398,35 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/unplugin": { + "version": "2.3.10", + "resolved": "https://registry.npmjs.org/unplugin/-/unplugin-2.3.10.tgz", + "integrity": "sha512-6NCPkv1ClwH+/BGE9QeoTIl09nuiAt0gS28nn1PvYXsGKRwM2TCbFA2QiilmehPDTXIe684k4rZI1yl3A1PCUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/remapping": "^2.3.5", + "acorn": "^8.15.0", + "picomatch": "^4.0.3", + "webpack-virtual-modules": "^0.6.2" + }, + "engines": { + "node": ">=18.12.0" + } + }, + "node_modules/unplugin/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/update-browserslist-db": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", @@ -6859,10 +7467,17 @@ "punycode": "^2.1.0" } }, + "node_modules/urlpattern-polyfill": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/urlpattern-polyfill/-/urlpattern-polyfill-10.1.0.tgz", + "integrity": "sha512-IGjKp/o0NL3Bso1PymYURCJxMPNAf/ILOpendP9f5B6e1rTJgdgiOvgfoT8VxCAdY+Wisb9uhGaJJf3yZ2V9nw==", + "dev": true, + "license": "MIT" + }, "node_modules/use-sync-external-store": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.5.0.tgz", - "integrity": "sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz", + "integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==", "license": "MIT", "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" @@ -6890,6 +7505,20 @@ "dev": true, "license": "MIT" }, + "node_modules/uuid": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", + "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", + "dev": true, + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/validator": { "version": "13.15.15", "resolved": "https://registry.npmjs.org/validator/-/validator-13.15.15.tgz", @@ -6922,9 +7551,9 @@ } }, "node_modules/vite": { - "version": "7.1.7", - "resolved": "https://registry.npmjs.org/vite/-/vite-7.1.7.tgz", - "integrity": "sha512-VbA8ScMvAISJNJVbRDTJdCwqQoAareR/wutevKanhR2/1EkoXVZVkkORaYm/tNVCjP/UDTKtcw3bAkwOUdedmA==", + "version": "7.1.9", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.1.9.tgz", + "integrity": "sha512-4nVGliEpxmhCL8DslSAUdxlB6+SMrhB0a1v5ijlh1xB1nEPuy1mxaHxysVucLHuWryAxLWg6a5ei+U4TLn/rFg==", "license": "MIT", "dependencies": { "esbuild": "^0.25.0", @@ -7044,6 +7673,13 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/webpack-virtual-modules": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/webpack-virtual-modules/-/webpack-virtual-modules-0.6.2.tgz", + "integrity": "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==", + "dev": true, + "license": "MIT" + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -7154,14 +7790,11 @@ } }, "node_modules/yallist": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", - "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", "dev": true, - "license": "BlueOak-1.0.0", - "engines": { - "node": ">=18" - } + "license": "ISC" }, "node_modules/yocto-queue": { "version": "0.1.0", @@ -7175,6 +7808,29 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/zod": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.1.12.tgz", + "integrity": "sha512-JInaHOamG8pt5+Ey8kGmdcAcg3OL9reK8ltczgHTAwNhMys/6ThXHityHxVV2p3fkw/c+MAvBHFVYHFZDmjMCQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zod-validation-error": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-4.0.2.tgz", + "integrity": "sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "zod": "^3.25.0 || ^4.0.0" + } + }, "node_modules/zustand": { "version": "4.5.7", "resolved": "https://registry.npmjs.org/zustand/-/zustand-4.5.7.tgz", diff --git a/ui/package.json b/ui/package.json index 412b407c..1b11d65f 100644 --- a/ui/package.json +++ b/ui/package.json @@ -1,7 +1,7 @@ { "name": "kvm-ui", "private": true, - "version": "2025.10.01.1900", + "version": "2025.10.07.1700", "type": "module", "engines": { "node": "^22.15.0" @@ -11,12 +11,14 @@ "dev:ssl": "USE_SSL=true ./dev_device.sh", "dev:cloud": "vite dev --mode=cloud-development", "build": "npm run build:prod", - "build:device": "tsc && vite build --mode=device --emptyOutDir", - "build:staging": "tsc && vite build --mode=cloud-staging", - "build:prod": "tsc && vite build --mode=cloud-production", - "lint": "eslint './src/**/*.{ts,tsx}'", - "lint:fix": "eslint './src/**/*.{ts,tsx}' --fix", - "preview": "vite preview" + "build:device": "npm run paraglide && tsc && vite build --mode=device --emptyOutDir", + "build:staging": "npm run paraglide && tsc && vite build --mode=cloud-staging", + "build:prod": "npm run paraglide && tsc && vite build --mode=cloud-production", + "lint": "npm run paraglide && eslint './src/**/*.{ts,tsx}'", + "lint:fix": "npm run paraglide && eslint './src/**/*.{ts,tsx}' --fix", + "paraglide": "paraglide-js compile --project ./localization/jetKVM.UI.inlang --outdir ./localization/paraglide", + "validate": "inlang validate --project ./localization/jetKVM.UI.inlang", + "machine-translate": "inlang machine translate --project ./localization/jetKVM.UI.inlang" }, "dependencies": { "@headlessui/react": "^2.2.9", @@ -36,13 +38,13 @@ "framer-motion": "^12.23.22", "lodash.throttle": "^4.1.1", "mini-svg-data-uri": "^1.4.4", - "react": "^19.1.1", + "react": "^19.2.0", "react-animate-height": "^3.2.3", - "react-dom": "^19.1.1", + "react-dom": "^19.2.0", "react-hot-toast": "^2.6.0", "react-icons": "^5.5.0", "react-router": "^7.9.3", - "react-simple-keyboard": "^3.8.125", + "react-simple-keyboard": "^3.8.126", "react-use-websocket": "^4.13.0", "react-xtermjs": "^1.0.10", "recharts": "^3.2.1", @@ -54,32 +56,37 @@ "devDependencies": { "@eslint/compat": "^1.4.0", "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "^9.36.0", + "@eslint/js": "^9.37.0", + "@inlang/cli": "^3.0.12", + "@inlang/paraglide-js": "^2.4.0", + "@inlang/plugin-m-function-matcher": "^2.1.0", + "@inlang/plugin-message-format": "^4.0.0", + "@inlang/sdk": "^2.4.9", "@tailwindcss/forms": "^0.5.10", "@tailwindcss/postcss": "^4.1.14", "@tailwindcss/typography": "^0.5.19", "@tailwindcss/vite": "^4.1.14", - "@types/react": "^19.1.17", - "@types/react-dom": "^19.1.10", + "@types/react": "^19.2.2", + "@types/react-dom": "^19.2.1", "@types/semver": "^7.7.1", "@types/validator": "^13.15.3", - "@typescript-eslint/eslint-plugin": "^8.45.0", - "@typescript-eslint/parser": "^8.45.0", + "@typescript-eslint/eslint-plugin": "^8.46.0", + "@typescript-eslint/parser": "^8.46.0", "@vitejs/plugin-react-swc": "^4.1.0", "autoprefixer": "^10.4.21", - "eslint": "^9.36.0", + "eslint": "^9.37.0", "eslint-config-prettier": "^10.1.8", "eslint-plugin-import": "^2.32.0", "eslint-plugin-react": "^7.37.5", - "eslint-plugin-react-hooks": "^5.2.0", - "eslint-plugin-react-refresh": "^0.4.22", + "eslint-plugin-react-hooks": "^6.1.1", + "eslint-plugin-react-refresh": "^0.4.23", "globals": "^16.4.0", "postcss": "^8.5.6", "prettier": "^3.6.2", "prettier-plugin-tailwindcss": "^0.6.14", "tailwindcss": "^4.1.14", "typescript": "^5.9.3", - "vite": "^7.1.7", + "vite": "^7.1.9", "vite-tsconfig-paths": "^5.1.4" } } diff --git a/ui/src/components/ActionBar.tsx b/ui/src/components/ActionBar.tsx index 4f79d7ed..04164696 100644 --- a/ui/src/components/ActionBar.tsx +++ b/ui/src/components/ActionBar.tsx @@ -1,24 +1,25 @@ +import { Fragment, useCallback, useRef } from "react"; import { MdOutlineContentPasteGo } from "react-icons/md"; import { LuCable, LuHardDrive, LuMaximize, LuSettings, LuSignal } from "react-icons/lu"; import { FaKeyboard } from "react-icons/fa6"; import { Popover, PopoverButton, PopoverPanel } from "@headlessui/react"; -import { Fragment, useCallback, useRef } from "react"; import { CommandLineIcon } from "@heroicons/react/20/solid"; -import { Button } from "@components/Button"; import { useHidStore, useMountMediaStore, useSettingsStore, useUiStore, -} from "@/hooks/stores"; -import Container from "@components/Container"; +} from "@hooks/stores"; import { cx } from "@/cva.config"; -import PasteModal from "@/components/popovers/PasteModal"; -import WakeOnLanModal from "@/components/popovers/WakeOnLan/Index"; -import MountPopopover from "@/components/popovers/MountPopover"; -import ExtensionPopover from "@/components/popovers/ExtensionPopover"; -import { useDeviceUiNavigation } from "@/hooks/useAppNavigation"; +import { Button } from "@components/Button"; +import Container from "@components/Container"; +import PasteModal from "@components/popovers/PasteModal"; +import WakeOnLanModal from "@components/popovers/WakeOnLan/Index"; +import MountPopopover from "@components/popovers/MountPopover"; +import ExtensionPopover from "@components/popovers/ExtensionPopover"; +import { useDeviceUiNavigation } from "@hooks/useAppNavigation"; +import { m } from "@localizations/messages.js"; export default function Actionbar({ requestFullscreen, @@ -28,10 +29,7 @@ export default function Actionbar({ const { navigateTo } = useDeviceUiNavigation(); const { isVirtualKeyboardEnabled, setVirtualKeyboardEnabled } = useHidStore(); const { setDisableVideoFocusTrap, terminalType, setTerminalType, toggleSidebarView } = useUiStore(); - - const remoteVirtualMediaState = useMountMediaStore( - state => state.remoteVirtualMediaState, - ); + const { remoteVirtualMediaState } = useMountMediaStore(); const { developerMode } = useSettingsStore(); // This is the only way to get a reliable state change for the popover @@ -64,7 +62,7 @@ export default function Actionbar({ diff --git a/ui/src/components/InfoBar.tsx b/ui/src/components/InfoBar.tsx index ce444d85..6d5db383 100644 --- a/ui/src/components/InfoBar.tsx +++ b/ui/src/components/InfoBar.tsx @@ -1,6 +1,5 @@ import { useEffect, useMemo } from "react"; -import { cx } from "@/cva.config"; import { useHidStore, useMouseStore, @@ -8,9 +7,11 @@ import { useSettingsStore, useVideoStore, VideoState -} from "@/hooks/stores"; +} from "@hooks/stores"; +import { useHidRpc } from "@hooks/useHidRpc"; import { keys, modifiers } from "@/keyboardMappings"; -import { useHidRpc } from "@/hooks/useHidRpc"; +import { cx } from "@/cva.config"; +import { m } from "@localizations/messages.js"; export default function InfoBar() { const { keysDownState } = useHidStore(); @@ -41,13 +42,16 @@ export default function InfoBar() { const { hdmiState } = useVideoStore(); const displayKeys = useMemo(() => { - if (!showPressedKeys) - return ""; + if (!showPressedKeys) return ""; const activeModifierMask = keysDownState.modifier || 0; const keysDown = keysDownState.keys || []; - const modifierNames = Object.entries(modifiers).filter(([_, mask]) => (activeModifierMask & mask) !== 0).map(([name, _]) => name); - const keyNames = Object.entries(keys).filter(([_, value]) => keysDown.includes(value)).map(([name, _]) => name); + const modifierNames = Object.entries(modifiers) + .filter(([_, mask]) => (activeModifierMask & mask) !== 0) + .map(([name]) => name); + const keyNames = Object.entries(keys) + .filter(([_, value]) => keysDown.includes(value)) + .map(([name]) => name); return [...modifierNames, ...keyNames].join(", "); }, [keysDownState, showPressedKeys]); @@ -59,76 +63,75 @@ export default function InfoBar() {
{debugMode ? (
- Resolution:{" "} + {m.info_resolution()}{" "} {videoSize}
) : null} {debugMode ? (
- Video Size: + {m.info_video_size()} {videoClientSize}
) : null} {(debugMode && mouseMode == "absolute") ? (
- Pointer: - - {mouseX},{mouseY} - + {m.info_pointer()} + {mouseX},{mouseY}
) : null} {(debugMode && mouseMode == "relative") ? (
- Last Move: + {m.info_last_move()} - {mouseMove ? - `${mouseMove.x},${mouseMove.y} ${mouseMove.buttons ? `(${mouseMove.buttons})` : ""}` : - "N/A"} + {mouseMove ? `${mouseMove.x},${mouseMove.y} ${mouseMove.buttons ? `(${mouseMove.buttons})` : ""}` : "N/A"}
) : null} {debugMode && (
- USB State: + {m.info_usb_state()} {usbState}
)} + {debugMode && (
- HDMI State: + {m.info_hdmi_state()} {hdmiState}
)} + {debugMode && (
- HidRPC State: + {m.info_hidrpc_state()} {rpcHidStatus}
)} + {isPasteInProgress && (
- Paste Mode: - Enabled + {m.info_paste_mode()} + {m.info_paste_enabled()}
)} + {showPressedKeys && (
- Keys: -

- {displayKeys} -

+ {m.info_keys()} +

{displayKeys}

)}
+
{isTurnServerInUse && (
- Relayed by Cloudflare + {m.info_relayed_by_cloudflare()}
)} @@ -140,8 +143,9 @@ export default function InfoBar() { : "text-slate-800/20 dark:text-slate-300/20", )} > - Caps Lock + {m.info_caps_lock()}
+
- Num Lock + {m.info_num_lock()}
+
- Scroll Lock + {m.info_scroll_lock()}
+ {keyboardLedState.compose ? ( -
- Compose -
+
{m.info_compose()}
) : null} + {keyboardLedState.kana ? ( -
- Kana -
+
{m.info_kana()}
) : null} + {keyboardLedState.shift ? ( -
- Shift -
+
{m.info_shift()}
) : null} diff --git a/ui/src/components/Ipv6NetworkCard.tsx b/ui/src/components/Ipv6NetworkCard.tsx index 0cfacc6d..6d561510 100644 --- a/ui/src/components/Ipv6NetworkCard.tsx +++ b/ui/src/components/Ipv6NetworkCard.tsx @@ -1,7 +1,7 @@ -import { NetworkState } from "../hooks/stores"; -import { LifeTimeLabel } from "../routes/devices.$id.settings.network"; - -import { GridCard } from "./Card"; +import { NetworkState } from "@hooks/stores"; +import { GridCard } from "@components/Card"; +import { LifeTimeLabel } from "@routes/devices.$id.settings.network"; +import { m } from "@localizations/messages.js"; export default function Ipv6NetworkCard({ networkState, @@ -13,14 +13,14 @@ export default function Ipv6NetworkCard({

- IPv6 Information + {m.ipv6_information()}

{networkState?.ipv6_link_local && (
- Link-local + {m.ipv6_link_local()} {networkState?.ipv6_link_local} @@ -42,7 +42,7 @@ export default function Ipv6NetworkCard({
- Address + {m.ipv6_address_label()} {addr.address}
@@ -50,12 +50,12 @@ export default function Ipv6NetworkCard({ {addr.valid_lifetime && (
- Valid Lifetime + {m.ipv6_valid_lifetime()} {addr.valid_lifetime === "" ? ( - N/A + {m.not_available()} ) : ( @@ -66,12 +66,12 @@ export default function Ipv6NetworkCard({ {addr.preferred_lifetime && (
- Preferred Lifetime + {m.ipv6_preferred_lifetime()} {addr.preferred_lifetime === "" ? ( - N/A + {m.not_available()} ) : ( diff --git a/ui/src/components/JigglerSetting.tsx b/ui/src/components/JigglerSetting.tsx index fc0f50dd..6578906e 100644 --- a/ui/src/components/JigglerSetting.tsx +++ b/ui/src/components/JigglerSetting.tsx @@ -1,11 +1,11 @@ import { useEffect, useMemo, useState } from "react"; import { LuExternalLink } from "react-icons/lu"; +import { JsonRpcResponse, useJsonRpc } from "@hooks/useJsonRpc"; import { Button, LinkButton } from "@components/Button"; -import { JsonRpcResponse, useJsonRpc } from "@/hooks/useJsonRpc"; - -import { InputFieldWithLabel } from "./InputField"; -import { SelectMenuBasic } from "./SelectMenuBasic"; +import { InputFieldWithLabel } from "@components/InputField"; +import { SelectMenuBasic } from "@components/SelectMenuBasic"; +import { m } from "@localizations/messages.js"; export interface JigglerConfig { inactivity_limit_seconds: number; @@ -51,7 +51,7 @@ export function JigglerSetting({ const exampleConfigs = [ { - name: "Business Hours 9-17", + name: m.jiggler_example_business_hours_late(), config: { inactivity_limit_seconds: 60, jitter_percentage: 25, @@ -60,7 +60,7 @@ export function JigglerSetting({ }, }, { - name: "Business Hours 8-17", + name: m.jiggler_example_business_hours_early(), config: { inactivity_limit_seconds: 60, jitter_percentage: 25, @@ -69,13 +69,10 @@ export function JigglerSetting({ }, }, ]; - return (
-

- Examples -

+

{m.jiggler_examples_label()}

{exampleConfigs.map((example, index) => (
@@ -100,8 +97,8 @@ export function JigglerSetting({ @@ -114,8 +111,8 @@ export function JigglerSetting({ %} value={jigglerConfigState.jitter_percentage} @@ -149,8 +146,8 @@ export function JigglerSetting({ @@ -167,7 +164,7 @@ export function JigglerSetting({
diff --git a/ui/src/components/KvmCard.tsx b/ui/src/components/KvmCard.tsx index ab34976b..f4e3bf4d 100644 --- a/ui/src/components/KvmCard.tsx +++ b/ui/src/components/KvmCard.tsx @@ -1,10 +1,11 @@ +import { Link } from "react-router"; import { MdConnectWithoutContact } from "react-icons/md"; import { Menu, MenuButton, MenuItem, MenuItems } from "@headlessui/react"; -import { Link } from "react-router"; import { LuEllipsisVertical } from "react-icons/lu"; import Card from "@components/Card"; import { Button, LinkButton } from "@components/Button"; +import { m } from "@localizations/messages.js"; function getRelativeTimeString(date: Date | number, lang = navigator.language): string { // Allow dates or times to be passed @@ -62,16 +63,16 @@ export default function KvmCard({ {online ? (
-
Online
+
{m.online()}
) : (
{lastSeen ? ( - <>Last online {getRelativeTimeString(lastSeen)} + <>{m.last_online({ time: getRelativeTimeString(lastSeen) })} ) : ( - <>Never seen online + <>{m.never_seen_online()} )}
@@ -85,7 +86,7 @@ export default function KvmCard({ )} diff --git a/ui/src/components/MacroBar.tsx b/ui/src/components/MacroBar.tsx index 0ba8cf4f..7536ad2a 100644 --- a/ui/src/components/MacroBar.tsx +++ b/ui/src/components/MacroBar.tsx @@ -1,11 +1,11 @@ import { useEffect } from "react"; import { LuCommand } from "react-icons/lu"; +import { useMacrosStore } from "@hooks/stores"; +import useKeyboard from "@hooks/useKeyboard"; +import { useJsonRpc } from "@hooks/useJsonRpc"; import { Button } from "@components/Button"; import Container from "@components/Container"; -import { useMacrosStore } from "@/hooks/stores"; -import useKeyboard from "@/hooks/useKeyboard"; -import { useJsonRpc } from "@/hooks/useJsonRpc"; export default function MacroBar() { const { macros, initialized, loadMacros, setSendFn } = useMacrosStore(); diff --git a/ui/src/components/MacroForm.tsx b/ui/src/components/MacroForm.tsx index 1aafe9c9..f9517164 100644 --- a/ui/src/components/MacroForm.tsx +++ b/ui/src/components/MacroForm.tsx @@ -1,18 +1,19 @@ import { useState } from "react"; import { LuPlus } from "react-icons/lu"; -import { Button } from "@/components/Button"; -import FieldLabel from "@/components/FieldLabel"; -import Fieldset from "@/components/Fieldset"; -import { InputFieldWithLabel, FieldError } from "@/components/InputField"; -import { MacroStepCard } from "@/components/MacroStepCard"; +import { KeySequence } from "@hooks/stores"; +import useKeyboardLayout from "@hooks/useKeyboardLayout"; +import { Button } from "@components/Button"; +import FieldLabel from "@components/FieldLabel"; +import Fieldset from "@components/Fieldset"; +import { InputFieldWithLabel, FieldError } from "@components/InputField"; +import { MacroStepCard } from "@components/MacroStepCard"; import { DEFAULT_DELAY, MAX_STEPS_PER_MACRO, MAX_KEYS_PER_STEP, } from "@/constants/macros"; -import { KeySequence } from "@/hooks/stores"; -import useKeyboardLayout from "@/hooks/useKeyboardLayout"; +import { m } from "@localizations/messages.js"; interface ValidationErrors { name?: string; @@ -31,7 +32,6 @@ interface MacroFormProps { onSubmit: (macro: Partial) => Promise; onCancel: () => void; isSubmitting?: boolean; - submitText?: string; } export function MacroForm({ @@ -39,7 +39,6 @@ export function MacroForm({ onSubmit, onCancel, isSubmitting = false, - submitText = "Save Macro", }: MacroFormProps) { const [macro, setMacro] = useState>(initialData); const [keyQueries, setKeyQueries] = useState>({}); @@ -57,13 +56,13 @@ export function MacroForm({ // Name validation if (!macro.name?.trim()) { - newErrors.name = "Name is required"; + newErrors.name = m.macro_name_required(); } else if (macro.name.trim().length > 50) { - newErrors.name = "Name must be less than 50 characters"; + newErrors.name = m.macro_name_too_long(); } if (!macro.steps?.length) { - newErrors.steps = { 0: { keys: "At least one step is required" } }; + newErrors.steps = { 0: { keys: m.macro_at_least_one_step_required() } }; } else { const hasKeyOrModifier = macro.steps.some( step => (step.keys?.length || 0) > 0 || (step.modifiers?.length || 0) > 0, @@ -71,7 +70,7 @@ export function MacroForm({ if (!hasKeyOrModifier) { newErrors.steps = { - 0: { keys: "At least one step must have keys or modifiers" }, + 0: { keys: m.macro_at_least_one_step_keys_or_modifiers() }, }; } } @@ -82,7 +81,7 @@ export function MacroForm({ const handleSubmit = async () => { if (!validateForm()) { - showTemporaryError("Please fix the validation errors"); + showTemporaryError(m.macro_please_fix_validation_errors()); return; } @@ -92,7 +91,7 @@ export function MacroForm({ if (error instanceof Error) { showTemporaryError(error.message); } else { - showTemporaryError("An error occurred while saving"); + showTemporaryError(m.macro_save_error()); } } }; @@ -114,7 +113,7 @@ export function MacroForm({ ? newSteps[stepIndex].keys : []; if (keysArray.length >= MAX_KEYS_PER_STEP) { - showTemporaryError(`Maximum of ${MAX_KEYS_PER_STEP} keys per step allowed`); + showTemporaryError(m.macro_max_steps_error({max: MAX_KEYS_PER_STEP})); return; } newSteps[stepIndex].keys = [...keysArray, option.value]; @@ -178,8 +177,8 @@ export function MacroForm({
{ @@ -197,12 +196,12 @@ export function MacroForm({
- {macro.steps?.length || 0}/{MAX_STEPS_PER_MACRO} steps + {m.macro_step_count({steps: macro.steps?.length || 0, max: MAX_STEPS_PER_MACRO})}
{errors.steps && errors.steps[0]?.keys && ( @@ -220,10 +219,10 @@ export function MacroForm({ onDelete={ macro.steps && macro.steps.length > 1 ? () => { - const newSteps = [...(macro.steps || [])]; - newSteps.splice(stepIndex, 1); - setMacro(prev => ({ ...prev, steps: newSteps })); - } + const newSteps = [...(macro.steps || [])]; + newSteps.splice(stepIndex, 1); + setMacro(prev => ({ ...prev, steps: newSteps })); + } : undefined } onMoveUp={() => handleStepMove(stepIndex, "up")} @@ -248,12 +247,10 @@ export function MacroForm({ theme="light" fullWidth LeadingIcon={LuPlus} - text={`Add Step ${isMaxStepsReached ? `(${MAX_STEPS_PER_MACRO} max)` : ""}`} + text={m.macro_add_step({ maxed_out: isMaxStepsReached ? m.macro_max_steps_reached({ max: MAX_STEPS_PER_MACRO} ) : ""})} onClick={() => { if (isMaxStepsReached) { - showTemporaryError( - `You can only add a maximum of ${MAX_STEPS_PER_MACRO} steps per macro.`, - ); + showTemporaryError(m.macro_max_steps_error({max: MAX_STEPS_PER_MACRO})); return; } @@ -280,11 +277,11 @@ export function MacroForm({
diff --git a/ui/src/components/MacroStepCard.tsx b/ui/src/components/MacroStepCard.tsx index c795520d..337e74a3 100644 --- a/ui/src/components/MacroStepCard.tsx +++ b/ui/src/components/MacroStepCard.tsx @@ -1,14 +1,15 @@ import { useMemo } from "react"; import { LuArrowUp, LuArrowDown, LuX, LuTrash2 } from "react-icons/lu"; -import { Button } from "@/components/Button"; -import { Combobox } from "@/components/Combobox"; -import { SelectMenuBasic } from "@/components/SelectMenuBasic"; -import Card from "@/components/Card"; -import FieldLabel from "@/components/FieldLabel"; +import { Button } from "@components/Button"; +import { Combobox } from "@components/Combobox"; +import Card from "@components/Card"; +import FieldLabel from "@components/FieldLabel"; +import { SelectMenuBasic } from "@components/SelectMenuBasic"; import { MAX_KEYS_PER_STEP, DEFAULT_DELAY } from "@/constants/macros"; import { KeyboardLayout } from "@/keyboardLayouts"; import { keys, modifiers } from "@/keyboardMappings"; +import { m } from "@localizations/messages.js"; // Filter out modifier keys since they're handled in the modifiers section const modifierKeyPrefixes = ['Alt', 'Control', 'Shift', 'Meta']; @@ -25,6 +26,7 @@ const groupedModifiers: Record = { Meta: modifierOptions.filter(mod => mod.value.startsWith('Meta')), }; +// not going to localize these since they're short time intervals const basePresetDelays = [ { value: "50", label: "50ms" }, { value: "100", label: "100ms" }, @@ -132,12 +134,12 @@ export function MacroStepCard({ LeadingIcon={LuArrowDown} />
- {onDelete && ( + {onDelete && (
); } diff --git a/ui/src/components/Terminal.tsx b/ui/src/components/Terminal.tsx index ba3e667c..3ba09bfa 100644 --- a/ui/src/components/Terminal.tsx +++ b/ui/src/components/Terminal.tsx @@ -1,17 +1,17 @@ +import { useEffect, useMemo } from "react"; import "react-simple-keyboard/build/css/index.css"; import { ChevronDownIcon } from "@heroicons/react/16/solid"; -import { useEffect, useMemo } from "react"; import { useXTerm } from "react-xtermjs"; import { FitAddon } from "@xterm/addon-fit"; import { WebLinksAddon } from "@xterm/addon-web-links"; import { WebglAddon } from "@xterm/addon-webgl"; import { Unicode11Addon } from "@xterm/addon-unicode11"; import { ClipboardAddon } from "@xterm/addon-clipboard"; - import { cx } from "@/cva.config"; -import { AvailableTerminalTypes, useUiStore } from "@/hooks/stores"; -import { Button } from "./Button"; +import { AvailableTerminalTypes, useUiStore } from "@hooks/stores"; +import { Button } from "@components/Button"; +import { m } from "@localizations/messages.js"; const isWebGl2Supported = !!document.createElement("canvas").getContext("webgl2"); @@ -191,7 +191,7 @@ function Terminal({
diff --git a/ui/src/components/UsbInfoSetting.tsx b/ui/src/components/UsbInfoSetting.tsx index 1b4f3114..1a564e03 100644 --- a/ui/src/components/UsbInfoSetting.tsx +++ b/ui/src/components/UsbInfoSetting.tsx @@ -1,15 +1,14 @@ import { useMemo , useCallback , useEffect, useState } from "react"; +import { UsbConfigState } from "@hooks/stores"; +import { JsonRpcResponse, useJsonRpc } from "@hooks/useJsonRpc"; import { Button } from "@components/Button"; +import Fieldset from "@components/Fieldset"; +import { InputFieldWithLabel } from "@components/InputField"; +import { SelectMenuBasic } from "@components/SelectMenuBasic"; import { SettingsItem } from "@components/SettingsItem"; - -import { UsbConfigState } from "../hooks/stores"; -import { JsonRpcResponse, useJsonRpc } from "../hooks/useJsonRpc"; -import notifications from "../notifications"; - -import { InputFieldWithLabel } from "./InputField"; -import { SelectMenuBasic } from "./SelectMenuBasic"; -import Fieldset from "./Fieldset"; +import notifications from "@/notifications"; +import { m } from "@localizations/messages.js"; const generatedSerialNumber = [generateNumber(1, 9), generateHex(7, 7), 0, 1].join("&"); @@ -31,21 +30,22 @@ export interface USBConfig { product: string; } + const usbConfigs = [ { - label: "JetKVM Default", + label: m.usb_config_default(), value: "USB Emulation Device", }, { - label: "Logitech Universal Adapter", + label: m.usb_config_logitech(), value: "Logitech USB Input Device", }, { - label: "Microsoft Wireless MultiMedia Keyboard", + label: m.usb_config_microsoft(), value: "Wireless MultiMedia Keyboard", }, { - label: "Dell Multimedia Pro Keyboard", + label: m.usb_config_dell(), value: "Multimedia Pro Keyboard", }, ]; @@ -94,10 +94,10 @@ export function UsbInfoSetting() { const syncUsbConfigProduct = useCallback(() => { send("getUsbConfig", {}, (resp: JsonRpcResponse) => { - if ("error" in resp) { + if ("error" in resp) { console.error("Failed to load USB Config:", resp.error); notifications.error( - `Failed to load USB Config: ${resp.error.data || "Unknown error"}`, + m.usb_config_failed_load({ error: String(resp.error.data || "Unknown error") }), ); } else { const usbConfigState = resp.result as UsbConfigState; @@ -116,7 +116,7 @@ export function UsbInfoSetting() { send("setUsbConfig", { usbConfig }, async (resp: JsonRpcResponse) => { if ("error" in resp) { notifications.error( - `Failed to set usb config: ${resp.error.data || "Unknown error"}`, + m.usb_config_failed_set({ error: String(resp.error.data || "Unknown error") }), ); setLoading(false); return; @@ -126,7 +126,7 @@ export function UsbInfoSetting() { await new Promise(resolve => setTimeout(resolve, 2000)); setLoading(false); notifications.success( - `USB Config set to ${usbConfig.manufacturer} ${usbConfig.product}`, + m.usb_config_set_success({ manufacturer: usbConfig.manufacturer, product: usbConfig.product }), ); syncUsbConfigProduct(); @@ -152,8 +152,8 @@ export function UsbInfoSetting() {
{usbConfigProduct === "custom" && ( @@ -246,38 +246,38 @@ function USBConfigDialog({
handleUsbVendorIdChange(e.target.value)} /> handleUsbProductIdChange(e.target.value)} /> handleUsbSerialChange(e.target.value)} /> handleUsbManufacturer(e.target.value)} /> handleUsbProduct(e.target.value)} /> @@ -287,13 +287,13 @@ function USBConfigDialog({ loading={loading} size="SM" theme="primary" - text="Update USB Identifiers" + text={m.usb_config_update_identifiers()} onClick={() => onSetUsbConfig(usbConfigState)} />
diff --git a/ui/src/components/VideoOverlay.tsx b/ui/src/components/VideoOverlay.tsx index 1c59e788..1194e1e9 100644 --- a/ui/src/components/VideoOverlay.tsx +++ b/ui/src/components/VideoOverlay.tsx @@ -5,6 +5,7 @@ import { motion, AnimatePresence } from "framer-motion"; import { LuPlay } from "react-icons/lu"; import { BsMouseFill } from "react-icons/bs"; +import { m } from "@localizations/messages.js"; import { Button, LinkButton } from "@components/Button"; import LoadingSpinner from "@components/LoadingSpinner"; import Card, { GridCard } from "@components/Card"; @@ -46,7 +47,7 @@ export function LoadingVideoOverlay({ show }: LoadingOverlayProps) {

- Loading video stream... + {m.video_overlay_loading_stream()}

@@ -118,26 +119,26 @@ export function ConnectionFailedOverlay({
-

Connection Issue Detected

+

{m.video_overlay_connection_issue_title()}

    -
  • Verify that the device is powered on and properly connected
  • -
  • Check all cable connections for any loose or damaged wires
  • -
  • Ensure your network connection is stable and active
  • -
  • Try restarting both the device and your computer
  • +
  • {m.video_overlay_conn_verify_power()}
  • +
  • {m.video_overlay_conn_check_cables()}
  • +
  • {m.video_overlay_conn_ensure_network()}
  • +
  • {m.video_overlay_conn_restart()}

- Virtual Keyboard + m.virtual_keyboard_header()

@@ -274,7 +272,7 @@ function KeyboardWrapper() {
{error?.message && (

- {error?.message} + {m.deregister_error({ status: error.message })}

)} diff --git a/ui/src/routes/devices.$id.mount.tsx b/ui/src/routes/devices.$id.mount.tsx index 4672ef99..5c6afa46 100644 --- a/ui/src/routes/devices.$id.mount.tsx +++ b/ui/src/routes/devices.$id.mount.tsx @@ -1,4 +1,5 @@ import { useCallback, useEffect, useMemo, useRef, useState } from "react"; +import { useNavigate } from "react-router"; import { LuLink, LuRadioReceiver, @@ -7,28 +8,28 @@ import { } from "react-icons/lu"; import { PlusCircleIcon, ExclamationTriangleIcon } from "@heroicons/react/20/solid"; import { TrashIcon } from "@heroicons/react/16/solid"; -import { useNavigate } from "react-router"; +import DebianIcon from "@assets/debian-icon.png"; +import UbuntuIcon from "@assets/ubuntu-icon.png"; +import FedoraIcon from "@assets/fedora-icon.png"; +import OpenSUSEIcon from "@assets/opensuse-icon.png"; +import ArchIcon from "@assets/arch-icon.png"; +import NetBootIcon from "@assets/netboot-icon.svg"; +import LogoBlueIcon from "@assets/logo-blue.svg"; +import LogoWhiteIcon from "@assets/logo-white.svg"; +import { cx } from "@/cva.config"; -import Card, { GridCard } from "@/components/Card"; -import { Button } from "@components/Button"; -import LogoBlueIcon from "@/assets/logo-blue.svg"; -import LogoWhiteIcon from "@/assets/logo-white.svg"; -import { formatters } from "@/utils"; +import { JsonRpcResponse, useJsonRpc } from "@hooks/useJsonRpc"; import AutoHeight from "@components/AutoHeight"; +import { Button } from "@components/Button"; +import Card, { GridCard } from "@components/Card"; +import Fieldset from "@components/Fieldset"; import { InputFieldWithLabel } from "@/components/InputField"; -import DebianIcon from "@/assets/debian-icon.png"; -import UbuntuIcon from "@/assets/ubuntu-icon.png"; -import FedoraIcon from "@/assets/fedora-icon.png"; -import OpenSUSEIcon from "@/assets/opensuse-icon.png"; -import ArchIcon from "@/assets/arch-icon.png"; -import NetBootIcon from "@/assets/netboot-icon.svg"; -import Fieldset from "@/components/Fieldset"; +import { formatters } from "@/utils"; import { DEVICE_API } from "@/ui.config"; +import { isOnDevice } from "@/main"; +import notifications from "@/notifications"; +import { m } from "@localizations/messages.js"; -import { JsonRpcResponse, useJsonRpc } from "../hooks/useJsonRpc"; -import notifications from "../notifications"; -import { isOnDevice } from "../main"; -import { cx } from "../cva.config"; import { MountMediaState, RemoteVirtualMediaState, @@ -145,12 +146,12 @@ export function Dialog({ onClose }: { onClose: () => void }) {
JetKVM Logo JetKVM Logo {modalView === "mode" && ( @@ -238,26 +239,26 @@ function ModeSelectionView({

- Virtual Media Source + {m.mount_virtual_media_source()}

- Choose how you want to mount your virtual media + {m.mount_virtual_media_source_description()}
{[ { - label: "URL Mount", + label: m.mount_url_mount(), value: "url", - description: "Mount files from any public web address", + description: m.mount_url_description(), icon: LuLink, - tag: "Experimental", + tag: m.experimental(), disabled: false, }, { - label: "JetKVM Storage Mount", + label: m.mount_jetkvm_storage(), value: "device", - description: "Mount previously uploaded files from the JetKVM storage", + description: m.mount_jetkvm_storage_description(), icon: LuRadioReceiver, tag: null, disabled: false, @@ -332,7 +333,7 @@ function ModeSelectionView({ onClick={() => { setModalView(selectedMode); }} - text="Continue" + text={m.continue()} />
@@ -410,8 +411,8 @@ function UrlView({ return (
handleUrlChange(e.target.value)} @@ -440,12 +441,12 @@ function UrlView({
-
@@ -553,7 +554,7 @@ function DeviceFileView({ const syncStorage = useCallback(() => { send("listStorageFiles", {}, (resp: JsonRpcResponse) => { if ("error" in resp) { - notifications.error(`Error listing storage files: ${resp.error}`); + notifications.error(m.mount_error_list_storage({ error: resp.error })); return; } const { files } = resp.result as StorageFiles; @@ -568,7 +569,7 @@ function DeviceFileView({ send("getStorageSpace", {}, (resp: JsonRpcResponse) => { if ("error" in resp) { - notifications.error(`Error getting storage space: ${resp.error}`); + notifications.error(m.mount_error_get_storage_space({ error: resp.error })); return; } @@ -597,7 +598,7 @@ function DeviceFileView({ console.log("Deleting file:", file); send("deleteStorageFile", { filename: file.name }, (resp: JsonRpcResponse) => { if ("error" in resp) { - notifications.error(`Error deleting file: ${resp.error}`); + notifications.error(m.mount_error_delete_file({ error: resp.error })); return; } @@ -630,8 +631,8 @@ function DeviceFileView({ return (

- No images available + {m.mount_no_images_title()}

- Upload an image to start virtual media mounting. + {m.mount_no_images_description()}

@@ -677,9 +678,7 @@ function DeviceFileView({ const selectedFile = onStorageFiles.find(f => f.name === file.name); if (!selectedFile) return; if ( - window.confirm( - "Are you sure you want to delete " + selectedFile.name + "?", - ) + window.confirm(m.mount_confirm_delete({ name: selectedFile.name })) ) { handleDeleteFile(selectedFile); } @@ -692,24 +691,24 @@ function DeviceFileView({ {onStorageFiles.length > filesPerPage && (

- Showing {indexOfFirstFile + 1} to{" "} - - {Math.min(indexOfLastFile, onStorageFiles.length)} - {" "} - of {onStorageFiles.length} results + {m.mount_button_showing_results({ + from: indexOfFirstFile + 1, + to: Math.min(indexOfLastFile, onStorageFiles.length), + total: onStorageFiles.length + })}

@@ -806,7 +805,7 @@ function DeviceFileView({ size="MD" theme="light" fullWidth - text="Upload a new image" + text={m.mount_button_upload_new_image()} onClick={() => onNewImageClick()} />
@@ -862,7 +861,7 @@ function UploadFileView({ if (!rtcDataChannel) { console.error("Failed to create data channel for file upload"); - notifications.error("Failed to create data channel for file upload"); + notifications.error(m.mount_upload_failed_datachannel()); setUploadState("idle"); console.log("Upload state set to 'idle'"); @@ -952,7 +951,7 @@ function UploadFileView({ rtcDataChannel.onerror = error => { console.error("RTC Data channel error:", error); - notifications.error(`Upload failed: ${error}`); + notifications.error(m.mount_upload_failed_rtc({ error: error })); setUploadState("idle"); console.log("Upload state set to 'idle'"); }; @@ -1037,7 +1036,7 @@ function UploadFileView({ file.name !== incompleteFileName.replace(".incomplete", "") ) { setFileError( - `Please select the file "${incompleteFileName.replace(".incomplete", "")}" to continue the upload.`, + m.mount_please_select_file({ name: incompleteFileName.replace(".incomplete", "") }), ); return; } @@ -1080,11 +1079,11 @@ function UploadFileView({ return (

{incompleteFileName - ? `Click to select "${incompleteFileName.replace(".incomplete", "")}"` - : "Click to select a file"} + ? m.mount_click_to_select_incomplete({ name: incompleteFileName.replace(".incomplete", "") }) + : m.mount_click_to_select_file()}

- Supported formats: ISO, IMG + {m.mount_supported_formats()}

)} @@ -1140,7 +1139,7 @@ function UploadFileView({

- Uploading {formatters.truncateMiddle(uploadedFileName, 30)} + {m.mount_uploading_with_name({ name: formatters.truncateMiddle(uploadedFileName, 30) })}

{formatters.bytes(uploadedFileSize || 0)} @@ -1153,11 +1152,11 @@ function UploadFileView({ >

- Uploading... + {m.mount_uploading()} {uploadSpeed !== null ? `${formatters.bytes(uploadSpeed)}/s` - : "Calculating..."} + : m.mount_calculating()}
@@ -1174,11 +1173,10 @@ function UploadFileView({

- Upload successful + {m.mount_upload_successful()}

- {formatters.truncateMiddle(uploadedFileName, 40)} has been - uploaded + {m.mount_uploaded_has_been_uploaded({ name: formatters.truncateMiddle(uploadedFileName, 40) })}

)} @@ -1205,7 +1203,7 @@ function UploadFileView({ className="mt-2 animate-fadeIn truncate text-sm text-red-600 dark:text-red-400 opacity-0" style={{ animationDuration: "0.7s" }} > - Error: {uploadError} + {m.mount_upload_error({ error: String(uploadError) })}
)} @@ -1221,7 +1219,7 @@ function UploadFileView({
); @@ -1341,7 +1339,7 @@ function PreUploadedImageItem({ size="XS" theme="light" LeadingIcon={TrashIcon} - text="Delete" + text={m.delete()} onClick={e => { e.stopPropagation(); onDelete(); @@ -1362,7 +1360,7 @@ function PreUploadedImageItem({
diff --git a/ui/src/routes/devices.$id.other-session.tsx b/ui/src/routes/devices.$id.other-session.tsx index 8a767d51..b612e9a6 100644 --- a/ui/src/routes/devices.$id.other-session.tsx +++ b/ui/src/routes/devices.$id.other-session.tsx @@ -1,9 +1,10 @@ import { useNavigate, useOutletContext } from "react-router"; -import { GridCard } from "@/components/Card"; import { Button } from "@components/Button"; -import LogoBlue from "@/assets/logo-blue.svg"; -import LogoWhite from "@/assets/logo-white.svg"; +import { GridCard } from "@components/Card"; +import LogoBlue from "@assets/logo-blue.svg"; +import LogoWhite from "@assets/logo-white.svg"; +import { m } from "@localizations/messages"; interface ContextType { setupPeerConnection: () => Promise; @@ -30,14 +31,13 @@ export default function OtherSessionRoute() {

- Another Active Session Detected + {m.other_session_detected()}

- Only one active session is supported at a time. Would you like to take over - this session? + {m.other_session_take_over()}

-
diff --git a/ui/src/routes/devices.$id.rename.tsx b/ui/src/routes/devices.$id.rename.tsx index 39f06bcf..2032e7d4 100644 --- a/ui/src/routes/devices.$id.rename.tsx +++ b/ui/src/routes/devices.$id.rename.tsx @@ -2,17 +2,17 @@ import { Form, redirect, useActionData, useLoaderData } from "react-router"; import type { ActionFunction, ActionFunctionArgs, LoaderFunction, LoaderFunctionArgs } from "react-router"; import { ChevronLeftIcon } from "@heroicons/react/16/solid"; +import { User } from "@hooks/stores"; import { Button, LinkButton } from "@components/Button"; import Card from "@components/Card"; import { CardHeader } from "@components/CardHeader"; -import { InputFieldWithLabel } from "@components/InputField"; import DashboardNavbar from "@components/Header"; -import { User } from "@/hooks/stores"; -import { checkAuth } from "@/main"; import Fieldset from "@components/Fieldset"; +import { InputFieldWithLabel } from "@components/InputField"; +import { checkAuth } from "@/main"; import { CLOUD_API } from "@/ui.config"; - -import api from "../api"; +import api from "@/api"; +import { m } from "@localizations/messages"; interface LoaderData { device: { id: string; name: string; user: { googleId: string } }; @@ -24,7 +24,7 @@ const action: ActionFunction = async ({ params, request }: ActionFunctionArgs) = const { name } = Object.fromEntries(await request.formData()); if (!name || name === "") { - return { message: "Please specify a name" }; + return { message: m.rename_device_no_name() }; } try { @@ -32,11 +32,11 @@ const action: ActionFunction = async ({ params, request }: ActionFunctionArgs) = name, }); if (!res.ok) { - return { message: "There was an error renaming your device. Please try again." }; + return { message: m.rename_device_error({ error: res.statusText }) }; } } catch (e) { console.error(e); - return { message: "There was an error renaming your device. Please try again." }; + return { message: m.rename_device_error({ error: String(e) }) }; } return redirect("/devices"); @@ -86,24 +86,24 @@ export default function DeviceIdRename() { size="SM" theme="blank" LeadingIcon={ChevronLeftIcon} - text="Back to Devices" + text={m.back_to_devices()} to="/devices" />
diff --git a/ui/src/routes/devices.$id.setup.tsx b/ui/src/routes/devices.$id.setup.tsx index 2fd65f50..26e47115 100644 --- a/ui/src/routes/devices.$id.setup.tsx +++ b/ui/src/routes/devices.$id.setup.tsx @@ -9,9 +9,9 @@ import Fieldset from "@components/Fieldset"; import { InputFieldWithLabel } from "@components/InputField"; import { Button } from "@components/Button"; import { checkAuth } from "@/main"; +import api from "@/api"; import { CLOUD_API } from "@/ui.config"; - -import api from "../api"; +import { m } from "@localizations/messages.js"; const loader: LoaderFunction = async ({ params }: LoaderFunctionArgs) => { await checkAuth(); @@ -31,12 +31,22 @@ const loader: LoaderFunction = async ({ params }: LoaderFunctionArgs) => { const action: ActionFunction = async ({ request }: ActionFunctionArgs) => { // Handle form submission const { name, id, returnTo } = Object.fromEntries(await request.formData()); - const res = await api.PUT(`${CLOUD_API}/devices/${id}`, { name }); + + if (!name || name === "") { + return { message: m.register_device_no_name() }; + } - if (res.ok) { - return redirect(returnTo?.toString() ?? `/devices/${id}`); - } else { - return { error: "There was an error registering your device" }; + try { + const res = await api.PUT(`${CLOUD_API}/devices/${id}`, { name }); + + if (res.ok) { + return redirect(returnTo?.toString() ?? `/devices/${id}`); + } else { + return { error: m.register_device_error({ error:res.statusText }) }; + } + } catch (e) { + console.error(e); + return { message: m.register_device_error({ error: String(e) }) }; } }; @@ -61,21 +71,19 @@ export default function SetupRoute() {

Let's name your device

- Name your device so you can easily identify it later. You can change - this name at any time. + {m.register_device_name_description()}

@@ -86,7 +94,7 @@ export default function SetupRoute() { theme="primary" fullWidth type="submit" - text="Finish Setup" + text={m.register_device_finish_button()} textAlign="center" /> diff --git a/ui/src/routes/devices.$id.tsx b/ui/src/routes/devices.$id.tsx index a1ace077..669c32b7 100644 --- a/ui/src/routes/devices.$id.tsx +++ b/ui/src/routes/devices.$id.tsx @@ -15,10 +15,10 @@ import { FocusTrap } from "focus-trap-react"; import { motion, AnimatePresence } from "framer-motion"; import useWebSocket from "react-use-websocket"; +import { cx } from "@/cva.config"; import { CLOUD_API, DEVICE_API } from "@/ui.config"; import api from "@/api"; import { checkAuth, isInCloud, isOnDevice } from "@/main"; -import { cx } from "@/cva.config"; import { KeyboardLedState, KeysDownState, @@ -33,23 +33,24 @@ import { useUpdateStore, useVideoStore, VideoState, -} from "@/hooks/stores"; +} from "@hooks/stores"; +import { JsonRpcRequest, JsonRpcResponse, RpcMethodNotFound, useJsonRpc } from "@hooks/useJsonRpc"; +import { useDeviceUiNavigation } from "@hooks/useAppNavigation"; +import { useVersion } from "@hooks/useVersion"; import WebRTCVideo from "@components/WebRTCVideo"; import DashboardNavbar from "@components/Header"; -const ConnectionStatsSidebar = lazy(() => import('@/components/sidebar/connectionStats')); +const ConnectionStatsSidebar = lazy(() => import('@components/sidebar/connectionStats')); const Terminal = lazy(() => import('@components/Terminal')); -const UpdateInProgressStatusCard = lazy(() => import("@/components/UpdateInProgressStatusCard")); -import Modal from "@/components/Modal"; -import { JsonRpcRequest, JsonRpcResponse, RpcMethodNotFound, useJsonRpc } from "@/hooks/useJsonRpc"; +const UpdateInProgressStatusCard = lazy(() => import("@components/UpdateInProgressStatusCard")); +import Modal from "@components/Modal"; import { ConnectionFailedOverlay, LoadingConnectionOverlay, PeerConnectionDisconnectedOverlay, -} from "@/components/VideoOverlay"; -import { useDeviceUiNavigation } from "@/hooks/useAppNavigation"; -import { FeatureFlagProvider } from "@/providers/FeatureFlagProvider"; +} from "@components/VideoOverlay"; +import { FeatureFlagProvider } from "@providers/FeatureFlagProvider"; import { DeviceStatus } from "@routes/welcome-local"; -import { useVersion } from "@/hooks/useVersion"; +import { m } from "@localizations/messages.js"; interface LocalLoaderResp { authMode: "password" | "noPassword" | null; @@ -123,9 +124,9 @@ export default function KvmIdRoute() { const params = useParams() as { id: string }; const { sidebarView, setSidebarView, disableVideoFocusTrap } = useUiStore(); - const [ queryParams, setQueryParams ] = useSearchParams(); + const [queryParams, setQueryParams] = useSearchParams(); - const { + const { peerConnection, setPeerConnection, peerConnectionState, setPeerConnectionState, setMediaStream, @@ -145,7 +146,7 @@ export default function KvmIdRoute() { const navigate = useNavigate(); const { otaState, setOtaState, setModalView } = useUpdateStore(); - const [loadingMessage, setLoadingMessage] = useState("Connecting to device..."); + const [loadingMessage, setLoadingMessage] = useState(m.connecting_to_device()); const cleanupAndStopReconnecting = useCallback( function cleanupAndStopReconnecting() { console.log("Closing peer connection"); @@ -182,12 +183,12 @@ export default function KvmIdRoute() { pc: RTCPeerConnection, remoteDescription: RTCSessionDescriptionInit, ) { - setLoadingMessage("Setting remote description"); + setLoadingMessage(m.setting_remote_description()); try { await pc.setRemoteDescription(new RTCSessionDescription(remoteDescription)); console.log("[setRemoteSessionDescription] Remote description set successfully"); - setLoadingMessage("Establishing secure connection..."); + setLoadingMessage(m.establishing_secure_connection()); } catch (error) { console.error( "[setRemoteSessionDescription] Failed to set remote description:", @@ -206,7 +207,7 @@ export default function KvmIdRoute() { if (pc.sctp?.state === "connected") { console.log("[setRemoteSessionDescription] Remote description set"); clearInterval(checkInterval); - setLoadingMessage("Connection established"); + setLoadingMessage(m.connection_established()); } else if (attempts >= 10) { console.warn( "[setRemoteSessionDescription] Failed to establish connection after 10 attempts", @@ -243,31 +244,32 @@ export default function KvmIdRoute() { retryOnError: true, reconnectAttempts: 15, reconnectInterval: 1000, - onReconnectStop: () => { - console.debug("Reconnect stopped"); + onReconnectStop: (numAttempts: number) => { + console.debug("Reconnect stopped", numAttempts); cleanupAndStopReconnecting(); }, - shouldReconnect(event) { + shouldReconnect(event: WebSocketEventMap['close']) { console.debug("[Websocket] shouldReconnect", event); // TODO: Why true? return true; }, - onClose(event) { + onClose(event: WebSocketEventMap['close']) { console.debug("[Websocket] onClose", event); // We don't want to close everything down, we wait for the reconnect to stop instead }, - onError(event) { + onError(event: WebSocketEventMap['error']) { console.error("[Websocket] onError", event); // We don't want to close everything down, we wait for the reconnect to stop instead }, - onOpen() { - console.debug("[Websocket] onOpen"); + onOpen(event: WebSocketEventMap['open']) { + console.debug("[Websocket] onOpen", event); }, - onMessage: message => { + onMessage(event: WebSocketEventMap['message']) { + const message = event as MessageEvent; if (message.data === "pong") return; /* @@ -360,12 +362,12 @@ export default function KvmIdRoute() { const sd = btoa(JSON.stringify(pc.localDescription)); // Legacy mode == UI in cloud with updated code connecting to older device version. - // In device mode, old devices wont server this JS, and on newer devices legacy mode wont be enabled + // In device mode, old devices wont serve this JS, and on newer devices legacy mode wont be enabled const sessionUrl = `${CLOUD_API}/webrtc/session`; console.log("Trying to get remote session description"); setLoadingMessage( - `Getting remote session description... ${signalingAttempts.current > 0 ? `(attempt ${signalingAttempts.current + 1})` : ""}`, + m.getting_remote_session_description({ attempt: signalingAttempts.current + 1 }), ); const res = await api.POST(sessionUrl, { sd, @@ -382,7 +384,7 @@ export default function KvmIdRoute() { } console.debug("Successfully got Remote Session Description. Setting."); - setLoadingMessage("Setting remote session description..."); + setLoadingMessage(m.setting_remote_session_description()); const decodedSd = atob(json.sd); const parsedSd = JSON.parse(decodedSd); @@ -394,12 +396,12 @@ export default function KvmIdRoute() { const setupPeerConnection = useCallback(async () => { console.debug("[setupPeerConnection] Setting up peer connection"); setConnectionFailed(false); - setLoadingMessage("Connecting to device..."); + setLoadingMessage(m.connecting_to_device()); let pc: RTCPeerConnection; try { console.debug("[setupPeerConnection] Creating peer connection"); - setLoadingMessage("Creating peer connection..."); + setLoadingMessage(m.creating_peer_connection()); pc = new RTCPeerConnection({ // We only use STUN or TURN servers if we're in the cloud ...(isInCloud && iceConfig?.iceServers @@ -409,7 +411,7 @@ export default function KvmIdRoute() { setPeerConnectionState(pc.connectionState); console.debug("[setupPeerConnection] Peer connection created", pc); - setLoadingMessage("Setting up connection to device..."); + setLoadingMessage(m.setting_up_connection_to_device()); } catch (e) { console.error(`[setupPeerConnection] Error creating peer connection: ${e}`); setTimeout(() => { @@ -459,7 +461,7 @@ export default function KvmIdRoute() { const pc = event.currentTarget as RTCPeerConnection; if (pc.iceGatheringState === "complete") { console.debug("ICE Gathering completed"); - setLoadingMessage("ICE Gathering completed"); + setLoadingMessage(m.ice_gathering_completed()); if (isLegacySignalingEnabled.current) { // We can now start the https/ws connection to get the remote session description from the KVM device @@ -467,7 +469,7 @@ export default function KvmIdRoute() { } } else if (pc.iceGatheringState === "gathering") { console.debug("ICE Gathering Started"); - setLoadingMessage("Gathering ICE candidates..."); + setLoadingMessage(m.gathering_ice_candidates()); } }; @@ -478,6 +480,8 @@ export default function KvmIdRoute() { setTransceiver(pc.addTransceiver("video", { direction: "recvonly" })); const rpcDataChannel = pc.createDataChannel("rpc"); + rpcDataChannel.onclose = () => console.log("rpcDataChannel has closed"); + rpcDataChannel.onerror = (e: Event) => console.error(`Error on DataChannel '${rpcDataChannel.label}': ${e}`); rpcDataChannel.onopen = () => { setRpcDataChannel(rpcDataChannel); }; @@ -597,13 +601,14 @@ export default function KvmIdRoute() { }); }, 10000); - const { setNetworkState} = useNetworkStateStore(); + const { setNetworkState } = useNetworkStateStore(); const { setHdmiState } = useVideoStore(); - const { - keyboardLedState, setKeyboardLedState, - keysDownState, setKeysDownState, setUsbState, + const { + keyboardLedState, setKeyboardLedState, + keysDownState, setKeysDownState, + setUsbState, } = useHidStore(); - const setHidRpcDisabled = useRTCStore(state => state.setHidRpcDisabled); + const { setHidRpcDisabled } = useRTCStore(); const [hasUpdated, setHasUpdated] = useState(false); const { navigateTo } = useDeviceUiNavigation(); @@ -681,7 +686,7 @@ export default function KvmIdRoute() { }); }, [rpcDataChannel?.readyState, send, setHdmiState]); - const [needLedState, setNeedLedState] = useState(true); + const [ needLedState, setNeedLedState ] = useState(true); // request keyboard led state from the device useEffect(() => { @@ -737,8 +742,8 @@ export default function KvmIdRoute() { }, [navigate, navigateTo, queryParams, setModalView, setQueryParams]); // System update - const [kvmTerminal, setKvmTerminal] = useState(null); - const [serialConsole, setSerialConsole] = useState(null); + const [ kvmTerminal, setKvmTerminal ] = useState(null); + const [ serialConsole, setSerialConsole ] = useState(null); useEffect(() => { if (!peerConnection) return; @@ -756,7 +761,7 @@ export default function KvmIdRoute() { if (location.pathname !== "/other-session") navigateTo("/"); }, [navigateTo, location.pathname]); - const { appVersion, getLocalVersion} = useVersion(); + const { appVersion, getLocalVersion } = useVersion(); useEffect(() => { if (appVersion) return; @@ -837,7 +842,7 @@ export default function KvmIdRoute() { isLoggedIn={authMode === "password" || !!user} userEmail={user?.email} picture={user?.picture} - kvmName={deviceName ?? "JetKVM Device"} + kvmName={deviceName ?? m.jetkvm_device()} />
@@ -873,11 +878,11 @@ export default function KvmIdRoute() {
{kvmTerminal && ( - + )} {serialConsole && ( - + )} ); diff --git a/ui/src/routes/devices.already-adopted.tsx b/ui/src/routes/devices.already-adopted.tsx index ee189a8a..40556525 100644 --- a/ui/src/routes/devices.already-adopted.tsx +++ b/ui/src/routes/devices.already-adopted.tsx @@ -2,6 +2,7 @@ import { LinkButton } from "@/components/Button"; import SimpleNavbar from "@/components/SimpleNavbar"; import Container from "@/components/Container"; import GridBackground from "@components/GridBackground"; +import { m } from "@localizations/messages.js"; export default function DevicesAlreadyAdopted() { return ( @@ -14,15 +15,12 @@ export default function DevicesAlreadyAdopted() {
-

Device Already Registered

+

{m.already_adopted_title()}

- This device is currently registered to another user in our cloud - dashboard. + {m.already_adopted_other_user()}

- If you're the new owner, please ask the previous owner to de-register - the device from their account in the cloud dashboard. If you believe - this is an error, contact our support team for assistance. + {m.already_adopted_new_owner()}

@@ -31,7 +29,7 @@ export default function DevicesAlreadyAdopted() { to="/devices" size="LG" theme="primary" - text="Return to Dashboard" + text={m.already_adopted_return_to_dashboard()} />
diff --git a/ui/src/routes/devices.tsx b/ui/src/routes/devices.tsx index 1dac696d..ed3e8ca9 100644 --- a/ui/src/routes/devices.tsx +++ b/ui/src/routes/devices.tsx @@ -4,13 +4,14 @@ import { LuMonitorSmartphone } from "react-icons/lu"; import { ArrowRightIcon } from "@heroicons/react/16/solid"; import { useInterval } from "usehooks-ts"; +import { User } from "@hooks/stores"; import DashboardNavbar from "@components/Header"; import EmptyCard from "@components/EmptyCard"; import KvmCard from "@components/KvmCard"; import { LinkButton } from "@components/Button"; -import { User } from "@/hooks/stores"; import { checkAuth } from "@/main"; import { CLOUD_API } from "@/ui.config"; +import { m } from "@localizations/messages"; interface LoaderData { devices: { id: string; name: string; online: boolean; lastSeen: string }[]; @@ -54,10 +55,10 @@ export default function DevicesRoute() {

- Cloud KVMs + {m.cloud_kvms()}

- Manage your cloud KVMs and connect to them securely. + {m.cloud_kvms_description()}

@@ -66,15 +67,15 @@ export default function DevicesRoute() {
} /> diff --git a/ui/src/routes/login-local.tsx b/ui/src/routes/login-local.tsx index 5fab7e6e..cd240bad 100644 --- a/ui/src/routes/login-local.tsx +++ b/ui/src/routes/login-local.tsx @@ -1,22 +1,21 @@ +import { useState } from "react"; import { Form, redirect, useActionData } from "react-router"; import type { ActionFunction, ActionFunctionArgs, LoaderFunction } from "react-router"; -import { useState } from "react"; import { LuEye, LuEyeOff } from "react-icons/lu"; -import SimpleNavbar from "@components/SimpleNavbar"; -import GridBackground from "@components/GridBackground"; -import Container from "@components/Container"; -import Fieldset from "@components/Fieldset"; -import { InputFieldWithLabel } from "@components/InputField"; +import LogoBlueIcon from "@assets/logo-blue.png"; +import LogoWhiteIcon from "@assets/logo-white.svg"; import { Button } from "@components/Button"; -import LogoBlueIcon from "@/assets/logo-blue.png"; -import LogoWhiteIcon from "@/assets/logo-white.svg"; +import Container from "@components/Container"; +import ExtLink from "@components/ExtLink"; +import Fieldset from "@components/Fieldset"; +import GridBackground from "@components/GridBackground"; +import { InputFieldWithLabel } from "@components/InputField"; +import SimpleNavbar from "@components/SimpleNavbar"; +import { DeviceStatus } from "@routes/welcome-local"; import { DEVICE_API } from "@/ui.config"; - -import api from "../api"; -import ExtLink from "../components/ExtLink"; - -import { DeviceStatus } from "./welcome-local"; +import api from "@/api"; +import { m } from "@localizations/messages.js"; const loader: LoaderFunction = async () => { const res = await api @@ -42,11 +41,11 @@ const action: ActionFunction = async ({ request }: ActionFunctionArgs) => { if (response.ok) { return redirect("/"); } else { - return { error: "Invalid password" }; + return { error: m.invalid_password() }; } } catch (error) { console.error(error); - return { error: "An error occurred while logging in" }; + return { error: m.login_error() }; } }; @@ -73,10 +72,10 @@ export default function LoginLocalRoute() {

- Welcome back to JetKVM + {m.login_welcome_back()}

- Enter your password to access your JetKVM. + {m.login_enter_password_description()}

@@ -84,11 +83,11 @@ export default function LoginLocalRoute() {
@@ -125,7 +124,7 @@ export default function LoginLocalRoute() { href="https://jetkvm.com/docs/networking/local-access#reset-password" className="hover:underline" > - Forgot password? + {m.login_forgot_password()}
diff --git a/ui/src/routes/signup.tsx b/ui/src/routes/signup.tsx index cb46c496..098aa4d5 100644 --- a/ui/src/routes/signup.tsx +++ b/ui/src/routes/signup.tsx @@ -1,7 +1,7 @@ import { useLocation, useSearchParams } from "react-router"; -import { m } from "@localizations/messages.js"; import AuthLayout from "@components/AuthLayout"; +import { m } from "@localizations/messages.js"; export default function SignupRoute() { const [sq] = useSearchParams(); diff --git a/ui/src/routes/welcome-local.mode.tsx b/ui/src/routes/welcome-local.mode.tsx index 8d1a808b..e4753494 100644 --- a/ui/src/routes/welcome-local.mode.tsx +++ b/ui/src/routes/welcome-local.mode.tsx @@ -1,17 +1,17 @@ +import { useState } from "react"; import { Form, redirect, useActionData } from "react-router"; import type { ActionFunction, ActionFunctionArgs, LoaderFunction } from "react-router"; -import { useState } from "react"; -import GridBackground from "@components/GridBackground"; -import Container from "@components/Container"; +import { cx } from "@/cva.config"; +import LogoBlueIcon from "@assets/logo-blue.png"; +import LogoWhiteIcon from "@assets/logo-white.svg"; import { Button } from "@components/Button"; -import LogoBlueIcon from "@/assets/logo-blue.png"; -import LogoWhiteIcon from "@/assets/logo-white.svg"; +import Container from "@components/Container"; +import GridBackground from "@components/GridBackground"; +import { GridCard } from "@components/Card"; import { DEVICE_API } from "@/ui.config"; - -import { GridCard } from "../components/Card"; -import { cx } from "../cva.config"; -import api from "../api"; +import api from "@/api"; +import { m } from "@localizations/messages.js"; import { DeviceStatus } from "./welcome-local"; @@ -27,7 +27,7 @@ const loader: LoaderFunction = async () => { const action: ActionFunction = async ({ request }: ActionFunctionArgs) => { const formData = await request.formData(); const localAuthMode = formData.get("localAuthMode"); - if (!localAuthMode) return { error: "Please select an authentication mode" }; + if (!localAuthMode) return { error: m.auth_authentication_mode() }; if (localAuthMode === "password") { return redirect("/welcome/password"); @@ -41,11 +41,11 @@ const action: ActionFunction = async ({ request }: ActionFunctionArgs) => { return redirect("/"); } catch (error) { console.error("Error setting authentication mode:", error); - return { error: "An error occurred while setting the authentication mode" }; + return { error: m.auth_authentication_mode_error() }; } } - return { error: "Invalid authentication mode" }; + return { error: m.auth_authentication_mode_invalid() }; }; export default function WelcomeLocalModeRoute() { @@ -75,10 +75,10 @@ export default function WelcomeLocalModeRoute() { style={{ animationDelay: "200ms" }} >

- Local Authentication Method + {m.auth_mode_local()}

- Select how you{"'"}d like to secure your JetKVM device locally. + {m.auth_mode_local_description()}

@@ -101,12 +101,12 @@ export default function WelcomeLocalModeRoute() { >

- {mode === "password" ? "Password protected" : "No Password"} + {mode === "password" ? m.auth_mode_local_password() : m.auth_mode_local_no_password()}

{mode === "password" - ? "Secure your device with a password for added protection." - : "Quick access without password authentication."} + ? m.auth_mode_local_password_description() + : m.auth_mode_local_no_password_description()}

@@ -153,7 +153,7 @@ export default function WelcomeLocalModeRoute() { className="animate-fadeIn mx-auto max-w-md text-center text-xs text-slate-500 opacity-0 dark:text-slate-400" style={{ animationDelay: "600ms" }} > - You can always change your authentication method later in the settings. + {m.auth_mode_local_change_later()}

diff --git a/ui/src/routes/welcome-local.password.tsx b/ui/src/routes/welcome-local.password.tsx index d0b7c7a9..077b0753 100644 --- a/ui/src/routes/welcome-local.password.tsx +++ b/ui/src/routes/welcome-local.password.tsx @@ -1,18 +1,18 @@ +import { useState, useRef, useEffect } from "react"; import { Form, redirect, useActionData } from "react-router"; import type { ActionFunction, ActionFunctionArgs, LoaderFunction } from "react-router"; -import { useState, useRef, useEffect } from "react"; import { LuEye, LuEyeOff } from "react-icons/lu"; +import LogoBlueIcon from "@assets/logo-blue.png"; +import LogoWhiteIcon from "@assets/logo-white.svg"; import GridBackground from "@components/GridBackground"; import Container from "@components/Container"; import Fieldset from "@components/Fieldset"; import { InputFieldWithLabel } from "@components/InputField"; import { Button } from "@components/Button"; -import LogoBlueIcon from "@/assets/logo-blue.png"; -import LogoWhiteIcon from "@/assets/logo-white.svg"; import { DEVICE_API } from "@/ui.config"; - -import api from "../api"; +import api from "@/api"; +import { m } from "@localizations/messages.js"; import { DeviceStatus } from "./welcome-local"; @@ -31,7 +31,7 @@ const action: ActionFunction = async ({ request }: ActionFunctionArgs) => { const confirmPassword = formData.get("confirmPassword"); if (password !== confirmPassword) { - return { error: "Passwords do not match" }; + return { error: m.auth_mode_local_password_do_not_match() }; } try { @@ -43,11 +43,11 @@ const action: ActionFunction = async ({ request }: ActionFunctionArgs) => { if (response.ok) { return redirect("/"); } else { - return { error: "Failed to set password" }; + return { error: m.auth_mode_local_password_failed_set({ error: response.statusText }) }; } } catch (error) { console.error("Error setting password:", error); - return { error: "An error occurred while setting the password" }; + return { error: m.auth_mode_local_password_failed_set({ error: String(error) })}; } }; @@ -86,10 +86,10 @@ export default function WelcomeLocalPasswordRoute() { style={{ animationDelay: "200ms" }} >

- Set a Password + {m.auth_mode_local_password_set()}

- Create a strong password to secure your JetKVM device locally. + {m.auth_mode_local_password_set_description()}

@@ -101,10 +101,10 @@ export default function WelcomeLocalPasswordRoute() { style={{ animationDelay: "400ms" }} > - {actionData?.error &&

{}

} + {actionData?.error &&

{ }

}
@@ -163,9 +163,7 @@ export default function WelcomeLocalPasswordRoute() { className="animate-fadeIn max-w-md text-center text-xs text-slate-500 opacity-0 dark:text-slate-400" style={{ animationDelay: "800ms" }} > - This password will be used to secure your device data and protect against - unauthorized access.{" "} - All data remains on your local device. + {m.auth_mode_local_password_note()} {m.auth_mode_local_password_note_local()}

diff --git a/ui/src/routes/welcome-local.tsx b/ui/src/routes/welcome-local.tsx index 337391f8..29cfd333 100644 --- a/ui/src/routes/welcome-local.tsx +++ b/ui/src/routes/welcome-local.tsx @@ -3,15 +3,15 @@ import { redirect } from "react-router"; import type { LoaderFunction } from "react-router"; import { cx } from "cva"; -import api from "@/api"; -import { DEVICE_API } from "@/ui.config"; -import GridBackground from "@components/GridBackground"; -import Container from "@components/Container"; -import { LinkButton } from "@components/Button"; import LogoBlueIcon from "@assets/logo-blue.png"; import LogoWhiteIcon from "@assets/logo-white.svg"; import DeviceImage from "@assets/jetkvm-device-still.png"; import LogoMark from "@assets/logo-mark.png"; +import Container from "@components/Container"; +import GridBackground from "@components/GridBackground"; +import { LinkButton } from "@components/Button"; +import api from "@/api"; +import { DEVICE_API } from "@/ui.config"; import { m } from "@localizations/messages.js"; export interface DeviceStatus { @@ -54,17 +54,17 @@ export default function WelcomeRoute() { /> JetKVM Logo

- Welcome to JetKVM + {m.welcome_to_jetkvm()}

- Control any computer remotely + {m.welcome_to_jetkvm_description()}

@@ -72,7 +72,7 @@ export default function WelcomeRoute() {
JetKVM Device
@@ -82,14 +82,13 @@ export default function WelcomeRoute() { style={{ animationDelay: "2000ms" }} className="animate-fadeIn mx-auto max-w-lg text-lg text-slate-700 opacity-0 dark:text-slate-300" > - JetKVM combines powerful hardware with intuitive software to provide a - seamless remote control experience. + {m.jetkvm_description()}

( )}