Add inlang/paraglide-js

This commit is contained in:
Marc Brooks 2025-10-01 12:21:03 -05:00
parent d91b95783a
commit d81a497926
No known key found for this signature in database
GPG Key ID: 583A6AF2D6AE1DC6
25 changed files with 1222 additions and 132 deletions

View File

@ -31,7 +31,10 @@
// Frontend // Frontend
"esbenp.prettier-vscode", "esbenp.prettier-vscode",
"dbaeumer.vscode-eslint", "dbaeumer.vscode-eslint",
"bradlc.vscode-tailwindcss" "bradlc.vscode-tailwindcss",
"codeandstuff.package-json-upgrade",
// Localization
"inlang.vs-code-extension"
] ]
} }
} }

25
.vscode/extensions.json vendored Normal file
View File

@ -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"
]
}

View File

@ -81,6 +81,8 @@ module.exports = defineConfig([{
map: [ map: [
["@components", "./src/components"], ["@components", "./src/components"],
["@routes", "./src/routes"], ["@routes", "./src/routes"],
["@hooks", "./src/hooks"],
["@providers", "./src/providers"],
["@assets", "./src/assets"], ["@assets", "./src/assets"],
["@", "./src"], ["@", "./src"],
], ],

View File

@ -45,31 +45,39 @@
<link rel="shortcut icon" href="/favicon.ico" /> <link rel="shortcut icon" href="/favicon.ico" />
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" /> <link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
<meta name="apple-mobile-web-app-title" content="JetKVM" /> <meta name="apple-mobile-web-app-title" content="JetKVM" />
<link rel="manifest" href="/site.webmanifest" /> <link rel="manifest" href="/public/site.webmanifest" />
<meta name="theme-color" content="#051946" /> <meta name="theme-color" content="#051946" />
<meta name="description" content="A web-based KVM console for managing remote servers." /> <meta
name="description"
content="A web-based KVM console for managing remote servers."
/>
<script> <script>
function applyThemeFromPreference() { function applyThemeFromPreference() {
// dark theme setup // dark theme setup
var darkDesired = localStorage.theme === "dark" || var darkDesired =
localStorage.theme === "dark" ||
(!("theme" in localStorage) && (!("theme" in localStorage) &&
window.matchMedia("(prefers-color-scheme: dark)").matches) window.matchMedia("(prefers-color-scheme: dark)").matches);
document.documentElement.classList.toggle("dark", darkDesired) document.documentElement.classList.toggle("dark", darkDesired);
} }
// initial theme application // initial theme application
applyThemeFromPreference(); applyThemeFromPreference();
// Listen for system theme changes // Listen for system theme changes
window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change", applyThemeFromPreference); window
window.matchMedia("(prefers-color-scheme: light)").addEventListener("change", applyThemeFromPreference); .matchMedia("(prefers-color-scheme: dark)")
.addEventListener("change", applyThemeFromPreference);
window
.matchMedia("(prefers-color-scheme: light)")
.addEventListener("change", applyThemeFromPreference);
</script> </script>
</head> </head>
<body <body
class="h-full w-full bg-[#f3f9ff] font-sans text-sm antialiased dark:bg-slate-900 md:text-base" class="h-full w-full bg-[#f3f9ff] font-sans text-sm antialiased md:text-base dark:bg-slate-900"
> >
<div id="root" class="w-full h-full"></div> <div id="root" class="h-full w-full"></div>
<script type="module" src="/src/main.tsx"></script> <script type="module" src="/src/main.tsx"></script>
</body> </body>
</html> </html>

17
ui/messages/da.json Normal file
View File

@ -0,0 +1,17 @@
{
"$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",
"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"
}

17
ui/messages/de.json Normal file
View File

@ -0,0 +1,17 @@
{
"$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",
"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"
}

17
ui/messages/en.json Normal file
View File

@ -0,0 +1,17 @@
{
"$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",
"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"
}

17
ui/messages/es.json Normal file
View File

@ -0,0 +1,17 @@
{
"$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",
"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"
}

17
ui/messages/fr.json Normal file
View File

@ -0,0 +1,17 @@
{
"$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",
"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"
}

17
ui/messages/it.json Normal file
View File

@ -0,0 +1,17 @@
{
"$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",
"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"
}

17
ui/messages/nb.json Normal file
View File

@ -0,0 +1,17 @@
{
"$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",
"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"
}

17
ui/messages/sv.json Normal file
View File

@ -0,0 +1,17 @@
{
"$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",
"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"
}

17
ui/messages/zh.json Normal file
View File

@ -0,0 +1,17 @@
{
"$schema": "https://inlang.com/schema/inlang-message-format",
"jetkvm": "JetKVM",
"oh_no": "噢不!",
"something_went_wrong": "出了点问题。请稍后重试或联系客服",
"jetkvm_logo": "JetKVM 徽标",
"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": "退出全屏"
}

929
ui/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -11,12 +11,13 @@
"dev:ssl": "USE_SSL=true ./dev_device.sh", "dev:ssl": "USE_SSL=true ./dev_device.sh",
"dev:cloud": "vite dev --mode=cloud-development", "dev:cloud": "vite dev --mode=cloud-development",
"build": "npm run build:prod", "build": "npm run build:prod",
"build:device": "tsc && vite build --mode=device --emptyOutDir", "build:device": "paraglide-js compile --project ./project.inlang && tsc && vite build --mode=device --emptyOutDir",
"build:staging": "tsc && vite build --mode=cloud-staging", "build:staging": "paraglide-js compile --project ./project.inlang && tsc && vite build --mode=cloud-staging",
"build:prod": "tsc && vite build --mode=cloud-production", "build:prod": "paraglide-js compile --project ./project.inlang && tsc && vite build --mode=cloud-production",
"lint": "eslint './src/**/*.{ts,tsx}'", "lint": "paraglide-js compile --project ./project.inlang && eslint './src/**/*.{ts,tsx}'",
"lint:fix": "eslint './src/**/*.{ts,tsx}' --fix", "lint:fix": "paraglide-js compile --project ./project.inlang && eslint './src/**/*.{ts,tsx}' --fix",
"preview": "vite preview" "paraglide": "paraglide-js compile --project ./project.inlang",
"machine-translate": "inlang machine translate --project project.inlang"
}, },
"dependencies": { "dependencies": {
"@headlessui/react": "^2.2.9", "@headlessui/react": "^2.2.9",
@ -36,9 +37,9 @@
"framer-motion": "^12.23.22", "framer-motion": "^12.23.22",
"lodash.throttle": "^4.1.1", "lodash.throttle": "^4.1.1",
"mini-svg-data-uri": "^1.4.4", "mini-svg-data-uri": "^1.4.4",
"react": "^19.1.1", "react": "^19.2.0",
"react-animate-height": "^3.2.3", "react-animate-height": "^3.2.3",
"react-dom": "^19.1.1", "react-dom": "^19.2.0",
"react-hot-toast": "^2.6.0", "react-hot-toast": "^2.6.0",
"react-icons": "^5.5.0", "react-icons": "^5.5.0",
"react-router": "^7.9.3", "react-router": "^7.9.3",
@ -55,12 +56,17 @@
"@eslint/compat": "^1.4.0", "@eslint/compat": "^1.4.0",
"@eslint/eslintrc": "^3.3.1", "@eslint/eslintrc": "^3.3.1",
"@eslint/js": "^9.36.0", "@eslint/js": "^9.36.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/forms": "^0.5.10",
"@tailwindcss/postcss": "^4.1.14", "@tailwindcss/postcss": "^4.1.14",
"@tailwindcss/typography": "^0.5.19", "@tailwindcss/typography": "^0.5.19",
"@tailwindcss/vite": "^4.1.14", "@tailwindcss/vite": "^4.1.14",
"@types/react": "^19.1.17", "@types/react": "^19.2.0",
"@types/react-dom": "^19.1.10", "@types/react-dom": "^19.2.0",
"@types/semver": "^7.7.1", "@types/semver": "^7.7.1",
"@types/validator": "^13.15.3", "@types/validator": "^13.15.3",
"@typescript-eslint/eslint-plugin": "^8.45.0", "@typescript-eslint/eslint-plugin": "^8.45.0",
@ -71,8 +77,8 @@
"eslint-config-prettier": "^10.1.8", "eslint-config-prettier": "^10.1.8",
"eslint-plugin-import": "^2.32.0", "eslint-plugin-import": "^2.32.0",
"eslint-plugin-react": "^7.37.5", "eslint-plugin-react": "^7.37.5",
"eslint-plugin-react-hooks": "^5.2.0", "eslint-plugin-react-hooks": "^6.1.0",
"eslint-plugin-react-refresh": "^0.4.22", "eslint-plugin-react-refresh": "^0.4.23",
"globals": "^16.4.0", "globals": "^16.4.0",
"postcss": "^8.5.6", "postcss": "^8.5.6",
"prettier": "^3.6.2", "prettier": "^3.6.2",

1
ui/project.inlang/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
cache

View File

@ -0,0 +1 @@
TI1a2RjjH4qkImNj0w

View File

@ -0,0 +1,28 @@
{
"$schema": "https://inlang.com/schema/project-settings",
"baseLocale": "en",
"sourceLanguageTag": "en",
"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": [
"localStorage",
"preferredLanguage",
"baseLocale"
]
}

View File

@ -1,24 +1,25 @@
import { Fragment, useCallback, useRef } from "react";
import { MdOutlineContentPasteGo } from "react-icons/md"; import { MdOutlineContentPasteGo } from "react-icons/md";
import { LuCable, LuHardDrive, LuMaximize, LuSettings, LuSignal } from "react-icons/lu"; import { LuCable, LuHardDrive, LuMaximize, LuSettings, LuSignal } from "react-icons/lu";
import { FaKeyboard } from "react-icons/fa6"; import { FaKeyboard } from "react-icons/fa6";
import { Popover, PopoverButton, PopoverPanel } from "@headlessui/react"; import { Popover, PopoverButton, PopoverPanel } from "@headlessui/react";
import { Fragment, useCallback, useRef } from "react";
import { CommandLineIcon } from "@heroicons/react/20/solid"; import { CommandLineIcon } from "@heroicons/react/20/solid";
import { Button } from "@components/Button";
import { import {
useHidStore, useHidStore,
useMountMediaStore, useMountMediaStore,
useSettingsStore, useSettingsStore,
useUiStore, useUiStore,
} from "@/hooks/stores"; } from "@hooks/stores";
import Container from "@components/Container";
import { cx } from "@/cva.config"; import { cx } from "@/cva.config";
import PasteModal from "@/components/popovers/PasteModal"; import { Button } from "@components/Button";
import WakeOnLanModal from "@/components/popovers/WakeOnLan/Index"; import Container from "@components/Container";
import MountPopopover from "@/components/popovers/MountPopover"; import PasteModal from "@components/popovers/PasteModal";
import ExtensionPopover from "@/components/popovers/ExtensionPopover"; import WakeOnLanModal from "@components/popovers/WakeOnLan/Index";
import { useDeviceUiNavigation } from "@/hooks/useAppNavigation"; import MountPopopover from "@components/popovers/MountPopover";
import ExtensionPopover from "@components/popovers/ExtensionPopover";
import { useDeviceUiNavigation } from "@hooks/useAppNavigation";
import { m } from "@/paraglide/messages.js";
export default function Actionbar({ export default function Actionbar({
requestFullscreen, requestFullscreen,
@ -28,10 +29,7 @@ export default function Actionbar({
const { navigateTo } = useDeviceUiNavigation(); const { navigateTo } = useDeviceUiNavigation();
const { isVirtualKeyboardEnabled, setVirtualKeyboardEnabled } = useHidStore(); const { isVirtualKeyboardEnabled, setVirtualKeyboardEnabled } = useHidStore();
const { setDisableVideoFocusTrap, terminalType, setTerminalType, toggleSidebarView } = useUiStore(); const { setDisableVideoFocusTrap, terminalType, setTerminalType, toggleSidebarView } = useUiStore();
const { remoteVirtualMediaState } = useMountMediaStore();
const remoteVirtualMediaState = useMountMediaStore(
state => state.remoteVirtualMediaState,
);
const { developerMode } = useSettingsStore(); const { developerMode } = useSettingsStore();
// This is the only way to get a reliable state change for the popover // This is the only way to get a reliable state change for the popover
@ -64,7 +62,7 @@ export default function Actionbar({
<Button <Button
size="XS" size="XS"
theme="light" theme="light"
text="Web Terminal" text={m.action_bar_web_terminal()}
LeadingIcon={({ className }) => <CommandLineIcon className={className} />} LeadingIcon={({ className }) => <CommandLineIcon className={className} />}
onClick={() => setTerminalType(terminalType === "kvm" ? "none" : "kvm")} onClick={() => setTerminalType(terminalType === "kvm" ? "none" : "kvm")}
/> />
@ -74,7 +72,7 @@ export default function Actionbar({
<Button <Button
size="XS" size="XS"
theme="light" theme="light"
text="Paste text" text={m.action_bar_paste_text()}
LeadingIcon={MdOutlineContentPasteGo} LeadingIcon={MdOutlineContentPasteGo}
onClick={() => { onClick={() => {
setDisableVideoFocusTrap(true); setDisableVideoFocusTrap(true);
@ -105,7 +103,7 @@ export default function Actionbar({
<Button <Button
size="XS" size="XS"
theme="light" theme="light"
text="Virtual Media" text={m.action_bar_virtual_media()}
LeadingIcon={({ className }) => { LeadingIcon={({ className }) => {
return ( return (
<> <>
@ -148,7 +146,7 @@ export default function Actionbar({
<Button <Button
size="XS" size="XS"
theme="light" theme="light"
text="Wake on LAN" text={m.action_bar_wake_on_lan()}
onClick={() => { onClick={() => {
setDisableVideoFocusTrap(true); setDisableVideoFocusTrap(true);
}} }}
@ -198,7 +196,7 @@ export default function Actionbar({
<Button <Button
size="XS" size="XS"
theme="light" theme="light"
text="Virtual Keyboard" text={m.action_bar_virtual_keyboard()}
LeadingIcon={FaKeyboard} LeadingIcon={FaKeyboard}
onClick={() => setVirtualKeyboardEnabled(!isVirtualKeyboardEnabled)} onClick={() => setVirtualKeyboardEnabled(!isVirtualKeyboardEnabled)}
/> />
@ -211,7 +209,7 @@ export default function Actionbar({
<Button <Button
size="XS" size="XS"
theme="light" theme="light"
text="Extension" text={m.action_bar_extension()}
LeadingIcon={LuCable} LeadingIcon={LuCable}
onClick={() => { onClick={() => {
setDisableVideoFocusTrap(true); setDisableVideoFocusTrap(true);
@ -237,7 +235,7 @@ export default function Actionbar({
<Button <Button
size="XS" size="XS"
theme="light" theme="light"
text="Virtual Keyboard" text={m.action_bar_virtual_keyboard()}
LeadingIcon={FaKeyboard} LeadingIcon={FaKeyboard}
onClick={() => setVirtualKeyboardEnabled(!isVirtualKeyboardEnabled)} onClick={() => setVirtualKeyboardEnabled(!isVirtualKeyboardEnabled)}
/> />
@ -246,7 +244,7 @@ export default function Actionbar({
<Button <Button
size="XS" size="XS"
theme="light" theme="light"
text="Connection Stats" text={m.action_bar_connection_stats()}
LeadingIcon={({ className }) => ( LeadingIcon={({ className }) => (
<LuSignal <LuSignal
className={cx(className, "mb-0.5 text-green-500")} className={cx(className, "mb-0.5 text-green-500")}
@ -262,7 +260,7 @@ export default function Actionbar({
<Button <Button
size="XS" size="XS"
theme="light" theme="light"
text="Settings" text={m.action_bar_settings()}
LeadingIcon={LuSettings} LeadingIcon={LuSettings}
onClick={() => { onClick={() => {
setDisableVideoFocusTrap(true); setDisableVideoFocusTrap(true);
@ -276,7 +274,7 @@ export default function Actionbar({
<Button <Button
size="XS" size="XS"
theme="light" theme="light"
text="Fullscreen" text={m.action_bar_fullscreen()}
LeadingIcon={LuMaximize} LeadingIcon={LuMaximize}
onClick={() => requestFullscreen()} onClick={() => requestFullscreen()}
/> />

View File

@ -1,6 +1,5 @@
import { lazy } from "react"; import { lazy } from "react";
import ReactDOM from "react-dom/client"; import ReactDOM from "react-dom/client";
import "./index.css";
import { import {
createBrowserRouter, createBrowserRouter,
isRouteErrorResponse, isRouteErrorResponse,
@ -8,11 +7,13 @@ import {
RouterProvider, RouterProvider,
useRouteError, useRouteError,
} from "react-router"; } from "react-router";
import "./index.css";
import { ExclamationTriangleIcon } from "@heroicons/react/16/solid"; import { ExclamationTriangleIcon } from "@heroicons/react/16/solid";
import { CLOUD_API, DEVICE_API } from "@/ui.config"; import { CLOUD_API, DEVICE_API } from "@/ui.config";
import api from "@/api"; import api from "@/api";
import Root from "@/root"; import Root from "@/root";
import { m } from "@/paraglide/messages.js";
import Card from "@components/Card"; import Card from "@components/Card";
import EmptyCard from "@components/EmptyCard"; import EmptyCard from "@components/EmptyCard";
import NotFoundPage from "@components/NotFoundPage"; import NotFoundPage from "@components/NotFoundPage";
@ -33,7 +34,7 @@ const SignupRoute = lazy(() => import("@routes/signup"));
const LoginRoute = lazy(() => import("@routes/login")); const LoginRoute = lazy(() => import("@routes/login"));
const DevicesAlreadyAdopted = lazy(() => import("@routes/devices.already-adopted")); const DevicesAlreadyAdopted = lazy(() => import("@routes/devices.already-adopted"));
const OtherSessionRoute = lazy(() => import("@routes/devices.$id.other-session")); const OtherSessionRoute = lazy(() => import("@routes/devices.$id.other-session"));
const MountRoute = lazy(() => import("./routes/devices.$id.mount")); const MountRoute = lazy(() => import("@routes/devices.$id.mount"));
const SettingsRoute = lazy(() => import("@routes/devices.$id.settings")); const SettingsRoute = lazy(() => import("@routes/devices.$id.settings"));
const SettingsMouseRoute = lazy(() => import("@routes/devices.$id.settings.mouse")); const SettingsMouseRoute = lazy(() => import("@routes/devices.$id.settings.mouse"));
const SettingsKeyboardRoute = lazy(() => import("@routes/devices.$id.settings.keyboard")); const SettingsKeyboardRoute = lazy(() => import("@routes/devices.$id.settings.keyboard"));
@ -404,8 +405,8 @@ function ErrorBoundary() {
<div className="w-full max-w-2xl"> <div className="w-full max-w-2xl">
<EmptyCard <EmptyCard
IconElm={ExclamationTriangleIcon} IconElm={ExclamationTriangleIcon}
headline="Oh no!" headline={m.oh_no()}
description="Something went wrong. Please try again later or contact support" description={m.something_went_wrong()}
BtnElm={ BtnElm={
errorMessage && ( errorMessage && (
<Card> <Card>

View File

@ -1,7 +1,7 @@
import { useNavigate } from "react-router";
import { useCallback } from "react"; import { useCallback } from "react";
import { useNavigate } from "react-router";
import { useJsonRpc } from "@/hooks/useJsonRpc"; import { useJsonRpc } from "@hooks/useJsonRpc";
import { Button } from "@components/Button"; import { Button } from "@components/Button";
export default function SettingsGeneralRebootRoute() { export default function SettingsGeneralRebootRoute() {

View File

@ -1,18 +1,18 @@
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { cx } from "cva";
import { redirect } from "react-router"; import { redirect } from "react-router";
import type { LoaderFunction } 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 GridBackground from "@components/GridBackground";
import Container from "@components/Container"; import Container from "@components/Container";
import { LinkButton } from "@components/Button"; import { LinkButton } from "@components/Button";
import LogoBlueIcon from "@/assets/logo-blue.png"; import LogoBlueIcon from "@assets/logo-blue.png";
import LogoWhiteIcon from "@/assets/logo-white.svg"; import LogoWhiteIcon from "@assets/logo-white.svg";
import DeviceImage from "@/assets/jetkvm-device-still.png"; import DeviceImage from "@assets/jetkvm-device-still.png";
import LogoMark from "@/assets/logo-mark.png"; import LogoMark from "@assets/logo-mark.png";
import { DEVICE_API } from "@/ui.config"; import { m } from "@/paraglide/messages.js";
import api from "../api";
export interface DeviceStatus { export interface DeviceStatus {
isSetup: boolean; isSetup: boolean;
@ -49,7 +49,7 @@ export default function WelcomeRoute() {
<div className="animate-fadeIn animation-delay-1000 flex items-center justify-center opacity-0"> <div className="animate-fadeIn animation-delay-1000 flex items-center justify-center opacity-0">
<img <img
src={LogoWhiteIcon} src={LogoWhiteIcon}
alt="JetKVM Logo" alt={m.jetkvm_logo()}
className="hidden h-[32px] dark:block" className="hidden h-[32px] dark:block"
/> />
<img <img

View File

@ -3,9 +3,14 @@
"target": "ES2020", "target": "ES2020",
"useDefineForClassFields": true, "useDefineForClassFields": true,
"forceConsistentCasingInFileNames": true, "forceConsistentCasingInFileNames": true,
"lib": ["ES2021", "DOM", "DOM.Iterable"], "lib": [
"ES2021",
"DOM",
"DOM.Iterable"
],
"module": "ESNext", "module": "ESNext",
"skipLibCheck": true, "skipLibCheck": true,
"allowJs": true,
/* Bundler mode */ /* Bundler mode */
"moduleResolution": "bundler", "moduleResolution": "bundler",
"allowImportingTsExtensions": false, "allowImportingTsExtensions": false,
@ -18,16 +23,36 @@
"noUnusedLocals": true, "noUnusedLocals": true,
"noUnusedParameters": true, "noUnusedParameters": true,
"noFallthroughCasesInSwitch": true, "noFallthroughCasesInSwitch": true,
"types": ["vite/client"], "erasableSyntaxOnly": true,
"noUncheckedSideEffectImports": true,
"types": [
"vite/client"
],
/* Import Aliases */ /* Import Aliases */
"paths": { "paths": {
"@components/*": ["./src/components/*"], "@components/*": [
"@routes/*": ["./src/routes/*"], "./src/components/*"
"@assets/*": ["./src/assets/*"], ],
"@/*": ["./src/*"] "@routes/*": [
"./src/routes/*"
],
"@hooks/*": [
"./src/hooks/*"
],
"@providers/*": [
"./src/providers/*"
],
"@assets/*": [
"./src/assets/*"
],
"@/*": [
"./src/*"
]
} }
}, },
"include": ["src"], "include": [
"src"
],
"references": [ "references": [
{ {
"path": "./tsconfig.node.json" "path": "./tsconfig.node.json"

View File

@ -1,11 +1,10 @@
{ {
"compilerOptions": { "compilerOptions": {
"composite": true, "composite": true,
"skipLibCheck": true, /* Bundler mode */
"module": "ESNext",
"moduleResolution": "bundler",
"allowSyntheticDefaultImports": true, "allowSyntheticDefaultImports": true,
"strict": true
}, },
"include": ["vite.config.ts"] "include": [
"vite.config.ts"
]
} }

View File

@ -3,6 +3,7 @@ import react from "@vitejs/plugin-react-swc";
import tailwindcss from "@tailwindcss/vite"; import tailwindcss from "@tailwindcss/vite";
import tsconfigPaths from "vite-tsconfig-paths"; import tsconfigPaths from "vite-tsconfig-paths";
import basicSsl from "@vitejs/plugin-basic-ssl"; import basicSsl from "@vitejs/plugin-basic-ssl";
import { paraglideVitePlugin } from "@inlang/paraglide-js";
declare const process: { declare const process: {
env: { env: {
@ -22,10 +23,20 @@ export default defineConfig(({ mode, command }) => {
tsconfigPaths(), tsconfigPaths(),
react() react()
]; ];
if (useSSL) { if (useSSL) {
plugins.push(basicSsl()); plugins.push(basicSsl());
} }
plugins.push(paraglideVitePlugin({
project: "./project.inlang",
outdir: "./src/paraglide",
outputStructure: 'message-modules',
cookieName: 'JETKVM_LOCALE',
localStorageKey: 'JETKVM_LOCALE',
strategy: ['cookie', 'localStorage', 'preferredLanguage', 'baseLocale'],
}))
return { return {
plugins, plugins,
esbuild: { esbuild: {