Compare commits

...

5 Commits

Author SHA1 Message Date
dependabot[bot] 8f374e752d
Merge d0fc410b4a into 0d7f47c109 2025-06-20 09:18:57 -05:00
Marc Brooks 0d7f47c109
fix(ui) firefox permissions error handling (#631) 2025-06-20 14:24:54 +02:00
iain MacDonnell 254c001572
fix: keyboard_layout default config (en-US/en_US) (#633) 2025-06-20 14:13:36 +02:00
Aveline 6f037a832d
feat(native): restart jetkvm_native automatically (#629) 2025-06-20 14:08:19 +02:00
dependabot[bot] d0fc410b4a
build(deps-dev): bump @tailwindcss/vite from 4.1.7 to 4.1.10 in /ui
Bumps [@tailwindcss/vite](https://github.com/tailwindlabs/tailwindcss/tree/HEAD/packages/@tailwindcss-vite) from 4.1.7 to 4.1.10.
- [Release notes](https://github.com/tailwindlabs/tailwindcss/releases)
- [Changelog](https://github.com/tailwindlabs/tailwindcss/blob/main/CHANGELOG.md)
- [Commits](https://github.com/tailwindlabs/tailwindcss/commits/v4.1.10/packages/@tailwindcss-vite)

---
updated-dependencies:
- dependency-name: "@tailwindcss/vite"
  dependency-version: 4.1.10
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-12 06:38:10 +00:00
7 changed files with 519 additions and 33 deletions

View File

@ -111,7 +111,7 @@ var defaultConfig = &Config{
ActiveExtension: "", ActiveExtension: "",
KeyboardMacros: []KeyboardMacro{}, KeyboardMacros: []KeyboardMacro{},
DisplayRotation: "270", DisplayRotation: "270",
KeyboardLayout: "en-US", KeyboardLayout: "en_US",
DisplayMaxBrightness: 64, DisplayMaxBrightness: 64,
DisplayDimAfterSec: 120, // 2 minutes DisplayDimAfterSec: 120, // 2 minutes
DisplayOffAfterSec: 1800, // 30 minutes DisplayOffAfterSec: 1800, // 30 minutes

View File

@ -8,6 +8,7 @@ import (
"io" "io"
"net" "net"
"os" "os"
"os/exec"
"sync" "sync"
"time" "time"
@ -41,6 +42,11 @@ var ongoingRequests = make(map[int32]chan *CtrlResponse)
var lock = &sync.Mutex{} var lock = &sync.Mutex{}
var (
nativeCmd *exec.Cmd
nativeCmdLock = &sync.Mutex{}
)
func CallCtrlAction(action string, params map[string]interface{}) (*CtrlResponse, error) { func CallCtrlAction(action string, params map[string]interface{}) (*CtrlResponse, error) {
lock.Lock() lock.Lock()
defer lock.Unlock() defer lock.Unlock()
@ -129,16 +135,26 @@ func StartNativeSocketServer(socketPath string, handleClient func(net.Conn), isC
scopedLogger.Info().Msg("server listening") scopedLogger.Info().Msg("server listening")
go func() { go func() {
conn, err := listener.Accept() for {
listener.Close() conn, err := listener.Accept()
if err != nil {
scopedLogger.Warn().Err(err).Msg("failed to accept socket") if err != nil {
scopedLogger.Warn().Err(err).Msg("failed to accept socket")
continue
}
if isCtrl {
// check if the channel is closed
select {
case <-ctrlClientConnected:
scopedLogger.Debug().Msg("ctrl client reconnected")
default:
close(ctrlClientConnected)
scopedLogger.Debug().Msg("first native ctrl socket client connected")
}
}
go handleClient(conn)
} }
if isCtrl {
close(ctrlClientConnected)
scopedLogger.Debug().Msg("first native ctrl socket client connected")
}
handleClient(conn)
}() }()
return listener return listener
@ -235,6 +251,51 @@ func handleVideoClient(conn net.Conn) {
} }
} }
func startNativeBinaryWithLock(binaryPath string) (*exec.Cmd, error) {
nativeCmdLock.Lock()
defer nativeCmdLock.Unlock()
cmd, err := startNativeBinary(binaryPath)
if err != nil {
return nil, err
}
nativeCmd = cmd
return cmd, nil
}
func restartNativeBinary(binaryPath string) error {
time.Sleep(10 * time.Second)
// restart the binary
nativeLogger.Info().Msg("restarting jetkvm_native binary")
cmd, err := startNativeBinary(binaryPath)
if err != nil {
nativeLogger.Warn().Err(err).Msg("failed to restart binary")
}
nativeCmd = cmd
return err
}
func superviseNativeBinary(binaryPath string) error {
nativeCmdLock.Lock()
defer nativeCmdLock.Unlock()
if nativeCmd == nil || nativeCmd.Process == nil {
return restartNativeBinary(binaryPath)
}
err := nativeCmd.Wait()
if err == nil {
nativeLogger.Info().Err(err).Msg("jetkvm_native binary exited with no error")
} else if exiterr, ok := err.(*exec.ExitError); ok {
nativeLogger.Warn().Int("exit_code", exiterr.ExitCode()).Msg("jetkvm_native binary exited with error")
} else {
nativeLogger.Warn().Err(err).Msg("jetkvm_native binary exited with unknown error")
}
return restartNativeBinary(binaryPath)
}
func ExtractAndRunNativeBin() error { func ExtractAndRunNativeBin() error {
binaryPath := "/userdata/jetkvm/bin/jetkvm_native" binaryPath := "/userdata/jetkvm/bin/jetkvm_native"
if err := ensureBinaryUpdated(binaryPath); err != nil { if err := ensureBinaryUpdated(binaryPath); err != nil {
@ -246,12 +307,28 @@ func ExtractAndRunNativeBin() error {
return fmt.Errorf("failed to make binary executable: %w", err) return fmt.Errorf("failed to make binary executable: %w", err)
} }
// Run the binary in the background // Run the binary in the background
cmd, err := startNativeBinary(binaryPath) cmd, err := startNativeBinaryWithLock(binaryPath)
if err != nil { if err != nil {
return fmt.Errorf("failed to start binary: %w", err) return fmt.Errorf("failed to start binary: %w", err)
} }
//TODO: add auto restart // check if the binary is still running every 10 seconds
go func() {
for {
select {
case <-appCtx.Done():
nativeLogger.Info().Msg("stopping native binary supervisor")
return
default:
err := superviseNativeBinary(binaryPath)
if err != nil {
nativeLogger.Warn().Err(err).Msg("failed to supervise native binary")
time.Sleep(1 * time.Second) // Add a short delay to prevent rapid successive calls
}
}
}
}()
go func() { go func() {
<-appCtx.Done() <-appCtx.Done()
nativeLogger.Info().Int("pid", cmd.Process.Pid).Msg("killing process") nativeLogger.Info().Int("pid", cmd.Process.Pid).Msg("killing process")

403
ui/package-lock.json generated
View File

@ -47,7 +47,7 @@
"@tailwindcss/forms": "^0.5.10", "@tailwindcss/forms": "^0.5.10",
"@tailwindcss/postcss": "^4.1.7", "@tailwindcss/postcss": "^4.1.7",
"@tailwindcss/typography": "^0.5.16", "@tailwindcss/typography": "^0.5.16",
"@tailwindcss/vite": "^4.1.7", "@tailwindcss/vite": "^4.1.10",
"@types/react": "^19.1.4", "@types/react": "^19.1.4",
"@types/react-dom": "^19.1.5", "@types/react-dom": "^19.1.5",
"@types/semver": "^7.7.0", "@types/semver": "^7.7.0",
@ -1767,6 +1767,66 @@
"node": ">=14.0.0" "node": ">=14.0.0"
} }
}, },
"node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/core": {
"version": "1.4.3",
"dev": true,
"inBundle": true,
"license": "MIT",
"optional": true,
"dependencies": {
"@emnapi/wasi-threads": "1.0.2",
"tslib": "^2.4.0"
}
},
"node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/runtime": {
"version": "1.4.3",
"dev": true,
"inBundle": true,
"license": "MIT",
"optional": true,
"dependencies": {
"tslib": "^2.4.0"
}
},
"node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/wasi-threads": {
"version": "1.0.2",
"dev": true,
"inBundle": true,
"license": "MIT",
"optional": true,
"dependencies": {
"tslib": "^2.4.0"
}
},
"node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@napi-rs/wasm-runtime": {
"version": "0.2.9",
"dev": true,
"inBundle": true,
"license": "MIT",
"optional": true,
"dependencies": {
"@emnapi/core": "^1.4.0",
"@emnapi/runtime": "^1.4.0",
"@tybys/wasm-util": "^0.9.0"
}
},
"node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@tybys/wasm-util": {
"version": "0.9.0",
"dev": true,
"inBundle": true,
"license": "MIT",
"optional": true,
"dependencies": {
"tslib": "^2.4.0"
}
},
"node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/tslib": {
"version": "2.8.0",
"dev": true,
"inBundle": true,
"license": "0BSD",
"optional": true
},
"node_modules/@tailwindcss/oxide-win32-arm64-msvc": { "node_modules/@tailwindcss/oxide-win32-arm64-msvc": {
"version": "4.1.7", "version": "4.1.7",
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.7.tgz", "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.7.tgz",
@ -1832,20 +1892,349 @@
} }
}, },
"node_modules/@tailwindcss/vite": { "node_modules/@tailwindcss/vite": {
"version": "4.1.7", "version": "4.1.10",
"resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.1.7.tgz", "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.1.10.tgz",
"integrity": "sha512-tYa2fO3zDe41I7WqijyVbRd8oWT0aEID1Eokz5hMT6wShLIHj3yvwj9XbfuloHP9glZ6H+aG2AN/+ZrxJ1Y5RQ==", "integrity": "sha512-QWnD5HDY2IADv+vYR82lOhqOlS1jSCUUAmfem52cXAhRTKxpDh3ARX8TTXJTCCO7Rv7cD2Nlekabv02bwP3a2A==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@tailwindcss/node": "4.1.7", "@tailwindcss/node": "4.1.10",
"@tailwindcss/oxide": "4.1.7", "@tailwindcss/oxide": "4.1.10",
"tailwindcss": "4.1.7" "tailwindcss": "4.1.10"
}, },
"peerDependencies": { "peerDependencies": {
"vite": "^5.2.0 || ^6" "vite": "^5.2.0 || ^6"
} }
}, },
"node_modules/@tailwindcss/vite/node_modules/@tailwindcss/node": {
"version": "4.1.10",
"resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.10.tgz",
"integrity": "sha512-2ACf1znY5fpRBwRhMgj9ZXvb2XZW8qs+oTfotJ2C5xR0/WNL7UHZ7zXl6s+rUqedL1mNi+0O+WQr5awGowS3PQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@ampproject/remapping": "^2.3.0",
"enhanced-resolve": "^5.18.1",
"jiti": "^2.4.2",
"lightningcss": "1.30.1",
"magic-string": "^0.30.17",
"source-map-js": "^1.2.1",
"tailwindcss": "4.1.10"
}
},
"node_modules/@tailwindcss/vite/node_modules/@tailwindcss/oxide": {
"version": "4.1.10",
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.10.tgz",
"integrity": "sha512-v0C43s7Pjw+B9w21htrQwuFObSkio2aV/qPx/mhrRldbqxbWJK6KizM+q7BF1/1CmuLqZqX3CeYF7s7P9fbA8Q==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"dependencies": {
"detect-libc": "^2.0.4",
"tar": "^7.4.3"
},
"engines": {
"node": ">= 10"
},
"optionalDependencies": {
"@tailwindcss/oxide-android-arm64": "4.1.10",
"@tailwindcss/oxide-darwin-arm64": "4.1.10",
"@tailwindcss/oxide-darwin-x64": "4.1.10",
"@tailwindcss/oxide-freebsd-x64": "4.1.10",
"@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.10",
"@tailwindcss/oxide-linux-arm64-gnu": "4.1.10",
"@tailwindcss/oxide-linux-arm64-musl": "4.1.10",
"@tailwindcss/oxide-linux-x64-gnu": "4.1.10",
"@tailwindcss/oxide-linux-x64-musl": "4.1.10",
"@tailwindcss/oxide-wasm32-wasi": "4.1.10",
"@tailwindcss/oxide-win32-arm64-msvc": "4.1.10",
"@tailwindcss/oxide-win32-x64-msvc": "4.1.10"
}
},
"node_modules/@tailwindcss/vite/node_modules/@tailwindcss/oxide-android-arm64": {
"version": "4.1.10",
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.10.tgz",
"integrity": "sha512-VGLazCoRQ7rtsCzThaI1UyDu/XRYVyH4/EWiaSX6tFglE+xZB5cvtC5Omt0OQ+FfiIVP98su16jDVHDEIuH4iQ==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@tailwindcss/vite/node_modules/@tailwindcss/oxide-darwin-arm64": {
"version": "4.1.10",
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.10.tgz",
"integrity": "sha512-ZIFqvR1irX2yNjWJzKCqTCcHZbgkSkSkZKbRM3BPzhDL/18idA8uWCoopYA2CSDdSGFlDAxYdU2yBHwAwx8euQ==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@tailwindcss/vite/node_modules/@tailwindcss/oxide-darwin-x64": {
"version": "4.1.10",
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.10.tgz",
"integrity": "sha512-eCA4zbIhWUFDXoamNztmS0MjXHSEJYlvATzWnRiTqJkcUteSjO94PoRHJy1Xbwp9bptjeIxxBHh+zBWFhttbrQ==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@tailwindcss/vite/node_modules/@tailwindcss/oxide-freebsd-x64": {
"version": "4.1.10",
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.10.tgz",
"integrity": "sha512-8/392Xu12R0cc93DpiJvNpJ4wYVSiciUlkiOHOSOQNH3adq9Gi/dtySK7dVQjXIOzlpSHjeCL89RUUI8/GTI6g==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@tailwindcss/vite/node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": {
"version": "4.1.10",
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.10.tgz",
"integrity": "sha512-t9rhmLT6EqeuPT+MXhWhlRYIMSfh5LZ6kBrC4FS6/+M1yXwfCtp24UumgCWOAJVyjQwG+lYva6wWZxrfvB+NhQ==",
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@tailwindcss/vite/node_modules/@tailwindcss/oxide-linux-arm64-gnu": {
"version": "4.1.10",
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.10.tgz",
"integrity": "sha512-3oWrlNlxLRxXejQ8zImzrVLuZ/9Z2SeKoLhtCu0hpo38hTO2iL86eFOu4sVR8cZc6n3z7eRXXqtHJECa6mFOvA==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@tailwindcss/vite/node_modules/@tailwindcss/oxide-linux-arm64-musl": {
"version": "4.1.10",
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.10.tgz",
"integrity": "sha512-saScU0cmWvg/Ez4gUmQWr9pvY9Kssxt+Xenfx1LG7LmqjcrvBnw4r9VjkFcqmbBb7GCBwYNcZi9X3/oMda9sqQ==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@tailwindcss/vite/node_modules/@tailwindcss/oxide-linux-x64-gnu": {
"version": "4.1.10",
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.10.tgz",
"integrity": "sha512-/G3ao/ybV9YEEgAXeEg28dyH6gs1QG8tvdN9c2MNZdUXYBaIY/Gx0N6RlJzfLy/7Nkdok4kaxKPHKJUlAaoTdA==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@tailwindcss/vite/node_modules/@tailwindcss/oxide-linux-x64-musl": {
"version": "4.1.10",
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.10.tgz",
"integrity": "sha512-LNr7X8fTiKGRtQGOerSayc2pWJp/9ptRYAa4G+U+cjw9kJZvkopav1AQc5HHD+U364f71tZv6XamaHKgrIoVzA==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@tailwindcss/vite/node_modules/@tailwindcss/oxide-wasm32-wasi": {
"version": "4.1.10",
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.10.tgz",
"integrity": "sha512-d6ekQpopFQJAcIK2i7ZzWOYGZ+A6NzzvQ3ozBvWFdeyqfOZdYHU66g5yr+/HC4ipP1ZgWsqa80+ISNILk+ae/Q==",
"bundleDependencies": [
"@napi-rs/wasm-runtime",
"@emnapi/core",
"@emnapi/runtime",
"@tybys/wasm-util",
"@emnapi/wasi-threads",
"tslib"
],
"cpu": [
"wasm32"
],
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
"@emnapi/core": "^1.4.3",
"@emnapi/runtime": "^1.4.3",
"@emnapi/wasi-threads": "^1.0.2",
"@napi-rs/wasm-runtime": "^0.2.10",
"@tybys/wasm-util": "^0.9.0",
"tslib": "^2.8.0"
},
"engines": {
"node": ">=14.0.0"
}
},
"node_modules/@tailwindcss/vite/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/core": {
"version": "1.4.3",
"dev": true,
"inBundle": true,
"license": "MIT",
"optional": true,
"dependencies": {
"@emnapi/wasi-threads": "1.0.2",
"tslib": "^2.4.0"
}
},
"node_modules/@tailwindcss/vite/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/runtime": {
"version": "1.4.3",
"dev": true,
"inBundle": true,
"license": "MIT",
"optional": true,
"dependencies": {
"tslib": "^2.4.0"
}
},
"node_modules/@tailwindcss/vite/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/wasi-threads": {
"version": "1.0.2",
"dev": true,
"inBundle": true,
"license": "MIT",
"optional": true,
"dependencies": {
"tslib": "^2.4.0"
}
},
"node_modules/@tailwindcss/vite/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@napi-rs/wasm-runtime": {
"version": "0.2.10",
"dev": true,
"inBundle": true,
"license": "MIT",
"optional": true,
"dependencies": {
"@emnapi/core": "^1.4.3",
"@emnapi/runtime": "^1.4.3",
"@tybys/wasm-util": "^0.9.0"
}
},
"node_modules/@tailwindcss/vite/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@tybys/wasm-util": {
"version": "0.9.0",
"dev": true,
"inBundle": true,
"license": "MIT",
"optional": true,
"dependencies": {
"tslib": "^2.4.0"
}
},
"node_modules/@tailwindcss/vite/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/tslib": {
"version": "2.8.0",
"dev": true,
"inBundle": true,
"license": "0BSD",
"optional": true
},
"node_modules/@tailwindcss/vite/node_modules/@tailwindcss/oxide-win32-arm64-msvc": {
"version": "4.1.10",
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.10.tgz",
"integrity": "sha512-i1Iwg9gRbwNVOCYmnigWCCgow8nDWSFmeTUU5nbNx3rqbe4p0kRbEqLwLJbYZKmSSp23g4N6rCDmm7OuPBXhDA==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@tailwindcss/vite/node_modules/@tailwindcss/oxide-win32-x64-msvc": {
"version": "4.1.10",
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.10.tgz",
"integrity": "sha512-sGiJTjcBSfGq2DVRtaSljq5ZgZS2SDHSIfhOylkBvHVjwOsodBhnb3HdmiKkVuUGKD0I7G63abMOVaskj1KpOA==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@tailwindcss/vite/node_modules/tailwindcss": {
"version": "4.1.10",
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.10.tgz",
"integrity": "sha512-P3nr6WkvKV/ONsTzj6Gb57sWPMX29EPNPopo7+FcpkQaNsrNpZ1pv8QmrYI2RqEKD7mlGqLnGovlcYnBK0IqUA==",
"dev": true,
"license": "MIT"
},
"node_modules/@tanstack/react-virtual": { "node_modules/@tanstack/react-virtual": {
"version": "3.13.8", "version": "3.13.8",
"resolved": "https://registry.npmjs.org/@tanstack/react-virtual/-/react-virtual-3.13.8.tgz", "resolved": "https://registry.npmjs.org/@tanstack/react-virtual/-/react-virtual-3.13.8.tgz",

View File

@ -58,7 +58,7 @@
"@tailwindcss/forms": "^0.5.10", "@tailwindcss/forms": "^0.5.10",
"@tailwindcss/postcss": "^4.1.7", "@tailwindcss/postcss": "^4.1.7",
"@tailwindcss/typography": "^0.5.16", "@tailwindcss/typography": "^0.5.16",
"@tailwindcss/vite": "^4.1.7", "@tailwindcss/vite": "^4.1.10",
"@types/react": "^19.1.4", "@types/react": "^19.1.4",
"@types/react-dom": "^19.1.5", "@types/react-dom": "^19.1.5",
"@types/semver": "^7.7.0", "@types/semver": "^7.7.0",

View File

@ -115,9 +115,18 @@ export default function WebRTCVideo() {
const isFullscreenEnabled = document.fullscreenEnabled; const isFullscreenEnabled = document.fullscreenEnabled;
const checkNavigatorPermissions = useCallback(async (permissionName: string) => { const checkNavigatorPermissions = useCallback(async (permissionName: string) => {
const name = permissionName as PermissionName; if (!navigator.permissions || !navigator.permissions.query) {
const { state } = await navigator.permissions.query({ name }); return false; // if can't query permissions, assume NOT granted
return state === "granted"; }
try {
const name = permissionName as PermissionName;
const { state } = await navigator.permissions.query({ name });
return state === "granted";
} catch {
// ignore errors
}
return false; // if query fails, assume NOT granted
}, []); }, []);
const requestPointerLock = useCallback(async () => { const requestPointerLock = useCallback(async () => {
@ -128,7 +137,11 @@ export default function WebRTCVideo() {
const isPointerLockGranted = await checkNavigatorPermissions("pointer-lock"); const isPointerLockGranted = await checkNavigatorPermissions("pointer-lock");
if (isPointerLockGranted && settings.mouseMode === "relative") { if (isPointerLockGranted && settings.mouseMode === "relative") {
await videoElm.current.requestPointerLock(); try {
await videoElm.current.requestPointerLock();
} catch {
// ignore errors
}
} }
}, [checkNavigatorPermissions, isPointerLockPossible, settings.mouseMode]); }, [checkNavigatorPermissions, isPointerLockPossible, settings.mouseMode]);
@ -136,10 +149,13 @@ export default function WebRTCVideo() {
if (videoElm.current === null) return; if (videoElm.current === null) return;
const isKeyboardLockGranted = await checkNavigatorPermissions("keyboard-lock"); const isKeyboardLockGranted = await checkNavigatorPermissions("keyboard-lock");
if (isKeyboardLockGranted) {
if ("keyboard" in navigator) { if (isKeyboardLockGranted && "keyboard" in navigator) {
try {
// @ts-expect-error - keyboard lock is not supported in all browsers // @ts-expect-error - keyboard lock is not supported in all browsers
await navigator.keyboard.lock(); await navigator.keyboard.lock();
} catch {
// ignore errors
} }
} }
}, [checkNavigatorPermissions]); }, [checkNavigatorPermissions]);
@ -148,8 +164,12 @@ export default function WebRTCVideo() {
if (videoElm.current === null || document.fullscreenElement !== videoElm.current) return; if (videoElm.current === null || document.fullscreenElement !== videoElm.current) return;
if ("keyboard" in navigator) { if ("keyboard" in navigator) {
// @ts-expect-error - keyboard unlock is not supported in all browsers try {
await navigator.keyboard.unlock(); // @ts-expect-error - keyboard unlock is not supported in all browsers
await navigator.keyboard.unlock();
} catch {
// ignore errors
}
} }
}, []); }, []);

View File

@ -39,11 +39,11 @@ export default function PasteModal() {
state => state.setKeyboardLayout, state => state.setKeyboardLayout,
); );
// this ensures we always get the original en-US if it hasn't been set yet // this ensures we always get the original en_US if it hasn't been set yet
const safeKeyboardLayout = useMemo(() => { const safeKeyboardLayout = useMemo(() => {
if (keyboardLayout && keyboardLayout.length > 0) if (keyboardLayout && keyboardLayout.length > 0)
return keyboardLayout; return keyboardLayout;
return "en-US"; return "en_US";
}, [keyboardLayout]); }, [keyboardLayout]);
useEffect(() => { useEffect(() => {

View File

@ -25,11 +25,11 @@ export default function SettingsKeyboardRoute() {
state => state.setShowPressedKeys, state => state.setShowPressedKeys,
); );
// this ensures we always get the original en-US if it hasn't been set yet // this ensures we always get the original en_US if it hasn't been set yet
const safeKeyboardLayout = useMemo(() => { const safeKeyboardLayout = useMemo(() => {
if (keyboardLayout && keyboardLayout.length > 0) if (keyboardLayout && keyboardLayout.length > 0)
return keyboardLayout; return keyboardLayout;
return "en-US"; return "en_US";
}, [keyboardLayout]); }, [keyboardLayout]);
const layoutOptions = Object.entries(layouts).map(([code, language]) => { return { value: code, label: language } }) const layoutOptions = Object.entries(layouts).map(([code, language]) => { return { value: code, label: language } })