mirror of https://github.com/jetkvm/kvm.git
Compare commits
37 Commits
9056b3711a
...
a71fc96ad7
Author | SHA1 | Date |
---|---|---|
|
a71fc96ad7 | |
|
a60e1a5e98 | |
|
4e90883bf8 | |
|
8eaa86ae45 | |
|
354941b54d | |
|
4b91c758fa | |
|
222a8470a5 | |
|
860327bfcd | |
|
a4c15d5c7e | |
|
7240abaf3d | |
|
6dd65fbba6 | |
|
9698564550 | |
|
d0759150ee | |
|
a4d6da7085 | |
|
146cee9309 | |
|
9b3d1e0417 | |
|
707a33cb07 | |
|
f8f225df6a | |
|
e10f0db3ba | |
|
7065c42e91 | |
|
8364c37f9a | |
|
dd7b2d4dcf | |
|
5fb7a2117c | |
|
cd10112ff2 | |
|
f810f09ab0 | |
|
18c7b253ca | |
|
0bf05becb4 | |
|
12f0814f8c | |
|
219573e25c | |
|
e0be7edf96 | |
|
ab94eb1da4 | |
|
33a4f38702 | |
|
c90b0425c7 | |
|
a2771f0b91 | |
|
99a5e9d385 | |
|
0bef35e044 | |
|
22849fceab |
|
@ -19,13 +19,13 @@ jobs:
|
|||
- name: Set up Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: v21.1.0
|
||||
node-version: "22"
|
||||
cache: "npm"
|
||||
cache-dependency-path: "**/package-lock.json"
|
||||
- name: Set up Golang
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: "1.24.0"
|
||||
go-version: "1.24.3"
|
||||
- name: Build frontend
|
||||
run: |
|
||||
make frontend
|
||||
|
|
|
@ -26,7 +26,7 @@ jobs:
|
|||
- name: Install Go
|
||||
uses: actions/setup-go@41dfa10bad2bb2ae585af6ee5bb4d7d973ad74ed # v5.1.0
|
||||
with:
|
||||
go-version: 1.24.x
|
||||
go-version: 1.24.3
|
||||
- name: Create empty resource directory
|
||||
run: |
|
||||
mkdir -p static && touch static/.gitkeep
|
||||
|
|
|
@ -106,7 +106,7 @@ jobs:
|
|||
- name: Set up Golang
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: "1.24.0"
|
||||
go-version: "1.24.3"
|
||||
- name: Golang Test Report
|
||||
uses: becheran/go-testreport@v0.3.2
|
||||
with:
|
||||
|
|
14
Makefile
14
Makefile
|
@ -2,8 +2,8 @@ BRANCH ?= $(shell git rev-parse --abbrev-ref HEAD)
|
|||
BUILDDATE ?= $(shell date -u +%FT%T%z)
|
||||
BUILDTS ?= $(shell date -u +%s)
|
||||
REVISION ?= $(shell git rev-parse HEAD)
|
||||
VERSION_DEV := 0.4.0-dev$(shell date +%Y%m%d%H%M)
|
||||
VERSION := 0.3.9
|
||||
VERSION_DEV := 0.4.1-dev$(shell date +%Y%m%d%H%M)
|
||||
VERSION := 0.4.0
|
||||
|
||||
PROMETHEUS_TAG := github.com/prometheus/common/version
|
||||
KVM_PKG_NAME := github.com/jetkvm/kvm
|
||||
|
@ -25,7 +25,10 @@ hash_resource:
|
|||
|
||||
build_dev: hash_resource
|
||||
@echo "Building..."
|
||||
$(GO_CMD) build -ldflags="$(GO_LDFLAGS) -X $(KVM_PKG_NAME).builtAppVersion=$(VERSION_DEV)" -o $(BIN_DIR)/jetkvm_app cmd/main.go
|
||||
$(GO_CMD) build \
|
||||
-ldflags="$(GO_LDFLAGS) -X $(KVM_PKG_NAME).builtAppVersion=$(VERSION_DEV)" \
|
||||
-trimpath \
|
||||
-o $(BIN_DIR)/jetkvm_app cmd/main.go
|
||||
|
||||
build_test2json:
|
||||
$(GO_CMD) build -o $(BIN_DIR)/test2json cmd/test2json
|
||||
|
@ -66,7 +69,10 @@ dev_release: frontend build_dev
|
|||
|
||||
build_release: frontend hash_resource
|
||||
@echo "Building release..."
|
||||
$(GO_CMD) build -ldflags="$(GO_LDFLAGS) -X $(KVM_PKG_NAME).builtAppVersion=$(VERSION)" -o bin/jetkvm_app cmd/main.go
|
||||
$(GO_CMD) build \
|
||||
-ldflags="$(GO_LDFLAGS) -X $(KVM_PKG_NAME).builtAppVersion=$(VERSION)" \
|
||||
-trimpath \
|
||||
-o bin/jetkvm_app cmd/main.go
|
||||
|
||||
release:
|
||||
@if rclone lsf r2://jetkvm-update/app/$(VERSION)/ | grep -q "jetkvm_app"; then \
|
||||
|
|
|
@ -87,6 +87,7 @@ type Config struct {
|
|||
LocalAuthMode string `json:"localAuthMode"` //TODO: fix it with migration
|
||||
WakeOnLanDevices []WakeOnLanDevice `json:"wake_on_lan_devices"`
|
||||
KeyboardMacros []KeyboardMacro `json:"keyboard_macros"`
|
||||
KeyboardLayout string `json:"keyboard_layout"`
|
||||
EdidString string `json:"hdmi_edid_string"`
|
||||
ActiveExtension string `json:"active_extension"`
|
||||
DisplayRotation string `json:"display_rotation"`
|
||||
|
@ -109,6 +110,7 @@ var defaultConfig = &Config{
|
|||
ActiveExtension: "",
|
||||
KeyboardMacros: []KeyboardMacro{},
|
||||
DisplayRotation: "270",
|
||||
KeyboardLayout: "en-US",
|
||||
DisplayMaxBrightness: 64,
|
||||
DisplayDimAfterSec: 120, // 2 minutes
|
||||
DisplayOffAfterSec: 1800, // 30 minutes
|
||||
|
|
21
jsonrpc.go
21
jsonrpc.go
|
@ -266,8 +266,13 @@ func rpcSetDevChannelState(enabled bool) error {
|
|||
func rpcGetUpdateStatus() (*UpdateStatus, error) {
|
||||
includePreRelease := config.IncludePreRelease
|
||||
updateStatus, err := GetUpdateStatus(context.Background(), GetDeviceID(), includePreRelease)
|
||||
// to ensure backwards compatibility,
|
||||
// if there's an error, we won't return an error, but we will set the error field
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error checking for updates: %w", err)
|
||||
if updateStatus == nil {
|
||||
return nil, fmt.Errorf("error checking for updates: %w", err)
|
||||
}
|
||||
updateStatus.Error = err.Error()
|
||||
}
|
||||
|
||||
return updateStatus, nil
|
||||
|
@ -877,6 +882,18 @@ func rpcSetCloudUrl(apiUrl string, appUrl string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func rpcGetKeyboardLayout() (string, error) {
|
||||
return config.KeyboardLayout, nil
|
||||
}
|
||||
|
||||
func rpcSetKeyboardLayout(layout string) error {
|
||||
config.KeyboardLayout = layout
|
||||
if err := SaveConfig(); err != nil {
|
||||
return fmt.Errorf("failed to save config: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func getKeyboardMacros() (interface{}, error) {
|
||||
macros := make([]KeyboardMacro, len(config.KeyboardMacros))
|
||||
copy(macros, config.KeyboardMacros)
|
||||
|
@ -1042,6 +1059,8 @@ var rpcHandlers = map[string]RPCHandler{
|
|||
"setUsbDevices": {Func: rpcSetUsbDevices, Params: []string{"devices"}},
|
||||
"setUsbDeviceState": {Func: rpcSetUsbDeviceState, Params: []string{"device", "enabled"}},
|
||||
"setCloudUrl": {Func: rpcSetCloudUrl, Params: []string{"apiUrl", "appUrl"}},
|
||||
"getKeyboardLayout": {Func: rpcGetKeyboardLayout},
|
||||
"setKeyboardLayout": {Func: rpcSetKeyboardLayout, Params: []string{"layout"}},
|
||||
"getKeyboardMacros": {Func: getKeyboardMacros},
|
||||
"setKeyboardMacros": {Func: setKeyboardMacros, Params: []string{"params"}},
|
||||
}
|
||||
|
|
40
ota.go
40
ota.go
|
@ -41,6 +41,9 @@ type UpdateStatus struct {
|
|||
Remote *UpdateMetadata `json:"remote"`
|
||||
SystemUpdateAvailable bool `json:"systemUpdateAvailable"`
|
||||
AppUpdateAvailable bool `json:"appUpdateAvailable"`
|
||||
|
||||
// for backwards compatibility
|
||||
Error string `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
const UpdateMetadataUrl = "https://api.jetkvm.com/releases"
|
||||
|
@ -489,52 +492,47 @@ func TryUpdate(ctx context.Context, deviceId string, includePreRelease bool) err
|
|||
}
|
||||
|
||||
func GetUpdateStatus(ctx context.Context, deviceId string, includePreRelease bool) (*UpdateStatus, error) {
|
||||
updateStatus := &UpdateStatus{}
|
||||
|
||||
// Get local versions
|
||||
systemVersionLocal, appVersionLocal, err := GetLocalVersion()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error getting local version: %w", err)
|
||||
return updateStatus, fmt.Errorf("error getting local version: %w", err)
|
||||
}
|
||||
updateStatus.Local = &LocalMetadata{
|
||||
AppVersion: appVersionLocal.String(),
|
||||
SystemVersion: systemVersionLocal.String(),
|
||||
}
|
||||
|
||||
// Get remote metadata
|
||||
remoteMetadata, err := fetchUpdateMetadata(ctx, deviceId, includePreRelease)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error checking for updates: %w", err)
|
||||
}
|
||||
|
||||
// Build local UpdateMetadata
|
||||
localMetadata := &LocalMetadata{
|
||||
AppVersion: appVersionLocal.String(),
|
||||
SystemVersion: systemVersionLocal.String(),
|
||||
return updateStatus, fmt.Errorf("error checking for updates: %w", err)
|
||||
}
|
||||
updateStatus.Remote = remoteMetadata
|
||||
|
||||
// Get remote versions
|
||||
systemVersionRemote, err := semver.NewVersion(remoteMetadata.SystemVersion)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error parsing remote system version: %w", err)
|
||||
return updateStatus, fmt.Errorf("error parsing remote system version: %w", err)
|
||||
}
|
||||
appVersionRemote, err := semver.NewVersion(remoteMetadata.AppVersion)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error parsing remote app version: %w, %s", err, remoteMetadata.AppVersion)
|
||||
return updateStatus, fmt.Errorf("error parsing remote app version: %w, %s", err, remoteMetadata.AppVersion)
|
||||
}
|
||||
|
||||
systemUpdateAvailable := systemVersionRemote.GreaterThan(systemVersionLocal)
|
||||
appUpdateAvailable := appVersionRemote.GreaterThan(appVersionLocal)
|
||||
updateStatus.SystemUpdateAvailable = systemVersionRemote.GreaterThan(systemVersionLocal)
|
||||
updateStatus.AppUpdateAvailable = appVersionRemote.GreaterThan(appVersionLocal)
|
||||
|
||||
// Handle pre-release updates
|
||||
isRemoteSystemPreRelease := systemVersionRemote.Prerelease() != ""
|
||||
isRemoteAppPreRelease := appVersionRemote.Prerelease() != ""
|
||||
|
||||
if isRemoteSystemPreRelease && !includePreRelease {
|
||||
systemUpdateAvailable = false
|
||||
updateStatus.SystemUpdateAvailable = false
|
||||
}
|
||||
if isRemoteAppPreRelease && !includePreRelease {
|
||||
appUpdateAvailable = false
|
||||
}
|
||||
|
||||
updateStatus := &UpdateStatus{
|
||||
Local: localMetadata,
|
||||
Remote: remoteMetadata,
|
||||
SystemUpdateAvailable: systemUpdateAvailable,
|
||||
AppUpdateAvailable: appUpdateAvailable,
|
||||
updateStatus.AppUpdateAvailable = false
|
||||
}
|
||||
|
||||
return updateStatus, nil
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
"arrowParens": "avoid",
|
||||
"singleQuote": false,
|
||||
"plugins": ["prettier-plugin-tailwindcss"],
|
||||
"tailwindFunctions": ["clsx"],
|
||||
"printWidth": 90
|
||||
"tailwindFunctions": ["clsx", "cx"],
|
||||
"printWidth": 90,
|
||||
"tailwindStylesheet": "./src/index.css"
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@ const AutoHeight = ({ children, ...props }: { children: React.ReactNode }) => {
|
|||
{...props}
|
||||
height={height}
|
||||
duration={300}
|
||||
contentClassName="h-fit"
|
||||
contentClassName="h-fit p-px"
|
||||
contentRef={contentDiv}
|
||||
disableDisplayNone
|
||||
>
|
||||
|
|
|
@ -12,7 +12,7 @@ const sizes = {
|
|||
|
||||
const checkboxVariants = cva({
|
||||
base: cx(
|
||||
"block rounded",
|
||||
"form-checkbox block rounded",
|
||||
|
||||
// Colors
|
||||
"border-slate-300 dark:border-slate-600 bg-slate-50 dark:bg-slate-800 checked:accent-blue-700 checked:dark:accent-blue-500 transition-colors",
|
||||
|
|
|
@ -0,0 +1,212 @@
|
|||
import { LuRefreshCcw } from "react-icons/lu";
|
||||
|
||||
import { Button } from "@/components/Button";
|
||||
import { GridCard } from "@/components/Card";
|
||||
import { LifeTimeLabel } from "@/routes/devices.$id.settings.network";
|
||||
import { NetworkState } from "@/hooks/stores";
|
||||
|
||||
export default function DhcpLeaseCard({
|
||||
networkState,
|
||||
setShowRenewLeaseConfirm,
|
||||
}: {
|
||||
networkState: NetworkState;
|
||||
setShowRenewLeaseConfirm: (show: boolean) => void;
|
||||
}) {
|
||||
return (
|
||||
<GridCard>
|
||||
<div className="animate-fadeIn p-4 opacity-0 animation-duration-500 text-black dark:text-white">
|
||||
<div className="space-y-3">
|
||||
<h3 className="text-base font-bold text-slate-900 dark:text-white">
|
||||
DHCP Lease Information
|
||||
</h3>
|
||||
|
||||
<div className="flex gap-x-6 gap-y-2">
|
||||
<div className="flex-1 space-y-2">
|
||||
{networkState?.dhcp_lease?.ip && (
|
||||
<div className="flex justify-between border-slate-800/10 pt-2 dark:border-slate-300/20">
|
||||
<span className="text-sm text-slate-600 dark:text-slate-400">
|
||||
IP Address
|
||||
</span>
|
||||
<span className="text-sm font-medium">
|
||||
{networkState?.dhcp_lease?.ip}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{networkState?.dhcp_lease?.netmask && (
|
||||
<div className="flex justify-between border-t border-slate-800/10 pt-2 dark:border-slate-300/20">
|
||||
<span className="text-sm text-slate-600 dark:text-slate-400">
|
||||
Subnet Mask
|
||||
</span>
|
||||
<span className="text-sm font-medium">
|
||||
{networkState?.dhcp_lease?.netmask}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{networkState?.dhcp_lease?.dns && (
|
||||
<div className="flex justify-between border-t border-slate-800/10 pt-2 dark:border-slate-300/20">
|
||||
<span className="text-sm text-slate-600 dark:text-slate-400">
|
||||
DNS Servers
|
||||
</span>
|
||||
<span className="text-right text-sm font-medium">
|
||||
{networkState?.dhcp_lease?.dns.map(dns => <div key={dns}>{dns}</div>)}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{networkState?.dhcp_lease?.broadcast && (
|
||||
<div className="flex justify-between border-t border-slate-800/10 pt-2 dark:border-slate-300/20">
|
||||
<span className="text-sm text-slate-600 dark:text-slate-400">
|
||||
Broadcast
|
||||
</span>
|
||||
<span className="text-sm font-medium">
|
||||
{networkState?.dhcp_lease?.broadcast}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{networkState?.dhcp_lease?.domain && (
|
||||
<div className="flex justify-between border-t border-slate-800/10 pt-2 dark:border-slate-300/20">
|
||||
<span className="text-sm text-slate-600 dark:text-slate-400">
|
||||
Domain
|
||||
</span>
|
||||
<span className="text-sm font-medium">
|
||||
{networkState?.dhcp_lease?.domain}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{networkState?.dhcp_lease?.ntp_servers &&
|
||||
networkState?.dhcp_lease?.ntp_servers.length > 0 && (
|
||||
<div className="flex justify-between gap-x-8 border-t border-slate-800/10 pt-2 dark:border-slate-300/20">
|
||||
<div className="w-full grow text-sm text-slate-600 dark:text-slate-400">
|
||||
NTP Servers
|
||||
</div>
|
||||
<div className="shrink text-right text-sm font-medium">
|
||||
{networkState?.dhcp_lease?.ntp_servers.map(server => (
|
||||
<div key={server}>{server}</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{networkState?.dhcp_lease?.hostname && (
|
||||
<div className="flex justify-between border-t border-slate-800/10 pt-2 dark:border-slate-300/20">
|
||||
<span className="text-sm text-slate-600 dark:text-slate-400">
|
||||
Hostname
|
||||
</span>
|
||||
<span className="text-sm font-medium">
|
||||
{networkState?.dhcp_lease?.hostname}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="flex-1 space-y-2">
|
||||
{networkState?.dhcp_lease?.routers &&
|
||||
networkState?.dhcp_lease?.routers.length > 0 && (
|
||||
<div className="flex justify-between pt-2">
|
||||
<span className="text-sm text-slate-600 dark:text-slate-400">
|
||||
Gateway
|
||||
</span>
|
||||
<span className="text-right text-sm font-medium">
|
||||
{networkState?.dhcp_lease?.routers.map(router => (
|
||||
<div key={router}>{router}</div>
|
||||
))}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{networkState?.dhcp_lease?.server_id && (
|
||||
<div className="flex justify-between border-t border-slate-800/10 pt-2 dark:border-slate-300/20">
|
||||
<span className="text-sm text-slate-600 dark:text-slate-400">
|
||||
DHCP Server
|
||||
</span>
|
||||
<span className="text-sm font-medium">
|
||||
{networkState?.dhcp_lease?.server_id}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{networkState?.dhcp_lease?.lease_expiry && (
|
||||
<div className="flex justify-between border-t border-slate-800/10 pt-2 dark:border-slate-300/20">
|
||||
<span className="text-sm text-slate-600 dark:text-slate-400">
|
||||
Lease Expires
|
||||
</span>
|
||||
<span className="text-sm font-medium">
|
||||
<LifeTimeLabel
|
||||
lifetime={`${networkState?.dhcp_lease?.lease_expiry}`}
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{networkState?.dhcp_lease?.mtu && (
|
||||
<div className="flex justify-between border-t border-slate-800/10 pt-2 dark:border-slate-300/20">
|
||||
<span className="text-sm text-slate-600 dark:text-slate-400">MTU</span>
|
||||
<span className="text-sm font-medium">
|
||||
{networkState?.dhcp_lease?.mtu}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{networkState?.dhcp_lease?.ttl && (
|
||||
<div className="flex justify-between border-t border-slate-800/10 pt-2 dark:border-slate-300/20">
|
||||
<span className="text-sm text-slate-600 dark:text-slate-400">TTL</span>
|
||||
<span className="text-sm font-medium">
|
||||
{networkState?.dhcp_lease?.ttl}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{networkState?.dhcp_lease?.bootp_next_server && (
|
||||
<div className="flex justify-between border-t border-slate-800/10 pt-2 dark:border-slate-300/20">
|
||||
<span className="text-sm text-slate-600 dark:text-slate-400">
|
||||
Boot Next Server
|
||||
</span>
|
||||
<span className="text-sm font-medium">
|
||||
{networkState?.dhcp_lease?.bootp_next_server}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{networkState?.dhcp_lease?.bootp_server_name && (
|
||||
<div className="flex justify-between border-t border-slate-800/10 pt-2 dark:border-slate-300/20">
|
||||
<span className="text-sm text-slate-600 dark:text-slate-400">
|
||||
Boot Server Name
|
||||
</span>
|
||||
<span className="text-sm font-medium">
|
||||
{networkState?.dhcp_lease?.bootp_server_name}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{networkState?.dhcp_lease?.bootp_file && (
|
||||
<div className="flex justify-between border-t border-slate-800/10 pt-2 dark:border-slate-300/20">
|
||||
<span className="text-sm text-slate-600 dark:text-slate-400">
|
||||
Boot File
|
||||
</span>
|
||||
<span className="text-sm font-medium">
|
||||
{networkState?.dhcp_lease?.bootp_file}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Button
|
||||
size="SM"
|
||||
theme="light"
|
||||
className="text-red-500"
|
||||
text="Renew DHCP Lease"
|
||||
LeadingIcon={LuRefreshCcw}
|
||||
onClick={() => setShowRenewLeaseConfirm(true)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</GridCard>
|
||||
);
|
||||
}
|
|
@ -0,0 +1,93 @@
|
|||
import { NetworkState } from "../hooks/stores";
|
||||
import { LifeTimeLabel } from "../routes/devices.$id.settings.network";
|
||||
|
||||
import { GridCard } from "./Card";
|
||||
|
||||
export default function Ipv6NetworkCard({
|
||||
networkState,
|
||||
}: {
|
||||
networkState: NetworkState;
|
||||
}) {
|
||||
return (
|
||||
<GridCard>
|
||||
<div className="animate-fadeIn p-4 text-black opacity-0 animation-duration-500 dark:text-white">
|
||||
<div className="space-y-4">
|
||||
<h3 className="text-base font-bold text-slate-900 dark:text-white">
|
||||
IPv6 Information
|
||||
</h3>
|
||||
|
||||
<div className="grid grid-cols-2 gap-x-6 gap-y-2">
|
||||
{networkState?.dhcp_lease?.ip && (
|
||||
<div className="flex flex-col justify-between">
|
||||
<span className="text-sm text-slate-600 dark:text-slate-400">
|
||||
Link-local
|
||||
</span>
|
||||
<span className="text-sm font-medium">
|
||||
{networkState?.ipv6_link_local}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="space-y-3 pt-2">
|
||||
{networkState?.ipv6_addresses && networkState?.ipv6_addresses.length > 0 && (
|
||||
<div className="space-y-3">
|
||||
<h4 className="text-sm font-semibold">IPv6 Addresses</h4>
|
||||
{networkState.ipv6_addresses.map(
|
||||
addr => (
|
||||
<div
|
||||
key={addr.address}
|
||||
className="rounded-md rounded-l-none border border-slate-500/10 border-l-blue-700/50 bg-white p-4 pl-4 backdrop-blur-sm dark:bg-transparent"
|
||||
>
|
||||
<div className="grid grid-cols-2 gap-x-8 gap-y-4">
|
||||
<div className="col-span-2 flex flex-col justify-between">
|
||||
<span className="text-sm text-slate-600 dark:text-slate-400">
|
||||
Address
|
||||
</span>
|
||||
<span className="text-sm font-medium">{addr.address}</span>
|
||||
</div>
|
||||
|
||||
{addr.valid_lifetime && (
|
||||
<div className="flex flex-col justify-between">
|
||||
<span className="text-sm text-slate-600 dark:text-slate-400">
|
||||
Valid Lifetime
|
||||
</span>
|
||||
<span className="text-sm font-medium">
|
||||
{addr.valid_lifetime === "" ? (
|
||||
<span className="text-slate-400 dark:text-slate-600">
|
||||
N/A
|
||||
</span>
|
||||
) : (
|
||||
<LifeTimeLabel lifetime={`${addr.valid_lifetime}`} />
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
{addr.preferred_lifetime && (
|
||||
<div className="flex flex-col justify-between">
|
||||
<span className="text-sm text-slate-600 dark:text-slate-400">
|
||||
Preferred Lifetime
|
||||
</span>
|
||||
<span className="text-sm font-medium">
|
||||
{addr.preferred_lifetime === "" ? (
|
||||
<span className="text-slate-400 dark:text-slate-600">
|
||||
N/A
|
||||
</span>
|
||||
) : (
|
||||
<LifeTimeLabel lifetime={`${addr.preferred_lifetime}`} />
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</GridCard>
|
||||
);
|
||||
}
|
|
@ -20,7 +20,9 @@ const Modal = React.memo(function Modal({
|
|||
transition
|
||||
className="fixed inset-0 bg-gray-500/75 transition-opacity data-closed:opacity-0 data-enter:duration-500 data-leave:duration-200 data-enter:ease-out data-leave:ease-in dark:bg-slate-900/90"
|
||||
/>
|
||||
<div className="fixed inset-0 z-20 w-screen overflow-y-auto">
|
||||
<div className="fixed inset-0 z-20 w-screen overflow-y-auto" style={{
|
||||
scrollbarGutter: 'stable'
|
||||
}}>
|
||||
{/* TODO: This doesn't work well with other-sessions */}
|
||||
<div className="flex min-h-full items-end justify-center p-4 text-center md:items-baseline md:p-4">
|
||||
<DialogPanel
|
||||
|
|
|
@ -107,7 +107,7 @@ export function ATXPowerControl() {
|
|||
<LoadingSpinner className="h-6 w-6 text-blue-500 dark:text-blue-400" />
|
||||
</Card>
|
||||
) : (
|
||||
<Card className="h-[120px] animate-fadeIn">
|
||||
<Card className="h-[120px] animate-fadeIn opacity-0">
|
||||
<div className="space-y-4 p-3">
|
||||
{/* Control Buttons */}
|
||||
<div className="flex items-center space-x-2">
|
||||
|
|
|
@ -63,7 +63,7 @@ export function DCPowerControl() {
|
|||
<LoadingSpinner className="h-6 w-6 text-blue-500 dark:text-blue-400" />
|
||||
</Card>
|
||||
) : (
|
||||
<Card className="h-[160px] animate-fadeIn">
|
||||
<Card className="h-[160px] animate-fadeIn opacity-0">
|
||||
<div className="space-y-4 p-3">
|
||||
{/* Power Controls */}
|
||||
<div className="flex items-center space-x-2">
|
||||
|
|
|
@ -58,7 +58,7 @@ export function SerialConsole() {
|
|||
description="Configure your serial console settings"
|
||||
/>
|
||||
|
||||
<Card className="animate-fadeIn">
|
||||
<Card className="animate-fadeIn opacity-0">
|
||||
<div className="space-y-4 p-3">
|
||||
{/* Open Console Button */}
|
||||
<div className="flex items-center">
|
||||
|
|
|
@ -92,7 +92,7 @@ export default function ExtensionPopover() {
|
|||
{renderActiveExtension()}
|
||||
|
||||
<div
|
||||
className="flex animate-fadeIn items-center justify-end space-x-2"
|
||||
className="flex animate-fadeIn opacity-0 items-center justify-end space-x-2"
|
||||
style={{
|
||||
animationDuration: "0.7s",
|
||||
animationDelay: "0.2s",
|
||||
|
@ -113,7 +113,7 @@ export default function ExtensionPopover() {
|
|||
title="Extensions"
|
||||
description="Load and manage your extensions"
|
||||
/>
|
||||
<Card className="animate-fadeIn">
|
||||
<Card className="animate-fadeIn opacity-0" >
|
||||
<div className="w-full divide-y divide-slate-700/30 dark:divide-slate-600/30">
|
||||
{AVAILABLE_EXTENSIONS.map(extension => (
|
||||
<div
|
||||
|
|
|
@ -214,7 +214,7 @@ const MountPopopover = forwardRef<HTMLDivElement, object>((_props, ref) => {
|
|||
) : null}
|
||||
|
||||
<div
|
||||
className="animate-fadeIn space-y-2"
|
||||
className="animate-fadeIn opacity-0 space-y-2"
|
||||
style={{
|
||||
animationDuration: "0.7s",
|
||||
animationDelay: "0.1s",
|
||||
|
@ -289,7 +289,7 @@ const MountPopopover = forwardRef<HTMLDivElement, object>((_props, ref) => {
|
|||
|
||||
{!remoteVirtualMediaState && (
|
||||
<div
|
||||
className="flex animate-fadeIn items-center justify-end space-x-2"
|
||||
className="flex animate-fadeIn opacity-0 items-center justify-end space-x-2"
|
||||
style={{
|
||||
animationDuration: "0.7s",
|
||||
animationDelay: "0.2s",
|
||||
|
|
|
@ -8,14 +8,21 @@ import { GridCard } from "@components/Card";
|
|||
import { TextAreaWithLabel } from "@components/TextArea";
|
||||
import { SettingsPageHeader } from "@components/SettingsPageheader";
|
||||
import { useJsonRpc } from "@/hooks/useJsonRpc";
|
||||
import { useHidStore, useRTCStore, useUiStore } from "@/hooks/stores";
|
||||
import { chars, keys, modifiers } from "@/keyboardMappings";
|
||||
import { useHidStore, useRTCStore, useUiStore, useSettingsStore } from "@/hooks/stores";
|
||||
import { keys, modifiers } from "@/keyboardMappings";
|
||||
import { layouts, chars } from "@/keyboardLayouts";
|
||||
import notifications from "@/notifications";
|
||||
|
||||
const hidKeyboardPayload = (keys: number[], modifier: number) => {
|
||||
return { keys, modifier };
|
||||
};
|
||||
|
||||
const modifierCode = (shift?: boolean, altRight?: boolean) => {
|
||||
return (shift ? modifiers["ShiftLeft"] : 0)
|
||||
| (altRight ? modifiers["AltRight"] : 0)
|
||||
}
|
||||
const noModifier = 0
|
||||
|
||||
export default function PasteModal() {
|
||||
const TextAreaRef = useRef<HTMLTextAreaElement>(null);
|
||||
const setPasteMode = useHidStore(state => state.setPasteModeEnabled);
|
||||
|
@ -27,6 +34,18 @@ export default function PasteModal() {
|
|||
const [invalidChars, setInvalidChars] = useState<string[]>([]);
|
||||
const close = useClose();
|
||||
|
||||
const keyboardLayout = useSettingsStore(state => state.keyboardLayout);
|
||||
const setKeyboardLayout = useSettingsStore(
|
||||
state => state.setKeyboardLayout,
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
send("getKeyboardLayout", {}, resp => {
|
||||
if ("error" in resp) return;
|
||||
setKeyboardLayout(resp.result as string);
|
||||
});
|
||||
}, []);
|
||||
|
||||
const onCancelPasteMode = useCallback(() => {
|
||||
setPasteMode(false);
|
||||
setDisableVideoFocusTrap(false);
|
||||
|
@ -37,27 +56,43 @@ export default function PasteModal() {
|
|||
setPasteMode(false);
|
||||
setDisableVideoFocusTrap(false);
|
||||
if (rpcDataChannel?.readyState !== "open" || !TextAreaRef.current) return;
|
||||
if (!keyboardLayout) return;
|
||||
if (!chars[keyboardLayout]) return;
|
||||
|
||||
const text = TextAreaRef.current.value;
|
||||
|
||||
try {
|
||||
for (const char of text) {
|
||||
const { key, shift } = chars[char] ?? {};
|
||||
const { key, shift, altRight, deadKey, accentKey } = chars[keyboardLayout][char]
|
||||
if (!key) continue;
|
||||
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
send(
|
||||
"keyboardReport",
|
||||
hidKeyboardPayload([keys[key]], shift ? modifiers["ShiftLeft"] : 0),
|
||||
params => {
|
||||
if ("error" in params) return reject(params.error);
|
||||
send("keyboardReport", hidKeyboardPayload([], 0), params => {
|
||||
const keyz = [ keys[key] ];
|
||||
const modz = [ modifierCode(shift, altRight) ];
|
||||
|
||||
if (deadKey) {
|
||||
keyz.push(keys["Space"]);
|
||||
modz.push(noModifier);
|
||||
}
|
||||
if (accentKey) {
|
||||
keyz.unshift(keys[accentKey.key])
|
||||
modz.unshift(modifierCode(accentKey.shift, accentKey.altRight))
|
||||
}
|
||||
|
||||
for (const [index, kei] of keyz.entries()) {
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
send(
|
||||
"keyboardReport",
|
||||
hidKeyboardPayload([kei], modz[index]),
|
||||
params => {
|
||||
if ("error" in params) return reject(params.error);
|
||||
resolve();
|
||||
});
|
||||
},
|
||||
);
|
||||
});
|
||||
send("keyboardReport", hidKeyboardPayload([], 0), params => {
|
||||
if ("error" in params) return reject(params.error);
|
||||
resolve();
|
||||
});
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
|
@ -83,7 +118,7 @@ export default function PasteModal() {
|
|||
/>
|
||||
|
||||
<div
|
||||
className="animate-fadeIn space-y-2"
|
||||
className="animate-fadeIn opacity-0 space-y-2"
|
||||
style={{
|
||||
animationDuration: "0.7s",
|
||||
animationDelay: "0.1s",
|
||||
|
@ -113,7 +148,7 @@ export default function PasteModal() {
|
|||
// @ts-expect-error TS doesn't recognize Intl.Segmenter in some environments
|
||||
[...new Intl.Segmenter().segment(value)]
|
||||
.map(x => x.segment)
|
||||
.filter(char => !chars[char]),
|
||||
.filter(char => !chars[keyboardLayout][char]),
|
||||
),
|
||||
];
|
||||
|
||||
|
@ -132,12 +167,17 @@ export default function PasteModal() {
|
|||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="space-y-4">
|
||||
<p className="text-xs text-slate-600 dark:text-slate-400">
|
||||
Sending key codes using keyboard layout {layouts[keyboardLayout]}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="flex animate-fadeIn items-center justify-end gap-x-2"
|
||||
className="flex animate-fadeIn opacity-0 items-center justify-end gap-x-2"
|
||||
style={{
|
||||
animationDuration: "0.7s",
|
||||
animationDelay: "0.2s",
|
||||
|
|
|
@ -26,7 +26,7 @@ export default function AddDeviceForm({
|
|||
return (
|
||||
<div className="space-y-4">
|
||||
<div
|
||||
className="animate-fadeIn space-y-4"
|
||||
className="animate-fadeIn opacity-0 space-y-4"
|
||||
style={{
|
||||
animationDuration: "0.5s",
|
||||
animationFillMode: "forwards",
|
||||
|
@ -73,7 +73,7 @@ export default function AddDeviceForm({
|
|||
/>
|
||||
</div>
|
||||
<div
|
||||
className="flex animate-fadeIn items-center justify-end space-x-2"
|
||||
className="flex animate-fadeIn opacity-0 items-center justify-end space-x-2"
|
||||
style={{
|
||||
animationDuration: "0.7s",
|
||||
animationDelay: "0.2s",
|
||||
|
|
|
@ -28,7 +28,7 @@ export default function DeviceList({
|
|||
}: DeviceListProps) {
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<Card className="animate-fadeIn">
|
||||
<Card className="animate-fadeIn opacity-0">
|
||||
<div className="w-full divide-y divide-slate-700/30 dark:divide-slate-600/30">
|
||||
{storedDevices.map((device, index) => (
|
||||
<div key={index} className="flex items-center justify-between gap-x-2 p-3">
|
||||
|
@ -63,7 +63,7 @@ export default function DeviceList({
|
|||
</div>
|
||||
</Card>
|
||||
<div
|
||||
className="flex animate-fadeIn items-center justify-end space-x-2"
|
||||
className="flex animate-fadeIn opacity-0 items-center justify-end space-x-2"
|
||||
style={{
|
||||
animationDuration: "0.7s",
|
||||
animationDelay: "0.2s",
|
||||
|
|
|
@ -13,7 +13,7 @@ export default function EmptyStateCard({
|
|||
}) {
|
||||
return (
|
||||
<div className="select-none space-y-4">
|
||||
<Card className="animate-fadeIn">
|
||||
<Card className="animate-fadeIn opacity-0">
|
||||
<div className="flex items-center justify-center py-8 text-center">
|
||||
<div className="space-y-3">
|
||||
<div className="space-y-1">
|
||||
|
@ -35,7 +35,7 @@ export default function EmptyStateCard({
|
|||
</div>
|
||||
</Card>
|
||||
<div
|
||||
className="flex animate-fadeIn items-center justify-end space-x-2"
|
||||
className="flex animate-fadeIn opacity-0 items-center justify-end space-x-2"
|
||||
style={{
|
||||
animationDuration: "0.7s",
|
||||
animationDelay: "0.2s",
|
||||
|
|
|
@ -302,6 +302,9 @@ interface SettingsState {
|
|||
|
||||
backlightSettings: BacklightSettings;
|
||||
setBacklightSettings: (settings: BacklightSettings) => void;
|
||||
|
||||
keyboardLayout: string;
|
||||
setKeyboardLayout: (layout: string) => void;
|
||||
}
|
||||
|
||||
export const useSettingsStore = create(
|
||||
|
@ -330,6 +333,9 @@ export const useSettingsStore = create(
|
|||
},
|
||||
setBacklightSettings: (settings: BacklightSettings) =>
|
||||
set({ backlightSettings: settings }),
|
||||
|
||||
keyboardLayout: "en-US",
|
||||
setKeyboardLayout: layout => set({ keyboardLayout: layout }),
|
||||
}),
|
||||
{
|
||||
name: "settings",
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
@import 'tailwindcss';
|
||||
@import "tailwindcss";
|
||||
|
||||
@config "../tailwind.config.js";
|
||||
|
||||
|
@ -10,10 +10,10 @@
|
|||
@custom-variant dark (&:where(.dark, .dark *));
|
||||
|
||||
@theme {
|
||||
--font-sans: 'Circular', sans-serif;
|
||||
--font-display: 'Circular', sans-serif;
|
||||
--font-serif: 'Circular', serif;
|
||||
--font-mono: 'Source Code Pro Variable', monospace;
|
||||
--font-sans: "Circular", sans-serif;
|
||||
--font-display: "Circular", sans-serif;
|
||||
--font-serif: "Circular", serif;
|
||||
--font-mono: "Source Code Pro Variable", monospace;
|
||||
|
||||
--grid-layout: auto 1fr auto;
|
||||
--grid-headerBody: auto 1fr;
|
||||
|
@ -122,13 +122,22 @@
|
|||
}
|
||||
}
|
||||
|
||||
/* If we don't ignore this, Prettier will add a space between the value and the `ms`. Rendering the utility invalid. */
|
||||
/* prettier-ignore */
|
||||
@utility max-width-* {
|
||||
max-width: --modifier(--container- *, [length], [ *]);
|
||||
max-width: --modifier(--container-*, [length], [*]);
|
||||
}
|
||||
|
||||
/* Ensure there is not a `ms` and ms -> `...)ms` */
|
||||
/* If we don't ignore this, Prettier will add a space between the value and the `ms`. Rendering the utility invalid. */
|
||||
/* prettier-ignore */
|
||||
@utility animation-delay-* {
|
||||
animation-delay: --value(integer) ms;
|
||||
animation-delay: --value(integer)ms;
|
||||
}
|
||||
|
||||
/* If we don't ignore this, Prettier will add a space between the value and the `ms`. Rendering the utility invalid. */
|
||||
/* prettier-ignore */
|
||||
@utility animation-duration-* {
|
||||
animation-duration: --value(integer)ms;
|
||||
}
|
||||
|
||||
html {
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
import { chars as chars_fr_BE, name as name_fr_BE } from "@/keyboardLayouts/fr_BE"
|
||||
import { chars as chars_cs_CZ, name as name_cs_CZ } from "@/keyboardLayouts/cs_CZ"
|
||||
import { chars as chars_en_UK, name as name_en_UK } from "@/keyboardLayouts/en_UK"
|
||||
import { chars as chars_en_US, name as name_en_US } from "@/keyboardLayouts/en_US"
|
||||
import { chars as chars_fr_FR, name as name_fr_FR } from "@/keyboardLayouts/fr_FR"
|
||||
import { chars as chars_de_DE, name as name_de_DE } from "@/keyboardLayouts/de_DE"
|
||||
import { chars as chars_it_IT, name as name_it_IT } from "@/keyboardLayouts/it_IT"
|
||||
import { chars as chars_nb_NO, name as name_nb_NO } from "@/keyboardLayouts/nb_NO"
|
||||
import { chars as chars_es_ES, name as name_es_ES } from "@/keyboardLayouts/es_ES"
|
||||
import { chars as chars_sv_SE, name as name_sv_SE } from "@/keyboardLayouts/sv_SE"
|
||||
import { chars as chars_fr_CH, name as name_fr_CH } from "@/keyboardLayouts/fr_CH"
|
||||
import { chars as chars_de_CH, name as name_de_CH } from "@/keyboardLayouts/de_CH"
|
||||
|
||||
type KeyInfo = { key: string | number; shift?: boolean, altRight?: boolean }
|
||||
export type KeyCombo = KeyInfo & { deadKey?: boolean, accentKey?: KeyInfo }
|
||||
|
||||
export const layouts: Record<string, string> = {
|
||||
be_FR: name_fr_BE,
|
||||
cs_CZ: name_cs_CZ,
|
||||
en_UK: name_en_UK,
|
||||
en_US: name_en_US,
|
||||
fr_FR: name_fr_FR,
|
||||
de_DE: name_de_DE,
|
||||
it_IT: name_it_IT,
|
||||
nb_NO: name_nb_NO,
|
||||
es_ES: name_es_ES,
|
||||
sv_SE: name_sv_SE,
|
||||
fr_CH: name_fr_CH,
|
||||
de_CH: name_de_CH,
|
||||
}
|
||||
|
||||
export const chars: Record<string, Record<string, KeyCombo>> = {
|
||||
be_FR: chars_fr_BE,
|
||||
cs_CZ: chars_cs_CZ,
|
||||
en_UK: chars_en_UK,
|
||||
en_US: chars_en_US,
|
||||
fr_FR: chars_fr_FR,
|
||||
de_DE: chars_de_DE,
|
||||
it_IT: chars_it_IT,
|
||||
nb_NO: chars_nb_NO,
|
||||
es_ES: chars_es_ES,
|
||||
sv_SE: chars_sv_SE,
|
||||
fr_CH: chars_fr_CH,
|
||||
de_CH: chars_de_CH,
|
||||
};
|
|
@ -0,0 +1,244 @@
|
|||
import { KeyCombo } from "../keyboardLayouts"
|
||||
|
||||
export const name = "Čeština";
|
||||
|
||||
const keyTrema = { key: "Backslash" } // tréma (umlaut), two dots placed above a vowel
|
||||
const keyAcute = { key: "Equal" } // accent aigu (acute accent), mark ´ placed above the letter
|
||||
const keyHat = { key: "Digit3", shift: true, altRight: true } // accent circonflexe (accent hat), mark ^ placed above the letter
|
||||
const keyCaron = { key: "Equal", shift: true } // caron or haček (inverted hat), mark ˇ placed above the letter
|
||||
const keyGrave = { key: "Digit7", shift: true, altRight: true } // accent grave, mark ` placed above the letter
|
||||
const keyTilde = { key: "Digit1", shift: true, altRight: true } // tilde, mark ~ placed above the letter
|
||||
const keyRing = { key: "Backquote", shift: true } // kroužek (little ring), mark ° placed above the letter
|
||||
const keyOverdot = { key: "Digit8", shift: true, altRight: true } // overdot (dot above), mark ˙ placed above the letter
|
||||
const keyHook = { key: "Digit6", shift: true, altRight: true } // ogonoek (little hook), mark ˛ placed beneath a letter
|
||||
const keyCedille = { key: "Equal", shift: true, altRight: true } // accent cedille (cedilla), mark ¸ placed beneath a letter
|
||||
|
||||
export const chars = {
|
||||
A: { key: "KeyA", shift: true },
|
||||
"Ä": { key: "KeyA", shift: true, accentKey: keyTrema },
|
||||
"Á": { key: "KeyA", shift: true, accentKey: keyAcute },
|
||||
"Â": { key: "KeyA", shift: true, accentKey: keyHat },
|
||||
"À": { key: "KeyA", shift: true, accentKey: keyGrave },
|
||||
"Ã": { key: "KeyA", shift: true, accentKey: keyTilde },
|
||||
"Ȧ": { key: "KeyA", shift: true, accentKey: keyOverdot },
|
||||
"Ą": { key: "KeyA", shift: true, accentKey: keyHook },
|
||||
B: { key: "KeyB", shift: true },
|
||||
"Ḃ": { key: "KeyB", shift: true, accentKEy: keyOverdot },
|
||||
C: { key: "KeyC", shift: true },
|
||||
"Č": { key: "KeyC", shift: true, accentKey: keyCaron },
|
||||
"Ċ": { key: "KeyC", shift: true, accentKey: keyOverdot },
|
||||
"Ç": { key: "KeyC", shift: true, accentKey: keyCedille },
|
||||
D: { key: "KeyD", shift: true },
|
||||
"Ď": { key: "KeyD", shift: true, accentKey: keyCaron },
|
||||
"Ḋ": { key: "KeyD", shift: true, accentKey: keyOverdot },
|
||||
E: { key: "KeyE", shift: true },
|
||||
"Ë": { key: "KeyE", shift: true, accentKey: keyTrema },
|
||||
"É": { key: "KeyE", shift: true, accentKey: keyAcute },
|
||||
"Ê": { key: "KeyE", shift: true, accentKey: keyHat },
|
||||
"Ě": { key: "KeyE", shift: true, accentKey: keyCaron },
|
||||
"È": { key: "KeyE", shift: true, accentKey: keyGrave },
|
||||
"Ẽ": { key: "KeyE", shift: true, accentKey: keyTilde },
|
||||
"Ė": { key: "KeyE", shift: true, accentKEy: keyOverdot },
|
||||
"Ę": { key: "KeyE", shift: true, accentKey: keyHook },
|
||||
F: { key: "KeyF", shift: true },
|
||||
"Ḟ": { key: "KeyF", shift: true, accentKey: keyOverdot },
|
||||
G: { key: "KeyG", shift: true },
|
||||
"Ġ": { key: "KeyG", shift: true, accentKey: keyOverdot },
|
||||
H: { key: "KeyH", shift: true },
|
||||
"Ḣ": { key: "KeyH", shift: true, accentKey: keyOverdot },
|
||||
I: { key: "KeyI", shift: true },
|
||||
"Ï": { key: "KeyI", shift: true, accentKey: keyTrema },
|
||||
"Í": { key: "KeyI", shift: true, accentKey: keyAcute },
|
||||
"Î": { key: "KeyI", shift: true, accentKey: keyHat },
|
||||
"Ì": { key: "KeyI", shift: true, accentKey: keyGrave },
|
||||
"Ĩ": { key: "KeyI", shift: true, accentKey: keyTilde },
|
||||
"İ": { key: "KeyI", shift: true, accentKey: keyOverdot },
|
||||
"Į": { key: "KeyI", shift: true, accentKey: keyHook },
|
||||
J: { key: "KeyJ", shift: true },
|
||||
K: { key: "KeyK", shift: true },
|
||||
L: { key: "KeyL", shift: true },
|
||||
"Ŀ": { key: "KeyL", shift: true },
|
||||
M: { key: "KeyM", shift: true },
|
||||
"Ṁ": { key: "KeyM", shift: true },
|
||||
N: { key: "KeyN", shift: true },
|
||||
"Ň": { key: "KeyN", shift: true, accentKey: keyCaron },
|
||||
"Ñ": { key: "KeyN", shift: true, accentKey: keyTilde },
|
||||
"Ṅ": { key: "KeyN", shift: true, accentKEy: keyOverdot },
|
||||
O: { key: "KeyO", shift: true },
|
||||
"Ö": { key: "KeyO", shift: true, accentKey: keyTrema },
|
||||
"Ó": { key: "KeyO", shift: true, accentKey: keyAcute },
|
||||
"Ô": { key: "KeyO", shift: true, accentKey: keyHat },
|
||||
"Ò": { key: "KeyO", shift: true, accentKey: keyGrave },
|
||||
"Õ": { key: "KeyO", shift: true, accentKey: keyTilde },
|
||||
"Ȯ": { key: "KeyO", shift: true, accentKey: keyOverdot },
|
||||
"Ǫ": { key: "KeyO", shift: true, accentKey: keyHook },
|
||||
P: { key: "KeyP", shift: true },
|
||||
"Ṗ": { key: "KeyP", shift: true, accentKey: keyOverdot },
|
||||
Q: { key: "KeyQ", shift: true },
|
||||
R: { key: "KeyR", shift: true },
|
||||
"Ř": { key: "KeyR", shift: true, accentKey: keyCaron },
|
||||
"Ṙ": { key: "KeyR", shift: true, accentKey: keyOverdot },
|
||||
S: { key: "KeyS", shift: true },
|
||||
"Š": { key: "KeyS", shift: true, accentKey: keyCaron },
|
||||
"Ṡ": { key: "KeyS", shift: true, accentKey: keyOverdot },
|
||||
T: { key: "KeyT", shift: true },
|
||||
"Ť": { key: "KeyT", shift: true, accentKey: keyCaron },
|
||||
"Ṫ": { key: "KeyT", shift: true, accentKey: keyOverdot },
|
||||
U: { key: "KeyU", shift: true },
|
||||
"Ü": { key: "KeyU", shift: true, accentKey: keyTrema },
|
||||
"Ú": { key: "KeyU", shift: true, accentKey: keyAcute },
|
||||
"Û": { key: "KeyU", shift: true, accentKey: keyHat },
|
||||
"Ù": { key: "KeyU", shift: true, accentKey: keyGrave },
|
||||
"Ũ": { key: "KeyU", shift: true, accentKey: keyTilde },
|
||||
"Ů": { key: "KeyU", shift: true, accentKey: keyRing },
|
||||
"Ų": { key: "KeyU", shift: true, accentKey: keyHook },
|
||||
V: { key: "KeyV", shift: true },
|
||||
W: { key: "KeyW", shift: true },
|
||||
"Ẇ": { key: "KeyW", shift: true, accentKey: keyOverdot },
|
||||
X: { key: "KeyX", shift: true },
|
||||
"Ẋ": { key: "KeyX", shift: true, accentKey: keyOverdot },
|
||||
Y: { key: "KeyY", shift: true },
|
||||
"Ý": { key: "KeyY", shift: true, accentKey: keyAcute },
|
||||
"Ẏ": { key: "KeyY", shift: true, accentKey: keyOverdot },
|
||||
Z: { key: "KeyZ", shift: true },
|
||||
"Ż": { key: "KeyZ", shift: true, accentKey: keyOverdot },
|
||||
a: { key: "KeyA" },
|
||||
"ä": { key: "KeyA", accentKey: keyTrema },
|
||||
"â": { key: "KeyA", accentKey: keyHat },
|
||||
"à": { key: "KeyA", accentKey: keyGrave },
|
||||
"ã": { key: "KeyA", accentKey: keyTilde },
|
||||
"ȧ": { key: "KeyA", accentKey: keyOverdot },
|
||||
"ą": { key: "KeyA", accentKey: keyHook },
|
||||
b: { key: "KeyB" },
|
||||
"{": { key: "KeyB", altRight: true },
|
||||
"ḃ": { key: "KeyB", accentKey: keyOverdot },
|
||||
c: { key: "KeyC" },
|
||||
"&": { key: "KeyC", altRight: true },
|
||||
"ç": { key: "KeyC", accentKey: keyCedille },
|
||||
"ċ": { key: "KeyC", accentKey: keyOverdot },
|
||||
d: { key: "KeyD" },
|
||||
"ď": { key: "KeyD", accentKey: keyCaron },
|
||||
"ḋ": { key: "KeyD", accentKey: keyOverdot },
|
||||
"Đ": { key: "KeyD", altRight: true },
|
||||
e: { key: "KeyE" },
|
||||
"ë": { key: "KeyE", accentKey: keyTrema },
|
||||
"ê": { key: "KeyE", accentKey: keyHat },
|
||||
"ẽ": { key: "KeyE", accentKey: keyTilde },
|
||||
"è": { key: "KeyE", accentKey: keyGrave },
|
||||
"ė": { key: "KeyE", accentKey: keyOverdot },
|
||||
"ę": { key: "KeyE", accentKey: keyHook },
|
||||
"€": { key: "KeyE", altRight: true },
|
||||
f: { key: "KeyF" },
|
||||
"ḟ": { key: "KeyF", accentKey: keyOverdot },
|
||||
"[": { key: "KeyF", altRight: true },
|
||||
g: { key: "KeyG" },
|
||||
"ġ": { key: "KeyG", accentKey: keyOverdot },
|
||||
"]": { key: "KeyF", altRight: true },
|
||||
h: { key: "KeyH" },
|
||||
"ḣ": { key: "KeyH", accentKey: keyOverdot },
|
||||
i: { key: "KeyI" },
|
||||
"ï": { key: "KeyI", accentKey: keyTrema },
|
||||
"î": { key: "KeyI", accentKey: keyHat },
|
||||
"ì": { key: "KeyI", accentKey: keyGrave },
|
||||
"ĩ": { key: "KeyI", accentKey: keyTilde },
|
||||
"ı": { key: "KeyI", accentKey: keyOverdot },
|
||||
"į": { key: "KeyI", accentKey: keyHook },
|
||||
j: { key: "KeyJ" },
|
||||
"ȷ": { key: "KeyJ", accentKey: keyOverdot },
|
||||
k: { key: "KeyK" },
|
||||
"ł": { key: "KeyK", altRight: true },
|
||||
l: { key: "KeyL" },
|
||||
"ŀ": { key: "KeyL", accentKey: keyOverdot },
|
||||
"Ł": { key: "KeyL", altRight: true },
|
||||
m: { key: "KeyM" },
|
||||
"ṁ": { key: "KeyM", accentKey: keyOverdot },
|
||||
n: { key: "KeyN" },
|
||||
"}": { key: "KeyN", altRight: true },
|
||||
"ň": { key: "KeyN", accentKey: keyCaron },
|
||||
"ñ": { key: "KeyN", accentKey: keyTilde },
|
||||
"ṅ": { key: "KeyN", accentKey: keyOverdot },
|
||||
o: { key: "KeyO" },
|
||||
"ö": { key: "Key0", accentKey: keyTrema },
|
||||
"ó": { key: "KeyO", accentKey: keyAcute },
|
||||
"ô": { key: "KeyO", accentKey: keyHat },
|
||||
"ò": { key: "KeyO", accentKey: keyGrave },
|
||||
"õ": { key: "KeyO", accentKey: keyTilde },
|
||||
"ȯ": { key: "KeyO", accentKey: keyOverdot },
|
||||
"ǫ": { key: "KeyO", accentKey: keyHook },
|
||||
p: { key: "KeyP" },
|
||||
"ṗ": { key: "KeyP", accentKey: keyOverdot },
|
||||
q: { key: "KeyQ" },
|
||||
r: { key: "KeyR" },
|
||||
"ṙ": { key: "KeyR", accentKey: keyOverdot },
|
||||
s: { key: "KeyS" },
|
||||
"ṡ": { key: "KeyS", accentKey: keyOverdot },
|
||||
"đ": { key: "KeyS", altRight: true },
|
||||
t: { key: "KeyT" },
|
||||
"ť": { key: "KeyT", accentKey: keyCaron },
|
||||
"ṫ": { key: "KeyT", accentKey: keyOverdot },
|
||||
u: { key: "KeyU" },
|
||||
"ü": { key: "KeyU", accentKey: keyTrema },
|
||||
"û": { key: "KeyU", accentKey: keyHat },
|
||||
"ù": { key: "KeyU", accentKey: keyGrave },
|
||||
"ũ": { key: "KeyU", accentKey: keyTilde },
|
||||
"ų": { key: "KeyU", accentKey: keyHook },
|
||||
v: { key: "KeyV" },
|
||||
"@": { key: "KeyV", altRight: true },
|
||||
w: { key: "KeyW" },
|
||||
"ẇ": { key: "KeyW", accentKey: keyOverdot },
|
||||
x: { key: "KeyX" },
|
||||
"#": { key: "KeyX", altRight: true },
|
||||
"ẋ": { key: "KeyX", accentKey: keyOverdot },
|
||||
y: { key: "KeyY" },
|
||||
"ẏ": { key: "KeyY", accentKey: keyOverdot },
|
||||
z: { key: "KeyZ" },
|
||||
"ż": { key: "KeyZ", accentKey: keyOverdot },
|
||||
";": { key: "Backquote" },
|
||||
"°": { key: "Backquote", shift: true, deadKey: true },
|
||||
"+": { key: "Digit1" },
|
||||
1: { key: "Digit1", shift: true },
|
||||
"ě": { key: "Digit2" },
|
||||
2: { key: "Digit2", shift: true },
|
||||
"š": { key: "Digit3" },
|
||||
3: { key: "Digit3", shift: true },
|
||||
"č": { key: "Digit4" },
|
||||
4: { key: "Digit4", shift: true },
|
||||
"ř": { key: "Digit5" },
|
||||
5: { key: "Digit5", shift: true },
|
||||
"ž": { key: "Digit6" },
|
||||
6: { key: "Digit6", shift: true },
|
||||
"ý": { key: "Digit7" },
|
||||
7: { key: "Digit7", shift: true },
|
||||
"á": { key: "Digit8" },
|
||||
8: { key: "Digit8", shift: true },
|
||||
"í": { key: "Digit9" },
|
||||
9: { key: "Digit9", shift: true },
|
||||
"é": { key: "Digit0" },
|
||||
0: { key: "Digit0", shift: true },
|
||||
"=": { key: "Minus" },
|
||||
"%": { key: "Minus", shift: true },
|
||||
"ú": { key: "BracketLeft" },
|
||||
"/": { key: "BracketLeft", shift: true },
|
||||
")": { key: "BracketRight" },
|
||||
"(": { key: "BracketRight", shift: true },
|
||||
"ů": { key: "Semicolon" },
|
||||
"\"": { key: "Semicolon", shift: true },
|
||||
"§": { key: "Quote" },
|
||||
"!": { key: "Quote", shift: true },
|
||||
"'": { key: "Backslash", shift: true },
|
||||
",": { key: "Comma" },
|
||||
"?": { key: "Comma", shift: true },
|
||||
"<": { key: "Comma", altRight: true },
|
||||
".": { key: "Period" },
|
||||
":": { key: "Period", shift: true },
|
||||
">": { key: "Period", altRight: true },
|
||||
"-": { key: "Slash" },
|
||||
"_": { key: "Slash", shift: true },
|
||||
"*": { key: "Slash", altRight: true },
|
||||
"\\": { key: "IntlBackslash" },
|
||||
"|": { key: "IntlBackslash", shift: true },
|
||||
" ": { key: "Space" },
|
||||
"\n": { key: "Enter" },
|
||||
Enter: { key: "Enter" },
|
||||
Tab: { key: "Tab" },
|
||||
} as Record<string, KeyCombo>;
|
|
@ -0,0 +1,165 @@
|
|||
import { KeyCombo } from "../keyboardLayouts"
|
||||
|
||||
export const name = "Schwiizerdütsch";
|
||||
|
||||
const keyTrema = { key: "BracketRight" } // tréma (umlaut), two dots placed above a vowel
|
||||
const keyAcute = { key: "Minus", altRight: true } // accent aigu (acute accent), mark ´ placed above the letter
|
||||
const keyHat = { key: "Equal" } // accent circonflexe (accent hat), mark ^ placed above the letter
|
||||
const keyGrave = { key: "Equal", shift: true } // accent grave, mark ` placed above the letter
|
||||
const keyTilde = { key: "Equal", altRight: true } // tilde, mark ~ placed above the letter
|
||||
|
||||
export const chars = {
|
||||
A: { key: "KeyA", shift: true },
|
||||
"Ä": { key: "KeyA", shift: true, accentKey: keyTrema },
|
||||
"Á": { key: "KeyA", shift: true, accentKey: keyAcute },
|
||||
"Â": { key: "KeyA", shift: true, accentKey: keyHat },
|
||||
"À": { key: "KeyA", shift: true, accentKey: keyGrave },
|
||||
"Ã": { key: "KeyA", shift: true, accentKey: keyTilde },
|
||||
B: { key: "KeyB", shift: true },
|
||||
C: { key: "KeyC", shift: true },
|
||||
D: { key: "KeyD", shift: true },
|
||||
E: { key: "KeyE", shift: true },
|
||||
"Ë": { key: "KeyE", shift: true, accentKey: keyTrema },
|
||||
"É": { key: "KeyE", shift: true, accentKey: keyAcute },
|
||||
"Ê": { key: "KeyE", shift: true, accentKey: keyHat },
|
||||
"È": { key: "KeyE", shift: true, accentKey: keyGrave },
|
||||
"Ẽ": { key: "KeyE", shift: true, accentKey: keyTilde },
|
||||
F: { key: "KeyF", shift: true },
|
||||
G: { key: "KeyG", shift: true },
|
||||
H: { key: "KeyH", shift: true },
|
||||
I: { key: "KeyI", shift: true },
|
||||
"Ï": { key: "KeyI", shift: true, accentKey: keyTrema },
|
||||
"Í": { key: "KeyI", shift: true, accentKey: keyAcute },
|
||||
"Î": { key: "KeyI", shift: true, accentKey: keyHat },
|
||||
"Ì": { key: "KeyI", shift: true, accentKey: keyGrave },
|
||||
"Ĩ": { key: "KeyI", shift: true, accentKey: keyTilde },
|
||||
J: { key: "KeyJ", shift: true },
|
||||
K: { key: "KeyK", shift: true },
|
||||
L: { key: "KeyL", shift: true },
|
||||
M: { key: "KeyM", shift: true },
|
||||
N: { key: "KeyN", shift: true },
|
||||
O: { key: "KeyO", shift: true },
|
||||
"Ö": { key: "KeyO", shift: true, accentKey: keyTrema },
|
||||
"Ó": { key: "KeyO", shift: true, accentKey: keyAcute },
|
||||
"Ô": { key: "KeyO", shift: true, accentKey: keyHat },
|
||||
"Ò": { key: "KeyO", shift: true, accentKey: keyGrave },
|
||||
"Õ": { key: "KeyO", shift: true, accentKey: keyTilde },
|
||||
P: { key: "KeyP", shift: true },
|
||||
Q: { key: "KeyQ", shift: true },
|
||||
R: { key: "KeyR", shift: true },
|
||||
S: { key: "KeyS", shift: true },
|
||||
T: { key: "KeyT", shift: true },
|
||||
U: { key: "KeyU", shift: true },
|
||||
"Ü": { key: "KeyU", shift: true, accentKey: keyTrema },
|
||||
"Ú": { key: "KeyU", shift: true, accentKey: keyAcute },
|
||||
"Û": { key: "KeyU", shift: true, accentKey: keyHat },
|
||||
"Ù": { key: "KeyU", shift: true, accentKey: keyGrave },
|
||||
"Ũ": { key: "KeyU", shift: true, accentKey: keyTilde },
|
||||
V: { key: "KeyV", shift: true },
|
||||
W: { key: "KeyW", shift: true },
|
||||
X: { key: "KeyX", shift: true },
|
||||
Y: { key: "KeyZ", shift: true },
|
||||
Z: { key: "KeyY", shift: true },
|
||||
a: { key: "KeyA" },
|
||||
"á": { key: "KeyA", accentKey: keyAcute },
|
||||
"â": { key: "KeyA", accentKey: keyHat },
|
||||
"ã": { key: "KeyA", accentKey: keyTilde },
|
||||
b: { key: "KeyB" },
|
||||
c: { key: "KeyC" },
|
||||
d: { key: "KeyD" },
|
||||
e: { key: "KeyE" },
|
||||
"ë": { key: "KeyE", accentKey: keyTrema },
|
||||
"ê": { key: "KeyE", accentKey: keyHat },
|
||||
"ẽ": { key: "KeyE", accentKey: keyTilde },
|
||||
"€": { key: "KeyE", altRight: true },
|
||||
f: { key: "KeyF" },
|
||||
g: { key: "KeyG" },
|
||||
h: { key: "KeyH" },
|
||||
i: { key: "KeyI" },
|
||||
"ï": { key: "KeyI", accentKey: keyTrema },
|
||||
"í": { key: "KeyI", accentKey: keyAcute },
|
||||
"î": { key: "KeyI", accentKey: keyHat },
|
||||
"ì": { key: "KeyI", accentKey: keyGrave },
|
||||
"ĩ": { key: "KeyI", accentKey: keyTilde },
|
||||
j: { key: "KeyJ" },
|
||||
k: { key: "KeyK" },
|
||||
l: { key: "KeyL" },
|
||||
m: { key: "KeyM" },
|
||||
n: { key: "KeyN" },
|
||||
o: { key: "KeyO" },
|
||||
"ó": { key: "KeyO", accentKey: keyAcute },
|
||||
"ô": { key: "KeyO", accentKey: keyHat },
|
||||
"ò": { key: "KeyO", accentKey: keyGrave },
|
||||
"õ": { key: "KeyO", accentKey: keyTilde },
|
||||
p: { key: "KeyP" },
|
||||
q: { key: "KeyQ" },
|
||||
r: { key: "KeyR" },
|
||||
s: { key: "KeyS" },
|
||||
t: { key: "KeyT" },
|
||||
u: { key: "KeyU" },
|
||||
"ú": { key: "KeyU", accentKey: keyAcute },
|
||||
"û": { key: "KeyU", accentKey: keyHat },
|
||||
"ù": { key: "KeyU", accentKey: keyGrave },
|
||||
"ũ": { key: "KeyU", accentKey: keyTilde },
|
||||
v: { key: "KeyV" },
|
||||
w: { key: "KeyW" },
|
||||
x: { key: "KeyX" },
|
||||
y: { key: "KeyZ" },
|
||||
z: { key: "KeyY" },
|
||||
"§": { key: "Backquote" },
|
||||
"°": { key: "Backquote", shift: true },
|
||||
1: { key: "Digit1" },
|
||||
"+": { key: "Digit1", shift: true },
|
||||
"|": { key: "Digit1", altRight: true },
|
||||
2: { key: "Digit2" },
|
||||
"\"": { key: "Digit2", shift: true },
|
||||
"@": { key: "Digit2", altRight: true },
|
||||
3: { key: "Digit3" },
|
||||
"*": { key: "Digit3", shift: true },
|
||||
"#": { key: "Digit3", altRight: true },
|
||||
4: { key: "Digit4" },
|
||||
"ç": { key: "Digit4", shift: true },
|
||||
5: { key: "Digit5" },
|
||||
"%": { key: "Digit5", shift: true },
|
||||
6: { key: "Digit6" },
|
||||
"&": { key: "Digit6", shift: true },
|
||||
7: { key: "Digit7" },
|
||||
"/": { key: "Digit7", shift: true },
|
||||
8: { key: "Digit8" },
|
||||
"(": { key: "Digit8", shift: true },
|
||||
9: { key: "Digit9" },
|
||||
")": { key: "Digit9", shift: true },
|
||||
0: { key: "Digit0" },
|
||||
"=": { key: "Digit0", shift: true },
|
||||
"'": { key: "Minus" },
|
||||
"?": { key: "Minus", shift: true },
|
||||
"^": { key: "Equal", deadKey: true },
|
||||
"`": { key: "Equal", shift: true },
|
||||
"~": { key: "Equal", altRight: true, deadKey: true },
|
||||
"ü": { key: "BracketLeft" },
|
||||
"è": { key: "BracketLeft", shift: true },
|
||||
"[": { key: "BracketLeft", altRight: true },
|
||||
"!": { key: "BracketRight", shift: true },
|
||||
"]": { key: "BracketRight", altRight: true },
|
||||
"ö": { key: "Semicolon" },
|
||||
"é": { key: "Semicolon", shift: true },
|
||||
"ä": { key: "Quote" },
|
||||
"à": { key: "Quote", shift: true },
|
||||
"{": { key: "Quote", altRight: true },
|
||||
"$": { key: "Backslash" },
|
||||
"£": { key: "Backslash", shift: true },
|
||||
"}": { key: "Backslash", altRight: true },
|
||||
",": { key: "Comma" },
|
||||
";": { key: "Comma", shift: true },
|
||||
".": { key: "Period" },
|
||||
":": { key: "Period", shift: true },
|
||||
"-": { key: "Slash" },
|
||||
"_": { key: "Slash", shift: true },
|
||||
"<": { key: "IntlBackslash" },
|
||||
">": { key: "IntlBackslash", shift: true },
|
||||
"\\": { key: "IntlBackslash", altRight: true },
|
||||
" ": { key: "Space" },
|
||||
"\n": { key: "Enter" },
|
||||
Enter: { key: "Enter" },
|
||||
Tab: { key: "Tab" },
|
||||
} as Record<string, KeyCombo>;
|
|
@ -0,0 +1,152 @@
|
|||
import { KeyCombo } from "../keyboardLayouts"
|
||||
|
||||
export const name = "Deutsch";
|
||||
|
||||
const keyAcute = { key: "Equal" } // accent aigu (acute accent), mark ´ placed above the letter
|
||||
const keyHat = { key: "Backquote" } // accent circonflexe (accent hat), mark ^ placed above the letter
|
||||
const keyGrave = { key: "Equal", shift: true } // accent grave, mark ` placed above the letter
|
||||
|
||||
export const chars = {
|
||||
A: { key: "KeyA", shift: true },
|
||||
"Á": { key: "KeyA", shift: true, accentKey: keyAcute },
|
||||
"Â": { key: "KeyA", shift: true, accentKey: keyHat },
|
||||
"À": { key: "KeyA", shift: true, accentKey: keyGrave },
|
||||
B: { key: "KeyB", shift: true },
|
||||
C: { key: "KeyC", shift: true },
|
||||
D: { key: "KeyD", shift: true },
|
||||
E: { key: "KeyE", shift: true },
|
||||
"É": { key: "KeyE", shift: true, accentKey: keyAcute },
|
||||
"Ê": { key: "KeyE", shift: true, accentKey: keyHat },
|
||||
"È": { key: "KeyE", shift: true, accentKey: keyGrave },
|
||||
F: { key: "KeyF", shift: true },
|
||||
G: { key: "KeyG", shift: true },
|
||||
H: { key: "KeyH", shift: true },
|
||||
I: { key: "KeyI", shift: true },
|
||||
"Í": { key: "KeyI", shift: true, accentKey: keyAcute },
|
||||
"Î": { key: "KeyI", shift: true, accentKey: keyHat },
|
||||
"Ì": { key: "KeyI", shift: true, accentKey: keyGrave },
|
||||
J: { key: "KeyJ", shift: true },
|
||||
K: { key: "KeyK", shift: true },
|
||||
L: { key: "KeyL", shift: true },
|
||||
M: { key: "KeyM", shift: true },
|
||||
N: { key: "KeyN", shift: true },
|
||||
O: { key: "KeyO", shift: true },
|
||||
"Ó": { key: "KeyO", shift: true, accentKey: keyAcute },
|
||||
"Ô": { key: "KeyO", shift: true, accentKey: keyHat },
|
||||
"Ò": { key: "KeyO", shift: true, accentKey: keyGrave },
|
||||
P: { key: "KeyP", shift: true },
|
||||
Q: { key: "KeyQ", shift: true },
|
||||
R: { key: "KeyR", shift: true },
|
||||
S: { key: "KeyS", shift: true },
|
||||
T: { key: "KeyT", shift: true },
|
||||
U: { key: "KeyU", shift: true },
|
||||
"Ú": { key: "KeyU", shift: true, accentKey: keyAcute },
|
||||
"Û": { key: "KeyU", shift: true, accentKey: keyHat },
|
||||
"Ù": { key: "KeyU", shift: true, accentKey: keyGrave },
|
||||
V: { key: "KeyV", shift: true },
|
||||
W: { key: "KeyW", shift: true },
|
||||
X: { key: "KeyX", shift: true },
|
||||
Y: { key: "KeyZ", shift: true },
|
||||
Z: { key: "KeyY", shift: true },
|
||||
a: { key: "KeyA" },
|
||||
"á": { key: "KeyA", accentKey: keyAcute },
|
||||
"â": { key: "KeyA", accentKey: keyHat },
|
||||
"à": { key: "KeyA", accentKey: keyGrave},
|
||||
b: { key: "KeyB" },
|
||||
c: { key: "KeyC" },
|
||||
d: { key: "KeyD" },
|
||||
e: { key: "KeyE" },
|
||||
"é": { key: "KeyE", accentKey: keyAcute},
|
||||
"ê": { key: "KeyE", accentKey: keyHat },
|
||||
"è": { key: "KeyE", accentKey: keyGrave },
|
||||
"€": { key: "KeyE", altRight: true },
|
||||
f: { key: "KeyF" },
|
||||
g: { key: "KeyG" },
|
||||
h: { key: "KeyH" },
|
||||
i: { key: "KeyI" },
|
||||
"í": { key: "KeyI", accentKey: keyAcute },
|
||||
"î": { key: "KeyI", accentKey: keyHat },
|
||||
"ì": { key: "KeyI", accentKey: keyGrave },
|
||||
j: { key: "KeyJ" },
|
||||
k: { key: "KeyK" },
|
||||
l: { key: "KeyL" },
|
||||
m: { key: "KeyM" },
|
||||
"µ": { key: "KeyM", altRight: true },
|
||||
n: { key: "KeyN" },
|
||||
o: { key: "KeyO" },
|
||||
"ó": { key: "KeyO", accentKey: keyAcute },
|
||||
"ô": { key: "KeyO", accentKey: keyHat },
|
||||
"ò": { key: "KeyO", accentKey: keyGrave },
|
||||
p: { key: "KeyP" },
|
||||
q: { key: "KeyQ" },
|
||||
"@": { key: "KeyQ", altRight: true },
|
||||
r: { key: "KeyR" },
|
||||
s: { key: "KeyS" },
|
||||
t: { key: "KeyT" },
|
||||
u: { key: "KeyU" },
|
||||
"ú": { key: "KeyU", accentKey: keyAcute },
|
||||
"û": { key: "KeyU", accentKey: keyHat },
|
||||
"ù": { key: "KeyU", accentKey: keyGrave },
|
||||
v: { key: "KeyV" },
|
||||
w: { key: "KeyW" },
|
||||
x: { key: "KeyX" },
|
||||
y: { key: "KeyZ" },
|
||||
z: { key: "KeyY" },
|
||||
"°": { key: "Backquote", shift: true },
|
||||
"^": { key: "Backquote", deadKey: true },
|
||||
1: { key: "Digit1" },
|
||||
"!": { key: "Digit1", shift: true },
|
||||
2: { key: "Digit2" },
|
||||
"\"": { key: "Digit2", shift: true },
|
||||
"²": { key: "Digit2", altRight: true },
|
||||
3: { key: "Digit3" },
|
||||
"§": { key: "Digit3", shift: true },
|
||||
"³": { key: "Digit3", altRight: true },
|
||||
4: { key: "Digit4" },
|
||||
"$": { key: "Digit4", shift: true },
|
||||
5: { key: "Digit5" },
|
||||
"%": { key: "Digit5", shift: true },
|
||||
6: { key: "Digit6" },
|
||||
"&": { key: "Digit6", shift: true },
|
||||
7: { key: "Digit7" },
|
||||
"/": { key: "Digit7", shift: true },
|
||||
"{": { key: "Digit7", altRight: true },
|
||||
8: { key: "Digit8" },
|
||||
"(": { key: "Digit8", shift: true },
|
||||
"[": { key: "Digit8", altRight: true },
|
||||
9: { key: "Digit9" },
|
||||
")": { key: "Digit9", shift: true },
|
||||
"]": { key: "Digit9", altRight: true },
|
||||
0: { key: "Digit0" },
|
||||
"=": { key: "Digit0", shift: true },
|
||||
"}": { key: "Digit0", altRight: true },
|
||||
"ß": { key: "Minus" },
|
||||
"?": { key: "Minus", shift: true },
|
||||
"\\": { key: "Minus", altRight: true },
|
||||
"´": { key: "Equal", deadKey: true },
|
||||
"`": { key: "Equal", shift: true, deadKey: true },
|
||||
"ü": { key: "BracketLeft" },
|
||||
"Ü": { key: "BracketLeft", shift: true },
|
||||
"+": { key: "BracketRight" },
|
||||
"*": { key: "BracketRight", shift: true },
|
||||
"~": { key: "BracketRight", altRight: true },
|
||||
"ö": { key: "Semicolon" },
|
||||
"Ö": { key: "Semicolon", shift: true },
|
||||
"ä": { key: "Quote" },
|
||||
"Ä": { key: "Quote", shift: true },
|
||||
"#": { key: "Backslash" },
|
||||
"'": { key: "Backslash", shift: true },
|
||||
",": { key: "Comma" },
|
||||
";": { key: "Comma", shift: true },
|
||||
".": { key: "Period" },
|
||||
":": { key: "Period", shift: true },
|
||||
"-": { key: "Slash" },
|
||||
"_": { key: "Slash", shift: true },
|
||||
"<": { key: "IntlBackslash" },
|
||||
">": { key: "IntlBackslash", shift: true },
|
||||
"|": { key: "IntlBackslash", altRight: true },
|
||||
" ": { key: "Space" },
|
||||
"\n": { key: "Enter" },
|
||||
Enter: { key: "Enter" },
|
||||
Tab: { key: "Tab" },
|
||||
} as Record<string, KeyCombo>;
|
|
@ -0,0 +1,107 @@
|
|||
import { KeyCombo } from "../keyboardLayouts"
|
||||
|
||||
export const name = "English (UK)";
|
||||
|
||||
export const chars = {
|
||||
A: { key: "KeyA", shift: true },
|
||||
B: { key: "KeyB", shift: true },
|
||||
C: { key: "KeyC", shift: true },
|
||||
D: { key: "KeyD", shift: true },
|
||||
E: { key: "KeyE", shift: true },
|
||||
F: { key: "KeyF", shift: true },
|
||||
G: { key: "KeyG", shift: true },
|
||||
H: { key: "KeyH", shift: true },
|
||||
I: { key: "KeyI", shift: true },
|
||||
J: { key: "KeyJ", shift: true },
|
||||
K: { key: "KeyK", shift: true },
|
||||
L: { key: "KeyL", shift: true },
|
||||
M: { key: "KeyM", shift: true },
|
||||
N: { key: "KeyN", shift: true },
|
||||
O: { key: "KeyO", shift: true },
|
||||
P: { key: "KeyP", shift: true },
|
||||
Q: { key: "KeyQ", shift: true },
|
||||
R: { key: "KeyR", shift: true },
|
||||
S: { key: "KeyS", shift: true },
|
||||
T: { key: "KeyT", shift: true },
|
||||
U: { key: "KeyU", shift: true },
|
||||
V: { key: "KeyV", shift: true },
|
||||
W: { key: "KeyW", shift: true },
|
||||
X: { key: "KeyX", shift: true },
|
||||
Y: { key: "KeyY", shift: true },
|
||||
Z: { key: "KeyZ", shift: true },
|
||||
a: { key: "KeyA" },
|
||||
b: { key: "KeyB" },
|
||||
c: { key: "KeyC" },
|
||||
d: { key: "KeyD" },
|
||||
e: { key: "KeyE" },
|
||||
f: { key: "KeyF" },
|
||||
g: { key: "KeyG" },
|
||||
h: { key: "KeyH" },
|
||||
i: { key: "KeyI" },
|
||||
j: { key: "KeyJ" },
|
||||
k: { key: "KeyK" },
|
||||
l: { key: "KeyL" },
|
||||
m: { key: "KeyM" },
|
||||
n: { key: "KeyN" },
|
||||
o: { key: "KeyO" },
|
||||
p: { key: "KeyP" },
|
||||
q: { key: "KeyQ" },
|
||||
r: { key: "KeyR" },
|
||||
s: { key: "KeyS" },
|
||||
t: { key: "KeyT" },
|
||||
u: { key: "KeyU" },
|
||||
v: { key: "KeyV" },
|
||||
w: { key: "KeyW" },
|
||||
x: { key: "KeyX" },
|
||||
y: { key: "KeyY" },
|
||||
z: { key: "KeyZ" },
|
||||
1: { key: "Digit1" },
|
||||
"!": { key: "Digit1", shift: true },
|
||||
2: { key: "Digit2" },
|
||||
"\"": { key: "Digit2", shift: true },
|
||||
3: { key: "Digit3" },
|
||||
"£": { key: "Digit3", shift: true },
|
||||
4: { key: "Digit4" },
|
||||
$: { key: "Digit4", shift: true },
|
||||
"€": { key: "Digit4", altRight: true },
|
||||
5: { key: "Digit5" },
|
||||
"%": { key: "Digit5", shift: true },
|
||||
6: { key: "Digit6" },
|
||||
"^": { key: "Digit6", shift: true },
|
||||
7: { key: "Digit7" },
|
||||
"&": { key: "Digit7", shift: true },
|
||||
8: { key: "Digit8" },
|
||||
"*": { key: "Digit8", shift: true },
|
||||
9: { key: "Digit9" },
|
||||
"(": { key: "Digit9", shift: true },
|
||||
0: { key: "Digit0" },
|
||||
")": { key: "Digit0", shift: true },
|
||||
"-": { key: "Minus" },
|
||||
_: { key: "Minus", shift: true },
|
||||
"=": { key: "Equal" },
|
||||
"+": { key: "Equal", shift: true },
|
||||
"'": { key: "Quote" },
|
||||
'@': { key: "Quote", shift: true },
|
||||
",": { key: "Comma" },
|
||||
"<": { key: "Comma", shift: true },
|
||||
"/": { key: "Slash" },
|
||||
"?": { key: "Slash", shift: true },
|
||||
".": { key: "Period" },
|
||||
">": { key: "Period", shift: true },
|
||||
";": { key: "Semicolon" },
|
||||
":": { key: "Semicolon", shift: true },
|
||||
"[": { key: "BracketLeft" },
|
||||
"{": { key: "BracketLeft", shift: true },
|
||||
"]": { key: "BracketRight" },
|
||||
"}": { key: "BracketRight", shift: true },
|
||||
"#": { key: "Backslash" },
|
||||
"~": { key: "Backslash", shift: true },
|
||||
"`": { key: "Backquote" },
|
||||
"¬": { key: "Backquote", shift: true },
|
||||
"\\": { key: "IntlBackslash" },
|
||||
"|": { key: "IntlBackslash", shift: true },
|
||||
" ": { key: "Space" },
|
||||
"\n": { key: "Enter" },
|
||||
Enter: { key: "Enter" },
|
||||
Tab: { key: "Tab" },
|
||||
} as Record<string, KeyCombo>
|
|
@ -0,0 +1,113 @@
|
|||
import { KeyCombo } from "../keyboardLayouts"
|
||||
|
||||
export const name = "English (US)";
|
||||
|
||||
export const chars = {
|
||||
A: { key: "KeyA", shift: true },
|
||||
B: { key: "KeyB", shift: true },
|
||||
C: { key: "KeyC", shift: true },
|
||||
D: { key: "KeyD", shift: true },
|
||||
E: { key: "KeyE", shift: true },
|
||||
F: { key: "KeyF", shift: true },
|
||||
G: { key: "KeyG", shift: true },
|
||||
H: { key: "KeyH", shift: true },
|
||||
I: { key: "KeyI", shift: true },
|
||||
J: { key: "KeyJ", shift: true },
|
||||
K: { key: "KeyK", shift: true },
|
||||
L: { key: "KeyL", shift: true },
|
||||
M: { key: "KeyM", shift: true },
|
||||
N: { key: "KeyN", shift: true },
|
||||
O: { key: "KeyO", shift: true },
|
||||
P: { key: "KeyP", shift: true },
|
||||
Q: { key: "KeyQ", shift: true },
|
||||
R: { key: "KeyR", shift: true },
|
||||
S: { key: "KeyS", shift: true },
|
||||
T: { key: "KeyT", shift: true },
|
||||
U: { key: "KeyU", shift: true },
|
||||
V: { key: "KeyV", shift: true },
|
||||
W: { key: "KeyW", shift: true },
|
||||
X: { key: "KeyX", shift: true },
|
||||
Y: { key: "KeyY", shift: true },
|
||||
Z: { key: "KeyZ", shift: true },
|
||||
a: { key: "KeyA" },
|
||||
b: { key: "KeyB" },
|
||||
c: { key: "KeyC" },
|
||||
d: { key: "KeyD" },
|
||||
e: { key: "KeyE" },
|
||||
f: { key: "KeyF" },
|
||||
g: { key: "KeyG" },
|
||||
h: { key: "KeyH" },
|
||||
i: { key: "KeyI" },
|
||||
j: { key: "KeyJ" },
|
||||
k: { key: "KeyK" },
|
||||
l: { key: "KeyL" },
|
||||
m: { key: "KeyM" },
|
||||
n: { key: "KeyN" },
|
||||
o: { key: "KeyO" },
|
||||
p: { key: "KeyP" },
|
||||
q: { key: "KeyQ" },
|
||||
r: { key: "KeyR" },
|
||||
s: { key: "KeyS" },
|
||||
t: { key: "KeyT" },
|
||||
u: { key: "KeyU" },
|
||||
v: { key: "KeyV" },
|
||||
w: { key: "KeyW" },
|
||||
x: { key: "KeyX" },
|
||||
y: { key: "KeyY" },
|
||||
z: { key: "KeyZ" },
|
||||
1: { key: "Digit1" },
|
||||
"!": { key: "Digit1", shift: true },
|
||||
2: { key: "Digit2" },
|
||||
"@": { key: "Digit2", shift: true },
|
||||
3: { key: "Digit3" },
|
||||
"#": { key: "Digit3", shift: true },
|
||||
4: { key: "Digit4" },
|
||||
$: { key: "Digit4", shift: true },
|
||||
"%": { key: "Digit5", shift: true },
|
||||
5: { key: "Digit5" },
|
||||
"^": { key: "Digit6", shift: true },
|
||||
6: { key: "Digit6" },
|
||||
"&": { key: "Digit7", shift: true },
|
||||
7: { key: "Digit7" },
|
||||
"*": { key: "Digit8", shift: true },
|
||||
8: { key: "Digit8" },
|
||||
"(": { key: "Digit9", shift: true },
|
||||
9: { key: "Digit9" },
|
||||
")": { key: "Digit0", shift: true },
|
||||
0: { key: "Digit0" },
|
||||
"-": { key: "Minus" },
|
||||
_: { key: "Minus", shift: true },
|
||||
"=": { key: "Equal" },
|
||||
"+": { key: "Equal", shift: true },
|
||||
"'": { key: "Quote" },
|
||||
'"': { key: "Quote", shift: true },
|
||||
",": { key: "Comma" },
|
||||
"<": { key: "Comma", shift: true },
|
||||
"/": { key: "Slash" },
|
||||
"?": { key: "Slash", shift: true },
|
||||
".": { key: "Period" },
|
||||
">": { key: "Period", shift: true },
|
||||
";": { key: "Semicolon" },
|
||||
":": { key: "Semicolon", shift: true },
|
||||
"[": { key: "BracketLeft" },
|
||||
"{": { key: "BracketLeft", shift: true },
|
||||
"]": { key: "BracketRight" },
|
||||
"}": { key: "BracketRight", shift: true },
|
||||
"\\": { key: "Backslash" },
|
||||
"|": { key: "Backslash", shift: true },
|
||||
"`": { key: "Backquote" },
|
||||
"~": { key: "Backquote", shift: true },
|
||||
"§": { key: "IntlBackslash" },
|
||||
"±": { key: "IntlBackslash", shift: true },
|
||||
" ": { key: "Space", shift: false },
|
||||
"\n": { key: "Enter", shift: false },
|
||||
Enter: { key: "Enter", shift: false },
|
||||
Tab: { key: "Tab", shift: false },
|
||||
PrintScreen: { key: "Prt Sc", shift: false },
|
||||
SystemRequest: { key: "Prt Sc", shift: true },
|
||||
ScrollLock: { key: "ScrollLock", shift: false},
|
||||
Pause: { key: "Pause", shift: false },
|
||||
Break: { key: "Pause", shift: true },
|
||||
Insert: { key: "Insert", shift: false },
|
||||
Delete: { key: "Delete", shift: false },
|
||||
} as Record<string, KeyCombo>
|
|
@ -0,0 +1,168 @@
|
|||
import { KeyCombo } from "../keyboardLayouts"
|
||||
|
||||
export const name = "Español";
|
||||
|
||||
const keyTrema = { key: "Quote", shift: true } // tréma (umlaut), two dots placed above a vowel
|
||||
const keyAcute = { key: "Quote" } // accent aigu (acute accent), mark ´ placed above the letter
|
||||
const keyHat = { key: "BracketRight", shift: true } // accent circonflexe (accent hat), mark ^ placed above the letter
|
||||
const keyGrave = { key: "BracketRight" } // accent grave, mark ` placed above the letter
|
||||
const keyTilde = { key: "Key4", altRight: true } // tilde, mark ~ placed above the letter
|
||||
|
||||
export const chars = {
|
||||
A: { key: "KeyA", shift: true },
|
||||
"Ä": { key: "KeyA", shift: true, accentKey: keyTrema },
|
||||
"Á": { key: "KeyA", shift: true, accentKey: keyAcute },
|
||||
"Â": { key: "KeyA", shift: true, accentKey: keyHat },
|
||||
"À": { key: "KeyA", shift: true, accentKey: keyGrave },
|
||||
"Ã": { key: "KeyA", shift: true, accentKey: keyTilde },
|
||||
B: { key: "KeyB", shift: true },
|
||||
C: { key: "KeyC", shift: true },
|
||||
D: { key: "KeyD", shift: true },
|
||||
E: { key: "KeyE", shift: true },
|
||||
"Ë": { key: "KeyE", shift: true, accentKey: keyTrema },
|
||||
"É": { key: "KeyE", shift: true, accentKey: keyAcute },
|
||||
"Ê": { key: "KeyE", shift: true, accentKey: keyHat },
|
||||
"È": { key: "KeyE", shift: true, accentKey: keyGrave },
|
||||
"Ẽ": { key: "KeyE", shift: true, accentKey: keyTilde },
|
||||
F: { key: "KeyF", shift: true },
|
||||
G: { key: "KeyG", shift: true },
|
||||
H: { key: "KeyH", shift: true },
|
||||
I: { key: "KeyI", shift: true },
|
||||
"Ï": { key: "KeyI", shift: true, accentKey: keyTrema },
|
||||
"Í": { key: "KeyI", shift: true, accentKey: keyAcute },
|
||||
"Î": { key: "KeyI", shift: true, accentKey: keyHat },
|
||||
"Ì": { key: "KeyI", shift: true, accentKey: keyGrave },
|
||||
"Ĩ": { key: "KeyI", shift: true, accentKey: keyTilde },
|
||||
J: { key: "KeyJ", shift: true },
|
||||
K: { key: "KeyK", shift: true },
|
||||
L: { key: "KeyL", shift: true },
|
||||
M: { key: "KeyM", shift: true },
|
||||
N: { key: "KeyN", shift: true },
|
||||
O: { key: "KeyO", shift: true },
|
||||
"Ö": { key: "KeyO", shift: true, accentKey: keyTrema },
|
||||
"Ó": { key: "KeyO", shift: true, accentKey: keyAcute },
|
||||
"Ô": { key: "KeyO", shift: true, accentKey: keyHat },
|
||||
"Ò": { key: "KeyO", shift: true, accentKey: keyGrave },
|
||||
"Õ": { key: "KeyO", shift: true, accentKey: keyTilde },
|
||||
P: { key: "KeyP", shift: true },
|
||||
Q: { key: "KeyQ", shift: true },
|
||||
R: { key: "KeyR", shift: true },
|
||||
S: { key: "KeyS", shift: true },
|
||||
T: { key: "KeyT", shift: true },
|
||||
U: { key: "KeyU", shift: true },
|
||||
"Ü": { key: "KeyU", shift: true, accentKey: keyTrema },
|
||||
"Ú": { key: "KeyU", shift: true, accentKey: keyAcute },
|
||||
"Û": { key: "KeyU", shift: true, accentKey: keyHat },
|
||||
"Ù": { key: "KeyU", shift: true, accentKey: keyGrave },
|
||||
"Ũ": { key: "KeyU", shift: true, accentKey: keyTilde },
|
||||
V: { key: "KeyV", shift: true },
|
||||
W: { key: "KeyW", shift: true },
|
||||
X: { key: "KeyX", shift: true },
|
||||
Y: { key: "KeyY", shift: true },
|
||||
Z: { key: "KeyZ", shift: true },
|
||||
a: { key: "KeyA" },
|
||||
"ä": { key: "KeyA", accentKey: keyTrema },
|
||||
"á": { key: "KeyA", accentKey: keyAcute },
|
||||
"â": { key: "KeyA", accentKey: keyHat },
|
||||
"à": { key: "KeyA", accentKey: keyGrave },
|
||||
"ã": { key: "KeyA", accentKey: keyTilde },
|
||||
b: { key: "KeyB" },
|
||||
c: { key: "KeyC" },
|
||||
d: { key: "KeyD" },
|
||||
e: { key: "KeyE" },
|
||||
"ë": { key: "KeyE", accentKey: keyTrema },
|
||||
"é": { key: "KeyE", accentKey: keyAcute },
|
||||
"ê": { key: "KeyE", accentKey: keyHat },
|
||||
"è": { key: "KeyE", accentKey: keyGrave },
|
||||
"ẽ": { key: "KeyE", accentKey: keyTilde },
|
||||
"€": { key: "KeyE", altRight: true },
|
||||
f: { key: "KeyF" },
|
||||
g: { key: "KeyG" },
|
||||
h: { key: "KeyH" },
|
||||
i: { key: "KeyI" },
|
||||
"ï": { key: "KeyI", accentKey: keyTrema },
|
||||
"í": { key: "KeyI", accentKey: keyAcute },
|
||||
"î": { key: "KeyI", accentKey: keyHat },
|
||||
"ì": { key: "KeyI", accentKey: keyGrave },
|
||||
"ĩ": { key: "KeyI", accentKey: keyTilde },
|
||||
j: { key: "KeyJ" },
|
||||
k: { key: "KeyK" },
|
||||
l: { key: "KeyL" },
|
||||
m: { key: "KeyM" },
|
||||
n: { key: "KeyN" },
|
||||
o: { key: "KeyO" },
|
||||
"ö": { key: "KeyO", accentKey: keyTrema },
|
||||
"ó": { key: "KeyO", accentKey: keyAcute },
|
||||
"ô": { key: "KeyO", accentKey: keyHat },
|
||||
"ò": { key: "KeyO", accentKey: keyGrave },
|
||||
"õ": { key: "KeyO", accentKey: keyTilde },
|
||||
p: { key: "KeyP" },
|
||||
q: { key: "KeyQ" },
|
||||
r: { key: "KeyR" },
|
||||
s: { key: "KeyS" },
|
||||
t: { key: "KeyT" },
|
||||
u: { key: "KeyU" },
|
||||
"ü": { key: "KeyU", accentKey: keyTrema },
|
||||
"ú": { key: "KeyU", accentKey: keyAcute },
|
||||
"û": { key: "KeyU", accentKey: keyHat },
|
||||
"ù": { key: "KeyU", accentKey: keyGrave },
|
||||
"ũ": { key: "KeyU", accentKey: keyTilde },
|
||||
v: { key: "KeyV" },
|
||||
w: { key: "KeyW" },
|
||||
x: { key: "KeyX" },
|
||||
y: { key: "KeyY" },
|
||||
z: { key: "KeyZ" },
|
||||
"º": { key: "Backquote" },
|
||||
"ª": { key: "Backquote", shift: true },
|
||||
"\\": { key: "Backquote", altRight: true },
|
||||
1: { key: "Digit1" },
|
||||
"!": { key: "Digit1", shift: true },
|
||||
"|": { key: "Digit1", altRight: true },
|
||||
2: { key: "Digit2" },
|
||||
"\"": { key: "Digit2", shift: true },
|
||||
"@": { key: "Digit2", altRight: true },
|
||||
3: { key: "Digit3" },
|
||||
"·": { key: "Digit3", shift: true },
|
||||
"#": { key: "Digit3", altRight: true },
|
||||
4: { key: "Digit4" },
|
||||
"$": { key: "Digit4", shift: true },
|
||||
5: { key: "Digit5" },
|
||||
"%": { key: "Digit5", shift: true },
|
||||
6: { key: "Digit6" },
|
||||
"&": { key: "Digit6", shift: true },
|
||||
"¬": { key: "Digit6", altRight: true },
|
||||
7: { key: "Digit7" },
|
||||
"/": { key: "Digit7", shift: true },
|
||||
8: { key: "Digit8" },
|
||||
"(": { key: "Digit8", shift: true },
|
||||
9: { key: "Digit9" },
|
||||
")": { key: "Digit9", shift: true },
|
||||
0: { key: "Digit0" },
|
||||
"=": { key: "Digit0", shift: true },
|
||||
"'": { key: "Minus" },
|
||||
"?": { key: "Minus", shift: true },
|
||||
"¡": { key: "Equal", deadKey: true },
|
||||
"¿": { key: "Equal", shift: true },
|
||||
"[": { key: "BracketLeft", altRight: true },
|
||||
"+": { key: "BracketRight" },
|
||||
"*": { key: "BracketRight", shift: true },
|
||||
"]": { key: "BracketRight", altRight: true },
|
||||
"ñ": { key: "Semicolon" },
|
||||
"Ñ": { key: "Semicolon", shift: true },
|
||||
"{": { key: "Quote", altRight: true },
|
||||
"ç": { key: "Backslash" },
|
||||
"Ç": { key: "Backslash", shift: true },
|
||||
"}": { key: "Backslash", altRight: true },
|
||||
",": { key: "Comma" },
|
||||
";": { key: "Comma", shift: true },
|
||||
".": { key: "Period" },
|
||||
":": { key: "Period", shift: true },
|
||||
"-": { key: "Slash" },
|
||||
"_": { key: "Slash", shift: true },
|
||||
"<": { key: "IntlBackslash" },
|
||||
">": { key: "IntlBackslash", shift: true },
|
||||
" ": { key: "Space" },
|
||||
"\n": { key: "Enter" },
|
||||
Enter: { key: "Enter" },
|
||||
Tab: { key: "Tab" },
|
||||
} as Record<string, KeyCombo>;
|
|
@ -0,0 +1,167 @@
|
|||
import { KeyCombo } from "../keyboardLayouts"
|
||||
|
||||
export const name = "Belgisch Nederlands";
|
||||
|
||||
const keyTrema = { key: "BracketLeft", shift: true } // tréma (umlaut), two dots placed above a vowel
|
||||
const keyHat = { key: "BracketLeft" } // accent circonflexe (accent hat), mark ^ placed above the letter
|
||||
const keyAcute = { key: "Semicolon", altRight: true } // accent aigu (acute accent), mark ´ placed above the letter
|
||||
const keyGrave = { key: "Quote", shift: true } // accent grave, mark ` placed above the letter
|
||||
const keyTilde = { key: "Slash", altRight: true } // tilde, mark ~ placed above the letter
|
||||
|
||||
export const chars = {
|
||||
A: { key: "KeyQ", shift: true },
|
||||
"Ä": { key: "KeyQ", shift: true, accentKey: keyTrema },
|
||||
"Â": { key: "KeyQ", shift: true, accentKey: keyHat },
|
||||
"Á": { key: "KeyQ", shift: true, accentKey: keyAcute },
|
||||
"À": { key: "KeyQ", shift: true, accentKey: keyGrave },
|
||||
"Ã": { key: "KeyQ", shift: true, accentKey: keyTilde },
|
||||
B: { key: "KeyB", shift: true },
|
||||
C: { key: "KeyC", shift: true },
|
||||
D: { key: "KeyD", shift: true },
|
||||
E: { key: "KeyE", shift: true },
|
||||
"Ë": { key: "KeyE", shift: true, accentKey: keyTrema },
|
||||
"Ê": { key: "KeyE", shift: true, accentKey: keyHat },
|
||||
"É": { key: "KeyE", shift: true, accentKey: keyAcute },
|
||||
"È": { key: "KeyE", shift: true, accentKey: keyGrave },
|
||||
"Ẽ": { key: "KeyE", shift: true, accentKey: keyTilde },
|
||||
F: { key: "KeyF", shift: true },
|
||||
G: { key: "KeyG", shift: true },
|
||||
H: { key: "KeyH", shift: true },
|
||||
I: { key: "KeyI", shift: true },
|
||||
"Ï": { key: "KeyI", shift: true, accentKey: keyTrema },
|
||||
"Î": { key: "KeyI", shift: true, accentKey: keyHat },
|
||||
"Í": { key: "KeyI", shift: true, accentKey: keyAcute },
|
||||
"Ì": { key: "KeyI", shift: true, accentKey: keyGrave },
|
||||
"Ĩ": { key: "KeyI", shift: true, accentKey: keyTilde },
|
||||
J: { key: "KeyJ", shift: true },
|
||||
K: { key: "KeyK", shift: true },
|
||||
L: { key: "KeyL", shift: true },
|
||||
M: { key: "Semicolon", shift: true },
|
||||
N: { key: "KeyN", shift: true },
|
||||
O: { key: "KeyO", shift: true },
|
||||
"Ö": { key: "KeyO", shift: true, accentKey: keyTrema },
|
||||
"Ô": { key: "KeyO", shift: true, accentKey: keyHat },
|
||||
"Ó": { key: "KeyO", shift: true, accentKey: keyAcute },
|
||||
"Ò": { key: "KeyO", shift: true, accentKey: keyGrave },
|
||||
"Õ": { key: "KeyO", shift: true, accentKey: keyTilde },
|
||||
P: { key: "KeyP", shift: true },
|
||||
Q: { key: "KeyA", shift: true },
|
||||
R: { key: "KeyR", shift: true },
|
||||
S: { key: "KeyS", shift: true },
|
||||
T: { key: "KeyT", shift: true },
|
||||
U: { key: "KeyU", shift: true },
|
||||
"Ü": { key: "KeyU", shift: true, accentKey: keyTrema },
|
||||
"Û": { key: "KeyU", shift: true, accentKey: keyHat },
|
||||
"Ú": { key: "KeyU", shift: true, accentKey: keyAcute },
|
||||
"Ù": { key: "KeyU", shift: true, accentKey: keyGrave },
|
||||
"Ũ": { key: "KeyU", shift: true, accentKey: keyTilde },
|
||||
V: { key: "KeyV", shift: true },
|
||||
W: { key: "KeyW", shift: true },
|
||||
X: { key: "KeyX", shift: true },
|
||||
Y: { key: "KeyZ", shift: true },
|
||||
Z: { key: "KeyY", shift: true },
|
||||
a: { key: "KeyQ" },
|
||||
"ä": { key: "KeyQ", accentKey: keyTrema },
|
||||
"â": { key: "KeyQ", accentKey: keyHat },
|
||||
"á": { key: "KeyQ", accentKey: keyAcute },
|
||||
"ã": { key: "KeyQ", accentKey: keyTilde },
|
||||
b: { key: "KeyB" },
|
||||
c: { key: "KeyC" },
|
||||
d: { key: "KeyD" },
|
||||
e: { key: "KeyE" },
|
||||
"ë": { key: "KeyE", accentKey: keyTrema },
|
||||
"ê": { key: "KeyE", accentKey: keyHat },
|
||||
"ẽ": { key: "KeyE", accentKey: keyTilde },
|
||||
"€": { key: "KeyE", altRight: true },
|
||||
f: { key: "KeyF" },
|
||||
g: { key: "KeyG" },
|
||||
h: { key: "KeyH" },
|
||||
i: { key: "KeyI" },
|
||||
"ï": { key: "KeyI", accentKey: keyTrema },
|
||||
"î": { key: "KeyI", accentKey: keyHat },
|
||||
"í": { key: "KeyI", accentKey: keyAcute },
|
||||
"ì": { key: "KeyI", accentKey: keyGrave },
|
||||
"ĩ": { key: "KeyI", accentKey: keyTilde },
|
||||
j: { key: "KeyJ" },
|
||||
k: { key: "KeyK" },
|
||||
l: { key: "KeyL" },
|
||||
m: { key: "Semicolon" },
|
||||
n: { key: "KeyN" },
|
||||
o: { key: "KeyO" },
|
||||
"ö": { key: "KeyO", accentKey: keyTrema },
|
||||
"ó": { key: "KeyO", accentKey: keyAcute },
|
||||
"ô": { key: "KeyO", accentKey: keyHat },
|
||||
"ò": { key: "KeyO", accentKey: keyGrave },
|
||||
"õ": { key: "KeyO", accentKey: keyTilde },
|
||||
p: { key: "KeyP" },
|
||||
q: { key: "KeyA" },
|
||||
r: { key: "KeyR" },
|
||||
s: { key: "KeyS" },
|
||||
t: { key: "KeyT" },
|
||||
u: { key: "KeyU" },
|
||||
"ü": { key: "KeyU", accentKey: keyTrema },
|
||||
"û": { key: "KeyU", accentKey: keyHat },
|
||||
"ú": { key: "KeyU", accentKey: keyAcute },
|
||||
"ũ": { key: "KeyU", accentKey: keyTilde },
|
||||
v: { key: "KeyV" },
|
||||
w: { key: "KeyW" },
|
||||
x: { key: "KeyX" },
|
||||
y: { key: "KeyZ" },
|
||||
z: { key: "KeyY" },
|
||||
"²": { key: "Backquote" },
|
||||
"³": { key: "Backquote", shift: true },
|
||||
"&": { key: "Digit1" },
|
||||
1: { key: "Digit1", shift: true },
|
||||
"|": { key: "Digit1", altRight: true },
|
||||
"é": { key: "Digit2" },
|
||||
2: { key: "Digit2", shift: true },
|
||||
"@": { key: "Digit2", altRight: true },
|
||||
"\"": { key: "Digit3" },
|
||||
3: { key: "Digit3", shift: true },
|
||||
"#": { key: "Digit3", altRight: true },
|
||||
"'": { key: "Digit4" },
|
||||
4: { key: "Digit4", shift: true },
|
||||
"(": { key: "Digit5" },
|
||||
5: { key: "Digit5", shift: true },
|
||||
"§": { key: "Digit6" },
|
||||
6: { key: "Digit6", shift: true },
|
||||
"^": { key: "Digit6", altRight: true },
|
||||
"è": { key: "Digit7" },
|
||||
7: { key: "Digit7", shift: true },
|
||||
"!": { key: "Digit8" },
|
||||
8: { key: "Digit8", shift: true },
|
||||
"ç": { key: "Digit9" },
|
||||
9: { key: "Digit9", shift: true },
|
||||
"{": { key: "Digit9", altRight: true },
|
||||
"à": { key: "Digit0" },
|
||||
0: { key: "Digit0", shift: true },
|
||||
"}": { key: "Digit0", altRight: true },
|
||||
")": { key: "Minus" },
|
||||
"°": { key: "Minus", shift: true },
|
||||
"-": { key: "Equal", deadKey: true },
|
||||
"_": { key: "Equal", shift: true },
|
||||
"[": { key: "BracketLeft", altRight: true },
|
||||
"$": { key: "BracketRight" },
|
||||
"*": { key: "BracketRight", altRight: true },
|
||||
"]": { key: "BracketRight", altRight: true },
|
||||
"ù": { key: "Quote" },
|
||||
"%": { key: "Quote", shift: true },
|
||||
"µ": { key: "Backslash" },
|
||||
"£": { key: "Backslash", shift: true },
|
||||
",": { key: "KeyM" },
|
||||
"?": { key: "KeyM", shift: true },
|
||||
";": { key: "Comma" },
|
||||
".": { key: "Comma", shift: true },
|
||||
":": { key: "Period" },
|
||||
"/": { key: "Period", shift: true },
|
||||
"=": { key: "Slash" },
|
||||
"+": { key: "Slash", shift: true },
|
||||
"~": { key: "Slash", deadKey: true },
|
||||
"<": { key: "IntlBackslash" },
|
||||
">": { key: "IntlBackslash", shift: true },
|
||||
"\\": { key: "IntlBackslash", altRight: true },
|
||||
" ": { key: "Space" },
|
||||
"\n": { key: "Enter" },
|
||||
Enter: { key: "Enter" },
|
||||
Tab: { key: "Tab" },
|
||||
} as Record<string, KeyCombo>;
|
|
@ -0,0 +1,14 @@
|
|||
import { KeyCombo } from "../keyboardLayouts"
|
||||
import { chars as chars_de_CH } from "./de_CH"
|
||||
|
||||
export const name = "Français de Suisse";
|
||||
|
||||
export const chars = {
|
||||
...chars_de_CH,
|
||||
"è": { key: "BracketLeft" },
|
||||
"ü": { key: "BracketLeft", shift: true },
|
||||
"é": { key: "Semicolon" },
|
||||
"ö": { key: "Semicolon", shift: true },
|
||||
"à": { key: "Quote" },
|
||||
"ä": { key: "Quote", shift: true },
|
||||
} as Record<string, KeyCombo>;
|
|
@ -0,0 +1,139 @@
|
|||
import { KeyCombo } from "../keyboardLayouts"
|
||||
|
||||
export const name = "Français";
|
||||
|
||||
const keyTrema = { key: "BracketLeft", shift: true } // tréma (umlaut), two dots placed above a vowel
|
||||
const keyHat = { key: "BracketLeft" } // accent circonflexe (accent hat), mark ^ placed above the letter
|
||||
|
||||
export const chars = {
|
||||
A: { key: "KeyQ", shift: true },
|
||||
"Ä": { key: "KeyQ", shift: true, accentKey: keyTrema },
|
||||
"Â": { key: "KeyQ", shift: true, accentKey: keyHat },
|
||||
B: { key: "KeyB", shift: true },
|
||||
C: { key: "KeyC", shift: true },
|
||||
D: { key: "KeyD", shift: true },
|
||||
E: { key: "KeyE", shift: true },
|
||||
"Ë": { key: "KeyE", shift: true, accentKey: keyTrema },
|
||||
"Ê": { key: "KeyE", shift: true, accentKey: keyHat },
|
||||
F: { key: "KeyF", shift: true },
|
||||
G: { key: "KeyG", shift: true },
|
||||
H: { key: "KeyH", shift: true },
|
||||
I: { key: "KeyI", shift: true },
|
||||
"Ï": { key: "KeyI", shift: true, accentKey: keyTrema },
|
||||
"Î": { key: "KeyI", shift: true, accentKey: keyHat },
|
||||
J: { key: "KeyJ", shift: true },
|
||||
K: { key: "KeyK", shift: true },
|
||||
L: { key: "KeyL", shift: true },
|
||||
M: { key: "Semicolon", shift: true },
|
||||
N: { key: "KeyN", shift: true },
|
||||
O: { key: "KeyO", shift: true },
|
||||
"Ö": { key: "KeyO", shift: true, accentKey: keyTrema },
|
||||
"Ô": { key: "KeyO", shift: true, accentKey: keyHat },
|
||||
P: { key: "KeyP", shift: true },
|
||||
Q: { key: "KeyA", shift: true },
|
||||
R: { key: "KeyR", shift: true },
|
||||
S: { key: "KeyS", shift: true },
|
||||
T: { key: "KeyT", shift: true },
|
||||
U: { key: "KeyU", shift: true },
|
||||
"Ü": { key: "KeyU", shift: true, accentKey: keyTrema },
|
||||
"Û": { key: "KeyU", shift: true, accentKey: keyHat },
|
||||
V: { key: "KeyV", shift: true },
|
||||
W: { key: "KeyZ", shift: true },
|
||||
X: { key: "KeyX", shift: true },
|
||||
Y: { key: "KeyY", shift: true },
|
||||
Z: { key: "KeyW", shift: true },
|
||||
a: { key: "KeyQ" },
|
||||
"ä": { key: "KeyQ", accentKey: keyTrema },
|
||||
"â": { key: "KeyQ", accentKey: keyHat },
|
||||
b: { key: "KeyB" },
|
||||
c: { key: "KeyC" },
|
||||
d: { key: "KeyD" },
|
||||
e: { key: "KeyE" },
|
||||
"ë": { key: "KeyE", accentKey: keyTrema },
|
||||
"ê": { key: "KeyE", accentKey: keyHat },
|
||||
"€": { key: "KeyE", altRight: true },
|
||||
f: { key: "KeyF" },
|
||||
g: { key: "KeyG" },
|
||||
h: { key: "KeyH" },
|
||||
i: { key: "KeyI" },
|
||||
"ï": { key: "KeyI", accentKey: keyTrema },
|
||||
"î": { key: "KeyI", accentKey: keyHat },
|
||||
j: { key: "KeyJ" },
|
||||
k: { key: "KeyK" },
|
||||
l: { key: "KeyL" },
|
||||
m: { key: "Semicolon" },
|
||||
n: { key: "KeyN" },
|
||||
o: { key: "KeyO" },
|
||||
"ö": { key: "KeyO", accentKey: keyTrema },
|
||||
"ô": { key: "KeyO", accentKey: keyHat },
|
||||
p: { key: "KeyP" },
|
||||
q: { key: "KeyA" },
|
||||
r: { key: "KeyR" },
|
||||
s: { key: "KeyS" },
|
||||
t: { key: "KeyT" },
|
||||
u: { key: "KeyU" },
|
||||
"ü": { key: "KeyU", accentKey: keyTrema },
|
||||
"û": { key: "KeyU", accentKey: keyHat },
|
||||
v: { key: "KeyV" },
|
||||
w: { key: "KeyZ" },
|
||||
x: { key: "KeyX" },
|
||||
y: { key: "KeyY" },
|
||||
z: { key: "KeyW" },
|
||||
"²": { key: "Backquote" },
|
||||
"&": { key: "Digit1" },
|
||||
1: { key: "Digit1", shift: true },
|
||||
"é": { key: "Digit2" },
|
||||
2: { key: "Digit2", shift: true },
|
||||
"~": { key: "Digit2", altRight: true },
|
||||
"\"": { key: "Digit3" },
|
||||
3: { key: "Digit3", shift: true },
|
||||
"#": { key: "Digit3", altRight: true },
|
||||
"'": { key: "Digit4" },
|
||||
4: { key: "Digit4", shift: true },
|
||||
"{": { key: "Digit4", altRight: true },
|
||||
"(": { key: "Digit5" },
|
||||
5: { key: "Digit5", shift: true },
|
||||
"[": { key: "Digit5", altRight: true },
|
||||
"-": { key: "Digit6" },
|
||||
6: { key: "Digit6", shift: true },
|
||||
"|": { key: "Digit6", altRight: true },
|
||||
"è": { key: "Digit7" },
|
||||
7: { key: "Digit7", shift: true },
|
||||
"`": { key: "Digit7", altRight: true },
|
||||
"_": { key: "Digit8" },
|
||||
8: { key: "Digit8", shift: true },
|
||||
"\\": { key: "Digit8", altRight: true },
|
||||
"ç": { key: "Digit9" },
|
||||
9: { key: "Digit9", shift: true },
|
||||
"^": { key: "Digit9", altRight: true },
|
||||
"à": { key: "Digit0" },
|
||||
0: { key: "Digit0", shift: true },
|
||||
"@": { key: "Digit0", altRight: true },
|
||||
")": { key: "Minus" },
|
||||
"°": { key: "Minus", shift: true },
|
||||
"]": { key: "Minus", altRight: true },
|
||||
"=": { key: "Equal" },
|
||||
"+": { key: "Equal", shift: true },
|
||||
"}": { key: "Equal", altRight: true },
|
||||
"$": { key: "BracketRight" },
|
||||
"£": { key: "BracketRight", shift: true },
|
||||
"¤": { key: "BracketRight", altRight: true },
|
||||
"ù": { key: "Quote" },
|
||||
"%": { key: "Quote", shift: true },
|
||||
"*": { key: "Backslash" },
|
||||
"µ": { key: "Backslash", shift: true },
|
||||
",": { key: "KeyM" },
|
||||
"?": { key: "KeyM", shift: true },
|
||||
";": { key: "Comma" },
|
||||
".": { key: "Comma", shift: true },
|
||||
":": { key: "Period" },
|
||||
"/": { key: "Period", shift: true },
|
||||
"!": { key: "Slash" },
|
||||
"§": { key: "Slash", shift: true },
|
||||
"<": { key: "IntlBackslash" },
|
||||
">": { key: "IntlBackslash", shift: true },
|
||||
" ": { key: "Space" },
|
||||
"\n": { key: "Enter" },
|
||||
Enter: { key: "Enter" },
|
||||
Tab: { key: "Tab" },
|
||||
} as Record<string, KeyCombo>;
|
|
@ -0,0 +1,113 @@
|
|||
import { KeyCombo } from "../keyboardLayouts"
|
||||
|
||||
export const name = "Italiano";
|
||||
|
||||
export const chars = {
|
||||
A: { key: "KeyA", shift: true },
|
||||
B: { key: "KeyB", shift: true },
|
||||
C: { key: "KeyC", shift: true },
|
||||
D: { key: "KeyD", shift: true },
|
||||
E: { key: "KeyE", shift: true },
|
||||
F: { key: "KeyF", shift: true },
|
||||
G: { key: "KeyG", shift: true },
|
||||
H: { key: "KeyH", shift: true },
|
||||
I: { key: "KeyI", shift: true },
|
||||
J: { key: "KeyJ", shift: true },
|
||||
K: { key: "KeyK", shift: true },
|
||||
L: { key: "KeyL", shift: true },
|
||||
M: { key: "KeyM", shift: true },
|
||||
N: { key: "KeyN", shift: true },
|
||||
O: { key: "KeyO", shift: true },
|
||||
P: { key: "KeyP", shift: true },
|
||||
Q: { key: "KeyQ", shift: true },
|
||||
R: { key: "KeyR", shift: true },
|
||||
S: { key: "KeyS", shift: true },
|
||||
T: { key: "KeyT", shift: true },
|
||||
U: { key: "KeyU", shift: true },
|
||||
V: { key: "KeyV", shift: true },
|
||||
W: { key: "KeyW", shift: true },
|
||||
X: { key: "KeyX", shift: true },
|
||||
Y: { key: "KeyY", shift: true },
|
||||
Z: { key: "KeyZ", shift: true },
|
||||
a: { key: "KeyA" },
|
||||
b: { key: "KeyB" },
|
||||
c: { key: "KeyC" },
|
||||
d: { key: "KeyD" },
|
||||
e: { key: "KeyE" },
|
||||
"€": { key: "KeyE", altRight: true },
|
||||
f: { key: "KeyF" },
|
||||
g: { key: "KeyG" },
|
||||
h: { key: "KeyH" },
|
||||
i: { key: "KeyI" },
|
||||
j: { key: "KeyJ" },
|
||||
k: { key: "KeyK" },
|
||||
l: { key: "KeyL" },
|
||||
m: { key: "KeyM" },
|
||||
n: { key: "KeyN" },
|
||||
o: { key: "KeyO" },
|
||||
p: { key: "KeyP" },
|
||||
q: { key: "KeyQ" },
|
||||
r: { key: "KeyR" },
|
||||
s: { key: "KeyS" },
|
||||
t: { key: "KeyT" },
|
||||
u: { key: "KeyU" },
|
||||
v: { key: "KeyV" },
|
||||
w: { key: "KeyW" },
|
||||
x: { key: "KeyX" },
|
||||
y: { key: "KeyY" },
|
||||
z: { key: "KeyZ" },
|
||||
"\\": { key: "Backquote" },
|
||||
"|": { key: "Backquote", shift: true },
|
||||
1: { key: "Digit1" },
|
||||
"!": { key: "Digit1", shift: true },
|
||||
2: { key: "Digit2" },
|
||||
"\"": { key: "Digit2", shift: true },
|
||||
3: { key: "Digit3" },
|
||||
"£": { key: "Digit3", shift: true },
|
||||
4: { key: "Digit4" },
|
||||
"$": { key: "Digit4", shift: true },
|
||||
5: { key: "Digit5" },
|
||||
"%": { key: "Digit5", shift: true },
|
||||
6: { key: "Digit6" },
|
||||
"&": { key: "Digit6", shift: true },
|
||||
7: { key: "Digit7" },
|
||||
"/": { key: "Digit7", shift: true },
|
||||
8: { key: "Digit8" },
|
||||
"(": { key: "Digit8", shift: true },
|
||||
9: { key: "Digit9" },
|
||||
")": { key: "Digit9", shift: true },
|
||||
0: { key: "Digit0" },
|
||||
"=": { key: "Digit0", shift: true },
|
||||
"'": { key: "Minus" },
|
||||
"?": { key: "Minus", shift: true },
|
||||
"ì": { key: "Equal" },
|
||||
"^": { key: "Equal", shift: true },
|
||||
"è": { key: "BracketLeft" },
|
||||
"é": { key: "BracketLeft", shift: true },
|
||||
"[": { key: "BracketLeft", altRight: true },
|
||||
"{": { key: "BracketLeft", shift: true, altRight: true },
|
||||
"+": { key: "BracketRight" },
|
||||
"*": { key: "BracketRight", shift: true },
|
||||
"]": { key: "BracketRight", altRight: true },
|
||||
"}": { key: "BracketRight", shift: true, altRight: true },
|
||||
"ò": { key: "Semicolon" },
|
||||
"ç": { key: "Semicolon", shift: true },
|
||||
"@": { key: "Semicolon", altRight: true },
|
||||
"à": { key: "Quote" },
|
||||
"°": { key: "Quote", shift: true },
|
||||
"#": { key: "Quote", altRight: true },
|
||||
"ù": { key: "Backslash" },
|
||||
"§": { key: "Backslash", shift: true },
|
||||
",": { key: "Comma" },
|
||||
";": { key: "Comma", shift: true },
|
||||
".": { key: "Period" },
|
||||
":": { key: "Period", shift: true },
|
||||
"-": { key: "Slash" },
|
||||
"_": { key: "Slash", shift: true },
|
||||
"<": { key: "IntlBackslash" },
|
||||
">": { key: "IntlBackslash", shift: true },
|
||||
" ": { key: "Space" },
|
||||
"\n": { key: "Enter" },
|
||||
Enter: { key: "Enter" },
|
||||
Tab: { key: "Tab" },
|
||||
} as Record<string, KeyCombo>;
|
|
@ -0,0 +1,167 @@
|
|||
import { KeyCombo } from "../keyboardLayouts"
|
||||
|
||||
export const name = "Norsk bokmål";
|
||||
|
||||
const keyTrema = { key: "BracketRight" } // tréma (umlaut), two dots placed above a vowel
|
||||
const keyAcute = { key: "Equal", altRight: true } // accent aigu (acute accent), mark ´ placed above the letter
|
||||
const keyHat = { key: "BracketRight", shift: true } // accent circonflexe (accent hat), mark ^ placed above the letter
|
||||
const keyGrave = { key: "Equal", shift: true } // accent grave, mark ` placed above the letter
|
||||
const keyTilde = { key: "BracketRight", altRight: true } // tilde, mark ~ placed above the letter
|
||||
|
||||
export const chars = {
|
||||
A: { key: "KeyA", shift: true },
|
||||
"Ä": { key: "KeyA", shift: true, accentKey: keyTrema },
|
||||
"Á": { key: "KeyA", shift: true, accentKey: keyAcute },
|
||||
"Â": { key: "KeyA", shift: true, accentKey: keyHat },
|
||||
"À": { key: "KeyA", shift: true, accentKey: keyGrave },
|
||||
"Ã": { key: "KeyA", shift: true, accentKey: keyTilde },
|
||||
B: { key: "KeyB", shift: true },
|
||||
C: { key: "KeyC", shift: true },
|
||||
D: { key: "KeyD", shift: true },
|
||||
E: { key: "KeyE", shift: true },
|
||||
"Ë": { key: "KeyE", shift: true, accentKey: keyTrema },
|
||||
"É": { key: "KeyE", shift: true, accentKey: keyAcute },
|
||||
"Ê": { key: "KeyE", shift: true, accentKey: keyHat },
|
||||
"È": { key: "KeyE", shift: true, accentKey: keyGrave },
|
||||
"Ẽ": { key: "KeyE", shift: true, accentKey: keyTilde },
|
||||
F: { key: "KeyF", shift: true },
|
||||
G: { key: "KeyG", shift: true },
|
||||
H: { key: "KeyH", shift: true },
|
||||
I: { key: "KeyI", shift: true },
|
||||
"Ï": { key: "KeyI", shift: true, accentKey: keyTrema },
|
||||
"Í": { key: "KeyI", shift: true, accentKey: keyAcute },
|
||||
"Î": { key: "KeyI", shift: true, accentKey: keyHat },
|
||||
"Ì": { key: "KeyI", shift: true, accentKey: keyGrave },
|
||||
"Ĩ": { key: "KeyI", shift: true, accentKey: keyTilde },
|
||||
J: { key: "KeyJ", shift: true },
|
||||
K: { key: "KeyK", shift: true },
|
||||
L: { key: "KeyL", shift: true },
|
||||
M: { key: "KeyM", shift: true },
|
||||
N: { key: "KeyN", shift: true },
|
||||
O: { key: "KeyO", shift: true },
|
||||
"Ö": { key: "KeyO", shift: true, accentKey: keyTrema },
|
||||
"Ó": { key: "KeyO", shift: true, accentKey: keyAcute },
|
||||
"Ô": { key: "KeyO", shift: true, accentKey: keyHat },
|
||||
"Ò": { key: "KeyO", shift: true, accentKey: keyGrave },
|
||||
"Õ": { key: "KeyO", shift: true, accentKey: keyTilde },
|
||||
P: { key: "KeyP", shift: true },
|
||||
Q: { key: "KeyQ", shift: true },
|
||||
R: { key: "KeyR", shift: true },
|
||||
S: { key: "KeyS", shift: true },
|
||||
T: { key: "KeyT", shift: true },
|
||||
U: { key: "KeyU", shift: true },
|
||||
"Ü": { key: "KeyU", shift: true, accentKey: keyTrema },
|
||||
"Ú": { key: "KeyU", shift: true, accentKey: keyAcute },
|
||||
"Û": { key: "KeyU", shift: true, accentKey: keyHat },
|
||||
"Ù": { key: "KeyU", shift: true, accentKey: keyGrave },
|
||||
"Ũ": { key: "KeyU", shift: true, accentKey: keyTilde },
|
||||
V: { key: "KeyV", shift: true },
|
||||
W: { key: "KeyW", shift: true },
|
||||
X: { key: "KeyX", shift: true },
|
||||
Y: { key: "KeyZ", shift: true },
|
||||
Z: { key: "KeyY", shift: true },
|
||||
a: { key: "KeyA" },
|
||||
"ä": { key: "KeyA", accentKey: keyTrema },
|
||||
"á": { key: "KeyA", accentKey: keyAcute },
|
||||
"â": { key: "KeyA", accentKey: keyHat },
|
||||
"à": { key: "KeyA", accentKey: keyGrave },
|
||||
"ã": { key: "KeyA", accentKey: keyTilde },
|
||||
b: { key: "KeyB" },
|
||||
c: { key: "KeyC" },
|
||||
d: { key: "KeyD" },
|
||||
e: { key: "KeyE" },
|
||||
"ë": { key: "KeyE", accentKey: keyTrema },
|
||||
"é": { key: "KeyE", accentKey: keyAcute },
|
||||
"ê": { key: "KeyE", accentKey: keyHat },
|
||||
"è": { key: "KeyE", accentKey: keyGrave },
|
||||
"ẽ": { key: "KeyE", accentKey: keyTilde },
|
||||
"€": { key: "KeyE", altRight: true },
|
||||
f: { key: "KeyF" },
|
||||
g: { key: "KeyG" },
|
||||
h: { key: "KeyH" },
|
||||
i: { key: "KeyI" },
|
||||
"ï": { key: "KeyI", accentKey: keyTrema },
|
||||
"í": { key: "KeyI", accentKey: keyAcute },
|
||||
"î": { key: "KeyI", accentKey: keyHat },
|
||||
"ì": { key: "KeyI", accentKey: keyGrave },
|
||||
"ĩ": { key: "KeyI", accentKey: keyTilde },
|
||||
j: { key: "KeyJ" },
|
||||
k: { key: "KeyK" },
|
||||
l: { key: "KeyL" },
|
||||
m: { key: "KeyM" },
|
||||
n: { key: "KeyN" },
|
||||
o: { key: "KeyO" },
|
||||
"ö": { key: "KeyO", accentKey: keyTrema },
|
||||
"ó": { key: "KeyO", accentKey: keyAcute },
|
||||
"ô": { key: "KeyO", accentKey: keyHat },
|
||||
"ò": { key: "KeyO", accentKey: keyGrave },
|
||||
"õ": { key: "KeyO", accentKey: keyTilde },
|
||||
p: { key: "KeyP" },
|
||||
q: { key: "KeyQ" },
|
||||
r: { key: "KeyR" },
|
||||
s: { key: "KeyS" },
|
||||
t: { key: "KeyT" },
|
||||
u: { key: "KeyU" },
|
||||
"ü": { key: "KeyU", accentKey: keyTrema },
|
||||
"ú": { key: "KeyU", accentKey: keyAcute },
|
||||
"û": { key: "KeyU", accentKey: keyHat },
|
||||
"ù": { key: "KeyU", accentKey: keyGrave },
|
||||
"ũ": { key: "KeyU", accentKey: keyTilde },
|
||||
v: { key: "KeyV" },
|
||||
w: { key: "KeyW" },
|
||||
x: { key: "KeyX" },
|
||||
y: { key: "KeyZ" },
|
||||
z: { key: "KeyY" },
|
||||
"|": { key: "Backquote" },
|
||||
"§": { key: "Backquote", shift: true },
|
||||
1: { key: "Digit1" },
|
||||
"!": { key: "Digit1", shift: true },
|
||||
2: { key: "Digit2" },
|
||||
"\"": { key: "Digit2", shift: true },
|
||||
"@": { key: "Digit2", altRight: true },
|
||||
3: { key: "Digit3" },
|
||||
"#": { key: "Digit3", shift: true },
|
||||
"£": { key: "Digit3", altRight: true },
|
||||
4: { key: "Digit4" },
|
||||
"¤": { key: "Digit4", shift: true },
|
||||
"$": { key: "Digit4", altRight: true },
|
||||
5: { key: "Digit5" },
|
||||
"%": { key: "Digit5", shift: true },
|
||||
6: { key: "Digit6" },
|
||||
"&": { key: "Digit6", shift: true },
|
||||
7: { key: "Digit7" },
|
||||
"/": { key: "Digit7", shift: true },
|
||||
"{": { key: "Digit7", altRight: true },
|
||||
8: { key: "Digit8" },
|
||||
"(": { key: "Digit8", shift: true },
|
||||
"[": { key: "Digit8", altRight: true },
|
||||
9: { key: "Digit9" },
|
||||
")": { key: "Digit9", shift: true },
|
||||
"]": { key: "Digit9", altRight: true },
|
||||
0: { key: "Digit0" },
|
||||
"=": { key: "Digit0", shift: true },
|
||||
"}": { key: "Digit0", altRight: true },
|
||||
"+": { key: "Minus" },
|
||||
"?": { key: "Minus", shift: true },
|
||||
"\\": { key: "Equal" },
|
||||
"å": { key: "BracketLeft" },
|
||||
"Å": { key: "BracketLeft", shift: true },
|
||||
"ø": { key: "Semicolon" },
|
||||
"Ø": { key: "Semicolon", shift: true },
|
||||
"æ": { key: "Quote" },
|
||||
"Æ": { key: "Quote", shift: true },
|
||||
"'": { key: "Backslash" },
|
||||
"*": { key: "Backslash", shift: true },
|
||||
",": { key: "Comma" },
|
||||
";": { key: "Comma", shift: true },
|
||||
".": { key: "Period" },
|
||||
":": { key: "Period", shift: true },
|
||||
"-": { key: "Slash" },
|
||||
"_": { key: "Slash", shift: true },
|
||||
"<": { key: "IntlBackslash" },
|
||||
">": { key: "IntlBackslash", shift: true },
|
||||
" ": { key: "Space" },
|
||||
"\n": { key: "Enter" },
|
||||
Enter: { key: "Enter" },
|
||||
Tab: { key: "Tab" },
|
||||
} as Record<string, KeyCombo>;
|
|
@ -0,0 +1,164 @@
|
|||
import { KeyCombo } from "../keyboardLayouts"
|
||||
|
||||
export const name = "Svenska";
|
||||
|
||||
const keyTrema = { key: "BracketRight" } // tréma (umlaut), two dots placed above a vowel
|
||||
const keyAcute = { key: "Equal" } // accent aigu (acute accent), mark ´ placed above the letter
|
||||
const keyHat = { key: "BracketRight", shift: true } // accent circonflexe (accent hat), mark ^ placed above the letter
|
||||
const keyGrave = { key: "Equal", shift: true } // accent grave, mark ` placed above the letter
|
||||
const keyTilde = { key: "BracketRight", altRight: true } // tilde, mark ~ placed above the letter
|
||||
|
||||
export const chars = {
|
||||
A: { key: "KeyA", shift: true },
|
||||
"Á": { key: "KeyA", shift: true, accentKey: keyAcute },
|
||||
"Â": { key: "KeyA", shift: true, accentKey: keyHat },
|
||||
"À": { key: "KeyA", shift: true, accentKey: keyGrave },
|
||||
"Ã": { key: "KeyA", shift: true, accentKey: keyTilde },
|
||||
B: { key: "KeyB", shift: true },
|
||||
C: { key: "KeyC", shift: true },
|
||||
D: { key: "KeyD", shift: true },
|
||||
E: { key: "KeyE", shift: true },
|
||||
"Ë": { key: "KeyE", shift: true, accentKey: keyTrema },
|
||||
"É": { key: "KeyE", shift: true, accentKey: keyAcute },
|
||||
"Ê": { key: "KeyE", shift: true, accentKey: keyHat },
|
||||
"È": { key: "KeyE", shift: true, accentKey: keyGrave },
|
||||
"Ẽ": { key: "KeyE", shift: true, accentKey: keyTilde },
|
||||
F: { key: "KeyF", shift: true },
|
||||
G: { key: "KeyG", shift: true },
|
||||
H: { key: "KeyH", shift: true },
|
||||
I: { key: "KeyI", shift: true },
|
||||
"Ï": { key: "KeyI", shift: true, accentKey: keyTrema },
|
||||
"Í": { key: "KeyI", shift: true, accentKey: keyAcute },
|
||||
"Î": { key: "KeyI", shift: true, accentKey: keyHat },
|
||||
"Ì": { key: "KeyI", shift: true, accentKey: keyGrave },
|
||||
"Ĩ": { key: "KeyI", shift: true, accentKey: keyTilde },
|
||||
J: { key: "KeyJ", shift: true },
|
||||
K: { key: "KeyK", shift: true },
|
||||
L: { key: "KeyL", shift: true },
|
||||
M: { key: "KeyM", shift: true },
|
||||
N: { key: "KeyN", shift: true },
|
||||
O: { key: "KeyO", shift: true },
|
||||
"Ó": { key: "KeyO", shift: true, accentKey: keyAcute },
|
||||
"Ô": { key: "KeyO", shift: true, accentKey: keyHat },
|
||||
"Ò": { key: "KeyO", shift: true, accentKey: keyGrave },
|
||||
"Õ": { key: "KeyO", shift: true, accentKey: keyTilde },
|
||||
P: { key: "KeyP", shift: true },
|
||||
Q: { key: "KeyQ", shift: true },
|
||||
R: { key: "KeyR", shift: true },
|
||||
S: { key: "KeyS", shift: true },
|
||||
T: { key: "KeyT", shift: true },
|
||||
U: { key: "KeyU", shift: true },
|
||||
"Ü": { key: "KeyU", shift: true, accentKey: keyTrema },
|
||||
"Ú": { key: "KeyU", shift: true, accentKey: keyAcute },
|
||||
"Û": { key: "KeyU", shift: true, accentKey: keyHat },
|
||||
"Ù": { key: "KeyU", shift: true, accentKey: keyGrave },
|
||||
"Ũ": { key: "KeyU", shift: true, accentKey: keyTilde },
|
||||
V: { key: "KeyV", shift: true },
|
||||
W: { key: "KeyW", shift: true },
|
||||
X: { key: "KeyX", shift: true },
|
||||
Y: { key: "KeyZ", shift: true },
|
||||
Z: { key: "KeyY", shift: true },
|
||||
a: { key: "KeyA" },
|
||||
"á": { key: "KeyA", accentKey: keyAcute },
|
||||
"â": { key: "KeyA", accentKey: keyHat },
|
||||
"à": { key: "KeyA", accentKey: keyGrave },
|
||||
"ã": { key: "KeyA", accentKey: keyTilde },
|
||||
b: { key: "KeyB" },
|
||||
c: { key: "KeyC" },
|
||||
d: { key: "KeyD" },
|
||||
e: { key: "KeyE" },
|
||||
"ë": { key: "KeyE", accentKey: keyTrema },
|
||||
"é": { key: "KeyE", accentKey: keyAcute },
|
||||
"ê": { key: "KeyE", accentKey: keyHat },
|
||||
"è": { key: "KeyE", accentKey: keyGrave },
|
||||
"ẽ": { key: "KeyE", accentKey: keyTilde },
|
||||
"€": { key: "KeyE", altRight: true },
|
||||
f: { key: "KeyF" },
|
||||
g: { key: "KeyG" },
|
||||
h: { key: "KeyH" },
|
||||
i: { key: "KeyI" },
|
||||
"ï": { key: "KeyI", accentKey: keyTrema },
|
||||
"í": { key: "KeyI", accentKey: keyAcute },
|
||||
"î": { key: "KeyI", accentKey: keyHat },
|
||||
"ì": { key: "KeyI", accentKey: keyGrave },
|
||||
"ĩ": { key: "KeyI", accentKey: keyTilde },
|
||||
j: { key: "KeyJ" },
|
||||
k: { key: "KeyK" },
|
||||
l: { key: "KeyL" },
|
||||
m: { key: "KeyM" },
|
||||
n: { key: "KeyN" },
|
||||
o: { key: "KeyO" },
|
||||
"ó": { key: "KeyO", accentKey: keyAcute },
|
||||
"ô": { key: "KeyO", accentKey: keyHat },
|
||||
"ò": { key: "KeyO", accentKey: keyGrave },
|
||||
"õ": { key: "KeyO", accentKey: keyTilde },
|
||||
p: { key: "KeyP" },
|
||||
q: { key: "KeyQ" },
|
||||
r: { key: "KeyR" },
|
||||
s: { key: "KeyS" },
|
||||
t: { key: "KeyT" },
|
||||
u: { key: "KeyU" },
|
||||
"ü": { key: "KeyU", accentKey: keyTrema },
|
||||
"ú": { key: "KeyU", accentKey: keyAcute },
|
||||
"û": { key: "KeyU", accentKey: keyHat },
|
||||
"ù": { key: "KeyU", accentKey: keyGrave },
|
||||
"ũ": { key: "KeyU", accentKey: keyTilde },
|
||||
v: { key: "KeyV" },
|
||||
w: { key: "KeyW" },
|
||||
x: { key: "KeyX" },
|
||||
y: { key: "KeyZ" },
|
||||
z: { key: "KeyY" },
|
||||
"§": { key: "Backquote" },
|
||||
"½": { key: "Backquote", shift: true },
|
||||
1: { key: "Digit1" },
|
||||
"!": { key: "Digit1", shift: true },
|
||||
2: { key: "Digit2" },
|
||||
"\"": { key: "Digit2", shift: true },
|
||||
"@": { key: "Digit2", altRight: true },
|
||||
3: { key: "Digit3" },
|
||||
"#": { key: "Digit3", shift: true },
|
||||
"£": { key: "Digit3", altRight: true },
|
||||
4: { key: "Digit4" },
|
||||
"¤": { key: "Digit4", shift: true },
|
||||
"$": { key: "Digit4", altRight: true },
|
||||
5: { key: "Digit5" },
|
||||
"%": { key: "Digit5", shift: true },
|
||||
6: { key: "Digit6" },
|
||||
"&": { key: "Digit6", shift: true },
|
||||
7: { key: "Digit7" },
|
||||
"/": { key: "Digit7", shift: true },
|
||||
"{": { key: "Digit7", altRight: true },
|
||||
8: { key: "Digit8" },
|
||||
"(": { key: "Digit8", shift: true },
|
||||
"[": { key: "Digit8", altRight: true },
|
||||
9: { key: "Digit9" },
|
||||
")": { key: "Digit9", shift: true },
|
||||
"]": { key: "Digit9", altRight: true },
|
||||
0: { key: "Digit0" },
|
||||
"=": { key: "Digit0", shift: true },
|
||||
"}": { key: "Digit0", altRight: true },
|
||||
"+": { key: "Minus" },
|
||||
"?": { key: "Minus", shift: true },
|
||||
"\\": { key: "Minus", altRight: true },
|
||||
"å": { key: "BracketLeft" },
|
||||
"Å": { key: "BracketLeft", shift: true },
|
||||
"ö": { key: "Semicolon" },
|
||||
"Ö": { key: "Semicolon", shift: true },
|
||||
"ä": { key: "Quote" },
|
||||
"Ä": { key: "Quote", shift: true },
|
||||
"'": { key: "Backslash" },
|
||||
"*": { key: "Backslash", shift: true },
|
||||
",": { key: "Comma" },
|
||||
";": { key: "Comma", shift: true },
|
||||
".": { key: "Period" },
|
||||
":": { key: "Period", shift: true },
|
||||
"-": { key: "Slash" },
|
||||
"_": { key: "Slash", shift: true },
|
||||
"<": { key: "IntlBackslash" },
|
||||
">": { key: "IntlBackslash", shift: true },
|
||||
"|": { key: "IntlBackslash", altRight: true },
|
||||
" ": { key: "Space" },
|
||||
"\n": { key: "Enter" },
|
||||
Enter: { key: "Enter" },
|
||||
Tab: { key: "Tab" },
|
||||
} as Record<string, KeyCombo>;
|
|
@ -43,7 +43,7 @@ export const keys = {
|
|||
F13: 0x68,
|
||||
Home: 0x4a,
|
||||
Insert: 0x49,
|
||||
IntlBackslash: 0x31,
|
||||
IntlBackslash: 0x64,
|
||||
KeyA: 0x04,
|
||||
KeyB: 0x05,
|
||||
KeyC: 0x06,
|
||||
|
@ -104,116 +104,6 @@ export const keys = {
|
|||
Tab: 0x2b,
|
||||
} as Record<string, number>;
|
||||
|
||||
export const chars = {
|
||||
A: { key: "KeyA", shift: true },
|
||||
B: { key: "KeyB", shift: true },
|
||||
C: { key: "KeyC", shift: true },
|
||||
D: { key: "KeyD", shift: true },
|
||||
E: { key: "KeyE", shift: true },
|
||||
F: { key: "KeyF", shift: true },
|
||||
G: { key: "KeyG", shift: true },
|
||||
H: { key: "KeyH", shift: true },
|
||||
I: { key: "KeyI", shift: true },
|
||||
J: { key: "KeyJ", shift: true },
|
||||
K: { key: "KeyK", shift: true },
|
||||
L: { key: "KeyL", shift: true },
|
||||
M: { key: "KeyM", shift: true },
|
||||
N: { key: "KeyN", shift: true },
|
||||
O: { key: "KeyO", shift: true },
|
||||
P: { key: "KeyP", shift: true },
|
||||
Q: { key: "KeyQ", shift: true },
|
||||
R: { key: "KeyR", shift: true },
|
||||
S: { key: "KeyS", shift: true },
|
||||
T: { key: "KeyT", shift: true },
|
||||
U: { key: "KeyU", shift: true },
|
||||
V: { key: "KeyV", shift: true },
|
||||
W: { key: "KeyW", shift: true },
|
||||
X: { key: "KeyX", shift: true },
|
||||
Y: { key: "KeyY", shift: true },
|
||||
Z: { key: "KeyZ", shift: true },
|
||||
a: { key: "KeyA", shift: false },
|
||||
b: { key: "KeyB", shift: false },
|
||||
c: { key: "KeyC", shift: false },
|
||||
d: { key: "KeyD", shift: false },
|
||||
e: { key: "KeyE", shift: false },
|
||||
f: { key: "KeyF", shift: false },
|
||||
g: { key: "KeyG", shift: false },
|
||||
h: { key: "KeyH", shift: false },
|
||||
i: { key: "KeyI", shift: false },
|
||||
j: { key: "KeyJ", shift: false },
|
||||
k: { key: "KeyK", shift: false },
|
||||
l: { key: "KeyL", shift: false },
|
||||
m: { key: "KeyM", shift: false },
|
||||
n: { key: "KeyN", shift: false },
|
||||
o: { key: "KeyO", shift: false },
|
||||
p: { key: "KeyP", shift: false },
|
||||
q: { key: "KeyQ", shift: false },
|
||||
r: { key: "KeyR", shift: false },
|
||||
s: { key: "KeyS", shift: false },
|
||||
t: { key: "KeyT", shift: false },
|
||||
u: { key: "KeyU", shift: false },
|
||||
v: { key: "KeyV", shift: false },
|
||||
w: { key: "KeyW", shift: false },
|
||||
x: { key: "KeyX", shift: false },
|
||||
y: { key: "KeyY", shift: false },
|
||||
z: { key: "KeyZ", shift: false },
|
||||
1: { key: "Digit1", shift: false },
|
||||
"!": { key: "Digit1", shift: true },
|
||||
2: { key: "Digit2", shift: false },
|
||||
"@": { key: "Digit2", shift: true },
|
||||
3: { key: "Digit3", shift: false },
|
||||
"#": { key: "Digit3", shift: true },
|
||||
4: { key: "Digit4", shift: false },
|
||||
$: { key: "Digit4", shift: true },
|
||||
"%": { key: "Digit5", shift: true },
|
||||
5: { key: "Digit5", shift: false },
|
||||
"^": { key: "Digit6", shift: true },
|
||||
6: { key: "Digit6", shift: false },
|
||||
"&": { key: "Digit7", shift: true },
|
||||
7: { key: "Digit7", shift: false },
|
||||
"*": { key: "Digit8", shift: true },
|
||||
8: { key: "Digit8", shift: false },
|
||||
"(": { key: "Digit9", shift: true },
|
||||
9: { key: "Digit9", shift: false },
|
||||
")": { key: "Digit0", shift: true },
|
||||
0: { key: "Digit0", shift: false },
|
||||
"-": { key: "Minus", shift: false },
|
||||
_: { key: "Minus", shift: true },
|
||||
"=": { key: "Equal", shift: false },
|
||||
"+": { key: "Equal", shift: true },
|
||||
"'": { key: "Quote", shift: false },
|
||||
'"': { key: "Quote", shift: true },
|
||||
",": { key: "Comma", shift: false },
|
||||
"<": { key: "Comma", shift: true },
|
||||
"/": { key: "Slash", shift: false },
|
||||
"?": { key: "Slash", shift: true },
|
||||
".": { key: "Period", shift: false },
|
||||
">": { key: "Period", shift: true },
|
||||
";": { key: "Semicolon", shift: false },
|
||||
":": { key: "Semicolon", shift: true },
|
||||
"[": { key: "BracketLeft", shift: false },
|
||||
"{": { key: "BracketLeft", shift: true },
|
||||
"]": { key: "BracketRight", shift: false },
|
||||
"}": { key: "BracketRight", shift: true },
|
||||
"\\": { key: "Backslash", shift: false },
|
||||
"|": { key: "Backslash", shift: true },
|
||||
"`": { key: "Backquote", shift: false },
|
||||
"~": { key: "Backquote", shift: true },
|
||||
"§": { key: "IntlBackslash", shift: false },
|
||||
"±": { key: "IntlBackslash", shift: true },
|
||||
" ": { key: "Space", shift: false },
|
||||
"\n": { key: "Enter", shift: false },
|
||||
Enter: { key: "Enter", shift: false },
|
||||
Tab: { key: "Tab", shift: false },
|
||||
PrintScreen: { key: "Prt Sc", shift: false },
|
||||
SystemRequest: { key: "Prt Sc", shift: true },
|
||||
ScrollLock: { key: "ScrollLock", shift: false},
|
||||
Pause: { key: "Pause", shift: false },
|
||||
Break: { key: "Pause", shift: true },
|
||||
Insert: { key: "Insert", shift: false },
|
||||
Delete: { key: "Delete", shift: false },
|
||||
} as Record<string, { key: string | number; shift: boolean }>;
|
||||
|
||||
export const modifiers = {
|
||||
ControlLeft: 0x01,
|
||||
ControlRight: 0x10,
|
||||
|
|
|
@ -32,7 +32,8 @@ import { CLOUD_API, DEVICE_API } from "./ui.config";
|
|||
import OtherSessionRoute from "./routes/devices.$id.other-session";
|
||||
import MountRoute from "./routes/devices.$id.mount";
|
||||
import * as SettingsRoute from "./routes/devices.$id.settings";
|
||||
import SettingsKeyboardMouseRoute from "./routes/devices.$id.settings.mouse";
|
||||
import SettingsMouseRoute from "./routes/devices.$id.settings.mouse";
|
||||
import SettingsKeyboardRoute from "./routes/devices.$id.settings.keyboard";
|
||||
import api from "./api";
|
||||
import * as SettingsIndexRoute from "./routes/devices.$id.settings._index";
|
||||
import SettingsAdvancedRoute from "./routes/devices.$id.settings.advanced";
|
||||
|
@ -147,7 +148,11 @@ if (isOnDevice) {
|
|||
},
|
||||
{
|
||||
path: "mouse",
|
||||
element: <SettingsKeyboardMouseRoute />,
|
||||
element: <SettingsMouseRoute />,
|
||||
},
|
||||
{
|
||||
path: "keyboard",
|
||||
element: <SettingsKeyboardRoute />,
|
||||
},
|
||||
{
|
||||
path: "advanced",
|
||||
|
@ -276,7 +281,11 @@ if (isOnDevice) {
|
|||
},
|
||||
{
|
||||
path: "mouse",
|
||||
element: <SettingsKeyboardMouseRoute />,
|
||||
element: <SettingsMouseRoute />,
|
||||
},
|
||||
{
|
||||
path: "keyboard",
|
||||
element: <SettingsKeyboardRoute />,
|
||||
},
|
||||
{
|
||||
path: "advanced",
|
||||
|
|
|
@ -7,7 +7,7 @@ import {
|
|||
LuCheck,
|
||||
LuUpload,
|
||||
} from "react-icons/lu";
|
||||
import { PlusCircleIcon , ExclamationTriangleIcon } from "@heroicons/react/20/solid";
|
||||
import { PlusCircleIcon, ExclamationTriangleIcon } from "@heroicons/react/20/solid";
|
||||
import { TrashIcon } from "@heroicons/react/16/solid";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
|
||||
|
@ -38,7 +38,6 @@ import {
|
|||
useRTCStore,
|
||||
} from "../hooks/stores";
|
||||
|
||||
|
||||
export default function MountRoute() {
|
||||
const navigate = useNavigate();
|
||||
{
|
||||
|
@ -283,8 +282,8 @@ function ModeSelectionView({
|
|||
|
||||
return (
|
||||
<div className="w-full space-y-4">
|
||||
<div className="animate-fadeIn space-y-0">
|
||||
<h2 className="text-lg font-bold leading-tight dark:text-white">
|
||||
<div className="animate-fadeIn space-y-0 opacity-0">
|
||||
<h2 className="text-lg leading-tight font-bold dark:text-white">
|
||||
Virtual Media Source
|
||||
</h2>
|
||||
<div className="text-sm leading-snug text-slate-600 dark:text-slate-400">
|
||||
|
@ -320,7 +319,7 @@ function ModeSelectionView({
|
|||
].map(({ label, description, value: mode, icon: Icon, tag, disabled }, index) => (
|
||||
<div
|
||||
key={label}
|
||||
className={cx("animate-fadeIn")}
|
||||
className="animate-fadeIn opacity-0"
|
||||
style={{
|
||||
animationDuration: "0.7s",
|
||||
animationDelay: `${25 * (index * 5)}ms`,
|
||||
|
@ -337,7 +336,7 @@ function ModeSelectionView({
|
|||
)}
|
||||
>
|
||||
<div
|
||||
className="relative z-50 flex select-none flex-col items-start p-4"
|
||||
className="relative z-50 flex flex-col items-start p-4 select-none"
|
||||
onClick={() =>
|
||||
disabled ? null : setSelectedMode(mode as "browser" | "url" | "device")
|
||||
}
|
||||
|
@ -365,7 +364,7 @@ function ModeSelectionView({
|
|||
value={mode}
|
||||
disabled={disabled}
|
||||
checked={selectedMode === mode}
|
||||
className="absolute right-4 top-4 h-4 w-4 text-blue-700"
|
||||
className="absolute top-4 right-4 form-radio h-4 w-4 rounded-full text-blue-700"
|
||||
/>
|
||||
</div>
|
||||
</Card>
|
||||
|
@ -373,7 +372,7 @@ function ModeSelectionView({
|
|||
))}
|
||||
</div>
|
||||
<div
|
||||
className="flex animate-fadeIn justify-end"
|
||||
className="flex animate-fadeIn justify-end opacity-0"
|
||||
style={{
|
||||
animationDuration: "0.7s",
|
||||
animationDelay: "0.2s",
|
||||
|
@ -437,19 +436,19 @@ function BrowserFileView({
|
|||
className="block cursor-pointer select-none"
|
||||
>
|
||||
<div
|
||||
className="group animate-fadeIn"
|
||||
className="group animate-fadeIn opacity-0"
|
||||
style={{
|
||||
animationDuration: "0.7s",
|
||||
}}
|
||||
>
|
||||
<Card className="outline-dashed transition-all duration-300 hover:bg-blue-50/50">
|
||||
<Card className="transition-all duration-300 outline-dashed">
|
||||
<div className="w-full px-4 py-12">
|
||||
<div className="flex h-full flex-col items-center justify-center text-center">
|
||||
{selectedFile ? (
|
||||
<>
|
||||
<div className="space-y-1">
|
||||
<LuHardDrive className="mx-auto h-6 w-6 text-blue-700" />
|
||||
<h3 className="text-sm font-semibold leading-none">
|
||||
<h3 className="text-sm leading-none font-semibold">
|
||||
{formatters.truncateMiddle(selectedFile.name, 40)}
|
||||
</h3>
|
||||
<p className="text-xs leading-none text-slate-700">
|
||||
|
@ -460,7 +459,7 @@ function BrowserFileView({
|
|||
) : (
|
||||
<div className="space-y-1">
|
||||
<PlusCircleIcon className="mx-auto h-6 w-6 text-blue-700" />
|
||||
<h3 className="text-sm font-semibold leading-none">
|
||||
<h3 className="text-sm leading-none font-semibold">
|
||||
Click to select a file
|
||||
</h3>
|
||||
<p className="text-xs leading-none text-slate-700">
|
||||
|
@ -483,7 +482,7 @@ function BrowserFileView({
|
|||
</div>
|
||||
|
||||
<div
|
||||
className="flex w-full animate-fadeIn items-end justify-between"
|
||||
className="flex w-full animate-fadeIn items-end justify-between opacity-0"
|
||||
style={{
|
||||
animationDuration: "0.7s",
|
||||
animationDelay: "0.1s",
|
||||
|
@ -578,7 +577,7 @@ function UrlView({
|
|||
/>
|
||||
|
||||
<div
|
||||
className="animate-fadeIn"
|
||||
className="animate-fadeIn opacity-0"
|
||||
style={{
|
||||
animationDuration: "0.7s",
|
||||
}}
|
||||
|
@ -593,7 +592,7 @@ function UrlView({
|
|||
/>
|
||||
</div>
|
||||
<div
|
||||
className="flex w-full animate-fadeIn items-end justify-between"
|
||||
className="flex w-full animate-fadeIn items-end justify-between opacity-0"
|
||||
style={{
|
||||
animationDuration: "0.7s",
|
||||
animationDelay: "0.1s",
|
||||
|
@ -619,7 +618,7 @@ function UrlView({
|
|||
|
||||
<hr className="border-slate-800/30 dark:border-slate-300/20" />
|
||||
<div
|
||||
className="animate-fadeIn"
|
||||
className="animate-fadeIn opacity-0"
|
||||
style={{
|
||||
animationDuration: "0.7s",
|
||||
animationDelay: "0.2s",
|
||||
|
@ -628,13 +627,13 @@ function UrlView({
|
|||
<h2 className="mb-2 text-sm font-semibold text-black dark:text-white">
|
||||
Popular images
|
||||
</h2>
|
||||
<Card className="divide-y-slate-800/30 w-full divide-y dark:divide-slate-300/20">
|
||||
<Card className="w-full divide-y divide-slate-800/20 dark:divide-slate-300/20">
|
||||
{popularImages.map((image, index) => (
|
||||
<div key={index} className="flex items-center justify-between gap-x-4 p-3.5">
|
||||
<div className="flex items-center gap-x-4">
|
||||
<img src={image.icon} alt={`${image.name} Icon`} className="w-6" />
|
||||
<div className="flex flex-col gap-y-1">
|
||||
<h3 className="text-sm font-semibold leading-none dark:text-white">
|
||||
<h3 className="text-sm leading-none font-semibold dark:text-white">
|
||||
{formatters.truncateMiddle(image.name, 40)}
|
||||
</h3>
|
||||
{image.description && (
|
||||
|
@ -797,7 +796,7 @@ function DeviceFileView({
|
|||
description="Select an image to mount from the JetKVM storage"
|
||||
/>
|
||||
<div
|
||||
className="w-full animate-fadeIn"
|
||||
className="w-full animate-fadeIn opacity-0"
|
||||
style={{
|
||||
animationDuration: "0.7s",
|
||||
animationDelay: "0.1s",
|
||||
|
@ -809,7 +808,7 @@ function DeviceFileView({
|
|||
<div className="space-y-3">
|
||||
<div className="space-y-1">
|
||||
<PlusCircleIcon className="mx-auto h-6 w-6 text-blue-700 dark:text-blue-500" />
|
||||
<h3 className="text-sm font-semibold leading-none text-black dark:text-white">
|
||||
<h3 className="text-sm leading-none font-semibold text-black dark:text-white">
|
||||
No images available
|
||||
</h3>
|
||||
<p className="text-xs leading-none text-slate-700 dark:text-slate-300">
|
||||
|
@ -827,7 +826,7 @@ function DeviceFileView({
|
|||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="divide-y-slate-800/30 w-full divide-y dark:divide-slate-300/20">
|
||||
<div className="w-full divide-y divide-slate-800/20 dark:divide-slate-300/20">
|
||||
{currentFiles.map((file, index) => (
|
||||
<PreUploadedImageItem
|
||||
key={index}
|
||||
|
@ -886,7 +885,7 @@ function DeviceFileView({
|
|||
|
||||
{onStorageFiles.length > 0 ? (
|
||||
<div
|
||||
className="flex animate-fadeIn items-end justify-between"
|
||||
className="flex animate-fadeIn items-end justify-between opacity-0"
|
||||
style={{
|
||||
animationDuration: "0.7s",
|
||||
animationDelay: "0.15s",
|
||||
|
@ -914,7 +913,7 @@ function DeviceFileView({
|
|||
</div>
|
||||
) : (
|
||||
<div
|
||||
className="flex animate-fadeIn items-end justify-end"
|
||||
className="flex animate-fadeIn items-end justify-end opacity-0"
|
||||
style={{
|
||||
animationDuration: "0.7s",
|
||||
animationDelay: "0.15s",
|
||||
|
@ -927,7 +926,7 @@ function DeviceFileView({
|
|||
)}
|
||||
<hr className="border-slate-800/20 dark:border-slate-300/20" />
|
||||
<div
|
||||
className="animate-fadeIn space-y-2"
|
||||
className="animate-fadeIn space-y-2 opacity-0"
|
||||
style={{
|
||||
animationDuration: "0.7s",
|
||||
animationDelay: "0.20s",
|
||||
|
@ -959,7 +958,7 @@ function DeviceFileView({
|
|||
|
||||
{onStorageFiles.length > 0 && (
|
||||
<div
|
||||
className="w-full animate-fadeIn"
|
||||
className="w-full animate-fadeIn opacity-0"
|
||||
style={{
|
||||
animationDuration: "0.7s",
|
||||
animationDelay: "0.25s",
|
||||
|
@ -1251,7 +1250,7 @@ function UploadFileView({
|
|||
}
|
||||
/>
|
||||
<div
|
||||
className="animate-fadeIn space-y-2"
|
||||
className="animate-fadeIn space-y-2 opacity-0"
|
||||
style={{
|
||||
animationDuration: "0.7s",
|
||||
}}
|
||||
|
@ -1267,7 +1266,7 @@ function UploadFileView({
|
|||
<div className="group">
|
||||
<Card
|
||||
className={cx("transition-all duration-300", {
|
||||
"cursor-pointer hover:bg-blue-900/50 dark:hover:bg-blue-900/50":
|
||||
"cursor-pointer hover:bg-blue-50/50 dark:hover:bg-blue-900/50":
|
||||
uploadState === "idle",
|
||||
})}
|
||||
>
|
||||
|
@ -1282,7 +1281,7 @@ function UploadFileView({
|
|||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
<h3 className="text-sm font-semibold leading-none text-black dark:text-white">
|
||||
<h3 className="text-sm leading-none font-semibold text-black dark:text-white">
|
||||
{incompleteFileName
|
||||
? `Click to select "${incompleteFileName.replace(".incomplete", "")}"`
|
||||
: "Click to select a file"}
|
||||
|
@ -1336,7 +1335,7 @@ function UploadFileView({
|
|||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
<h3 className="text-sm font-semibold leading-none text-black dark:text-white">
|
||||
<h3 className="text-sm leading-none font-semibold text-black dark:text-white">
|
||||
Upload successful
|
||||
</h3>
|
||||
<p className="text-xs leading-none text-slate-700 dark:text-slate-300">
|
||||
|
@ -1365,7 +1364,7 @@ function UploadFileView({
|
|||
{/* Display upload error if present */}
|
||||
{uploadError && (
|
||||
<div
|
||||
className="mt-2 animate-fadeIn truncate text-sm text-red-600 dark:text-red-400"
|
||||
className="mt-2 animate-fadeIn truncate text-sm text-red-600 dark:text-red-400 opacity-0"
|
||||
style={{ animationDuration: "0.7s" }}
|
||||
>
|
||||
Error: {uploadError}
|
||||
|
@ -1373,7 +1372,7 @@ function UploadFileView({
|
|||
)}
|
||||
|
||||
<div
|
||||
className="flex w-full animate-fadeIn items-end"
|
||||
className="flex w-full animate-fadeIn items-end opacity-0"
|
||||
style={{
|
||||
animationDuration: "0.7s",
|
||||
animationDelay: "0.1s",
|
||||
|
@ -1422,7 +1421,7 @@ function ErrorView({
|
|||
<div className="space-y-2">
|
||||
<div className="flex items-center space-x-2 text-red-600">
|
||||
<ExclamationTriangleIcon className="h-6 w-6" />
|
||||
<h2 className="text-lg font-bold leading-tight">Mount Error</h2>
|
||||
<h2 className="text-lg leading-tight font-bold">Mount Error</h2>
|
||||
</div>
|
||||
<p className="text-sm leading-snug text-slate-600">
|
||||
An error occurred while attempting to mount the media. Please try again.
|
||||
|
@ -1481,8 +1480,8 @@ function PreUploadedImageItem({
|
|||
}}
|
||||
>
|
||||
<div className="flex items-center gap-x-4">
|
||||
<div className="select-none space-y-0.5">
|
||||
<div className="text-sm font-semibold leading-none dark:text-white">
|
||||
<div className="space-y-0.5 select-none">
|
||||
<div className="text-sm leading-none font-semibold dark:text-white">
|
||||
{formatters.truncateMiddle(name, 45)}
|
||||
</div>
|
||||
<div className="flex items-center text-sm">
|
||||
|
@ -1494,7 +1493,7 @@ function PreUploadedImageItem({
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="relative flex select-none items-center gap-x-3">
|
||||
<div className="relative flex items-center gap-x-3 select-none">
|
||||
<div
|
||||
className={cx("opacity-0 transition-opacity duration-200", {
|
||||
"w-auto opacity-100": isHovering,
|
||||
|
@ -1518,7 +1517,7 @@ function PreUploadedImageItem({
|
|||
checked={isSelected}
|
||||
onChange={onSelect}
|
||||
name={name}
|
||||
className="h-3 w-3 border-slate-800/30 bg-white text-blue-700 focus:ring-blue-500 disabled:opacity-30 dark:border-slate-300/20 dark:bg-slate-800"
|
||||
className="form-radio h-3 w-3 border-slate-800/30 bg-white text-blue-700 focus:ring-blue-500 disabled:opacity-30 dark:border-slate-300/20 dark:bg-slate-800"
|
||||
onClick={e => e.stopPropagation()} // Prevent double-firing of onSelect
|
||||
/>
|
||||
) : (
|
||||
|
@ -1540,7 +1539,7 @@ function PreUploadedImageItem({
|
|||
function ViewHeader({ title, description }: { title: string; description: string }) {
|
||||
return (
|
||||
<div className="space-y-0">
|
||||
<h2 className="text-lg font-bold leading-tight text-black dark:text-white">
|
||||
<h2 className="text-lg leading-tight font-bold text-black dark:text-white">
|
||||
{title}
|
||||
</h2>
|
||||
<div className="text-sm leading-snug text-slate-600 dark:text-slate-400">
|
||||
|
@ -1558,7 +1557,7 @@ function UsbModeSelector({
|
|||
setUsbMode: (mode: RemoteVirtualMediaState["mode"]) => void;
|
||||
}) {
|
||||
return (
|
||||
<div className="flex select-none flex-col items-start space-y-1">
|
||||
<div className="flex flex-col items-start space-y-1 select-none">
|
||||
<label className="text-sm font-semibold text-black dark:text-white">Mount as</label>
|
||||
<div className="flex space-x-4">
|
||||
<label htmlFor="cdrom" className="flex items-center">
|
||||
|
@ -1568,7 +1567,7 @@ function UsbModeSelector({
|
|||
name="mountType"
|
||||
onChange={() => setUsbMode("CDROM")}
|
||||
checked={usbMode === "CDROM"}
|
||||
className="h-3 w-3 border-slate-800/30 bg-white text-blue-700 transition-opacity focus:ring-blue-500 disabled:opacity-30 dark:bg-slate-800"
|
||||
className="form-radio h-3 w-3 rounded-full border-slate-800/30 bg-white text-blue-700 transition-opacity focus:ring-blue-500 disabled:opacity-30 dark:bg-slate-800"
|
||||
/>
|
||||
<span className="ml-2 text-sm font-medium text-slate-900 dark:text-white">
|
||||
CD/DVD
|
||||
|
@ -1581,13 +1580,11 @@ function UsbModeSelector({
|
|||
name="mountType"
|
||||
checked={usbMode === "Disk"}
|
||||
onChange={() => setUsbMode("Disk")}
|
||||
className="h-3 w-3 border-slate-800/30 bg-white text-blue-700 transition-opacity focus:ring-blue-500 disabled:opacity-30 dark:bg-slate-800"
|
||||
className="form-radio h-3 w-3 rounded-full border-slate-800/30 bg-white text-blue-700 transition-opacity focus:ring-blue-500 disabled:opacity-30 dark:bg-slate-800"
|
||||
/>
|
||||
<div className="ml-2 flex flex-col gap-y-0">
|
||||
<span className="text-sm font-medium leading-none text-slate-900 opacity-50 dark:text-white">
|
||||
Disk
|
||||
</span>
|
||||
</div>
|
||||
<span className="ml-2 text-sm font-medium text-slate-900 dark:text-white">
|
||||
Disk
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -409,7 +409,7 @@ export default function SettingsAccessIndexRoute() {
|
|||
.
|
||||
</div>
|
||||
</div>
|
||||
<hr className="block w-full dark:border-slate-600" />
|
||||
<hr className="block w-full border-slate-800/20 dark:border-slate-300/20" />
|
||||
|
||||
<div>
|
||||
<LinkButton
|
||||
|
@ -469,4 +469,4 @@ export default function SettingsAccessIndexRoute() {
|
|||
);
|
||||
}
|
||||
|
||||
SettingsAccessIndexRoute.loader = loader;
|
||||
SettingsAccessIndexRoute.loader = loader;
|
||||
|
|
|
@ -43,9 +43,10 @@ export default function SettingsGeneralUpdateRoute() {
|
|||
|
||||
export interface SystemVersionInfo {
|
||||
local: { appVersion: string; systemVersion: string };
|
||||
remote: { appVersion: string; systemVersion: string };
|
||||
remote?: { appVersion: string; systemVersion: string };
|
||||
systemUpdateAvailable: boolean;
|
||||
appUpdateAvailable: boolean;
|
||||
error?: string;
|
||||
}
|
||||
|
||||
export function Dialog({
|
||||
|
@ -142,13 +143,19 @@ function LoadingState({
|
|||
return new Promise<SystemVersionInfo>((resolve, reject) => {
|
||||
send("getUpdateStatus", {}, async resp => {
|
||||
if ("error" in resp) {
|
||||
notifications.error("Failed to check for updates");
|
||||
notifications.error(`Failed to check for updates: ${resp.error}`);
|
||||
reject(new Error("Failed to check for updates"));
|
||||
} else {
|
||||
const result = resp.result as SystemVersionInfo;
|
||||
setAppVersion(result.local.appVersion);
|
||||
setSystemVersion(result.local.systemVersion);
|
||||
resolve(result);
|
||||
|
||||
if (result.error) {
|
||||
notifications.error(`Failed to check for updates: ${result.error}`);
|
||||
reject(new Error("Failed to check for updates"));
|
||||
} else {
|
||||
resolve(result);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
@ -235,9 +242,9 @@ function UpdatingDeviceState({
|
|||
|
||||
console.log(
|
||||
`For ${type}:\n` +
|
||||
` Download Progress: ${downloadProgress}% (${otaState[`${type}DownloadProgress`]})\n` +
|
||||
` Update Progress: ${updateProgress}% (${otaState[`${type}UpdateProgress`]})\n` +
|
||||
` Verification Progress: ${verificationProgress}% (${otaState[`${type}VerificationProgress`]})`,
|
||||
` Download Progress: ${downloadProgress}% (${otaState[`${type}DownloadProgress`]})\n` +
|
||||
` Update Progress: ${updateProgress}% (${otaState[`${type}UpdateProgress`]})\n` +
|
||||
` Verification Progress: ${verificationProgress}% (${otaState[`${type}VerificationProgress`]})`,
|
||||
);
|
||||
|
||||
if (type === "app") {
|
||||
|
@ -442,13 +449,14 @@ function UpdateAvailableState({
|
|||
{versionInfo?.systemUpdateAvailable ? (
|
||||
<>
|
||||
<span className="font-semibold">System:</span>{" "}
|
||||
{versionInfo?.remote.systemVersion}
|
||||
{versionInfo?.remote?.systemVersion}
|
||||
<br />
|
||||
</>
|
||||
) : null}
|
||||
{versionInfo?.appUpdateAvailable ? (
|
||||
<>
|
||||
<span className="font-semibold">App:</span> {versionInfo?.remote.appVersion}
|
||||
<span className="font-semibold">App:</span>{" "}
|
||||
{versionInfo?.remote?.appVersion}
|
||||
</>
|
||||
) : null}
|
||||
</p>
|
||||
|
|
|
@ -0,0 +1,77 @@
|
|||
import { useCallback, useEffect } from "react";
|
||||
|
||||
import { useSettingsStore } from "@/hooks/stores";
|
||||
import { useJsonRpc } from "@/hooks/useJsonRpc";
|
||||
import notifications from "@/notifications";
|
||||
import { SettingsPageHeader } from "@components/SettingsPageheader";
|
||||
import { layouts } from "@/keyboardLayouts";
|
||||
|
||||
import { FeatureFlag } from "../components/FeatureFlag";
|
||||
import { SelectMenuBasic } from "../components/SelectMenuBasic";
|
||||
|
||||
import { SettingsItem } from "./devices.$id.settings";
|
||||
|
||||
export default function SettingsKeyboardRoute() {
|
||||
const keyboardLayout = useSettingsStore(state => state.keyboardLayout);
|
||||
const setKeyboardLayout = useSettingsStore(
|
||||
state => state.setKeyboardLayout,
|
||||
);
|
||||
|
||||
const layoutOptions = Object.entries(layouts).map(([code, language]) => { return { value: code, label: language } })
|
||||
|
||||
const [send] = useJsonRpc();
|
||||
|
||||
useEffect(() => {
|
||||
send("getKeyboardLayout", {}, resp => {
|
||||
if ("error" in resp) return;
|
||||
setKeyboardLayout(resp.result as string);
|
||||
});
|
||||
}, []);
|
||||
|
||||
const onKeyboardLayoutChange = useCallback(
|
||||
(e: React.ChangeEvent<HTMLSelectElement>) => {
|
||||
const layout = e.target.value;
|
||||
send("setKeyboardLayout", { layout }, resp => {
|
||||
if ("error" in resp) {
|
||||
notifications.error(
|
||||
`Failed to set keyboard layout: ${resp.error.data || "Unknown error"}`,
|
||||
);
|
||||
}
|
||||
notifications.success("Keyboard layout set successfully");
|
||||
setKeyboardLayout(layout);
|
||||
});
|
||||
},
|
||||
[send, setKeyboardLayout],
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<SettingsPageHeader
|
||||
title="Keyboard"
|
||||
description="Configure keyboard layout settings for your device"
|
||||
/>
|
||||
|
||||
<div className="space-y-4">
|
||||
<FeatureFlag minAppVersion="0.4.0" name="Paste text">
|
||||
{ /* this menu item could be renamed to plain "Keyboard layout" in the future, when also the virtual keyboard layout mappings are being implemented */ }
|
||||
<SettingsItem
|
||||
title="Paste text"
|
||||
description="Keyboard layout of target operating system"
|
||||
>
|
||||
<SelectMenuBasic
|
||||
size="SM"
|
||||
label=""
|
||||
fullWidth
|
||||
value={keyboardLayout}
|
||||
onChange={onKeyboardLayoutChange}
|
||||
options={layoutOptions}
|
||||
/>
|
||||
</SettingsItem>
|
||||
<p className="text-xs text-slate-600 dark:text-slate-400">
|
||||
Pasting text sends individual key strokes to the target device. The keyboard layout determines which key codes are being sent. Ensure that the keyboard layout in JetKVM matches the settings in the operating system.
|
||||
</p>
|
||||
</FeatureFlag>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
|
@ -15,7 +15,7 @@ import { cx } from "../cva.config";
|
|||
|
||||
import { SettingsItem } from "./devices.$id.settings";
|
||||
|
||||
export default function SettingsKeyboardMouseRoute() {
|
||||
export default function SettingsMouseRoute() {
|
||||
const hideCursor = useSettingsStore(state => state.isCursorHidden);
|
||||
const setHideCursor = useSettingsStore(state => state.setCursorVisibility);
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { useCallback, useEffect, useRef, useState } from "react";
|
||||
import dayjs from "dayjs";
|
||||
import relativeTime from "dayjs/plugin/relativeTime";
|
||||
import { ArrowPathIcon } from "@heroicons/react/24/outline";
|
||||
import { LuEthernetPort } from "react-icons/lu";
|
||||
|
||||
import {
|
||||
IPv4Mode,
|
||||
|
@ -16,13 +16,18 @@ import {
|
|||
import { useJsonRpc } from "@/hooks/useJsonRpc";
|
||||
import { Button } from "@components/Button";
|
||||
import { GridCard } from "@components/Card";
|
||||
import InputField from "@components/InputField";
|
||||
import InputField, { InputFieldWithLabel } from "@components/InputField";
|
||||
import { SelectMenuBasic } from "@/components/SelectMenuBasic";
|
||||
import { SettingsPageHeader } from "@/components/SettingsPageheader";
|
||||
import Fieldset from "@/components/Fieldset";
|
||||
import { ConfirmDialog } from "@/components/ConfirmDialog";
|
||||
import notifications from "@/notifications";
|
||||
|
||||
import Ipv6NetworkCard from "../components/Ipv6NetworkCard";
|
||||
import EmptyCard from "../components/EmptyCard";
|
||||
import AutoHeight from "../components/AutoHeight";
|
||||
import DhcpLeaseCard from "../components/DhcpLeaseCard";
|
||||
|
||||
import { SettingsItem } from "./devices.$id.settings";
|
||||
|
||||
dayjs.extend(relativeTime);
|
||||
|
@ -56,15 +61,11 @@ export function LifeTimeLabel({ lifetime }: { lifetime: string }) {
|
|||
|
||||
return (
|
||||
<>
|
||||
<strong>{dayjs(lifetime).format("YYYY-MM-DD HH:mm")}</strong>
|
||||
{remaining && (
|
||||
<>
|
||||
{" "}
|
||||
<span className="text-xs text-slate-700 dark:text-slate-300">
|
||||
({remaining})
|
||||
</span>
|
||||
</>
|
||||
)}
|
||||
<span className="text-sm font-medium">{remaining && <> {remaining}</>}</span>
|
||||
<span className="text-xs text-slate-700 dark:text-slate-300">
|
||||
{" "}
|
||||
({dayjs(lifetime).format("YYYY-MM-DD HH:mm")})
|
||||
</span>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
@ -114,6 +115,14 @@ export default function SettingsNetworkRoute() {
|
|||
});
|
||||
}, [send]);
|
||||
|
||||
const getNetworkState = useCallback(() => {
|
||||
send("getNetworkState", {}, resp => {
|
||||
if ("error" in resp) return;
|
||||
console.log(resp.result);
|
||||
setNetworkState(resp.result as NetworkState);
|
||||
});
|
||||
}, [send, setNetworkState]);
|
||||
|
||||
const setNetworkSettingsRemote = useCallback(
|
||||
(settings: NetworkSettings) => {
|
||||
setNetworkSettingsLoaded(false);
|
||||
|
@ -129,21 +138,14 @@ export default function SettingsNetworkRoute() {
|
|||
// We need to update the firstNetworkSettings ref to the new settings so we can use it to determine if the settings have changed
|
||||
firstNetworkSettings.current = resp.result as NetworkSettings;
|
||||
setNetworkSettings(resp.result as NetworkSettings);
|
||||
getNetworkState();
|
||||
setNetworkSettingsLoaded(true);
|
||||
notifications.success("Network settings saved");
|
||||
});
|
||||
},
|
||||
[send],
|
||||
[getNetworkState, send],
|
||||
);
|
||||
|
||||
const getNetworkState = useCallback(() => {
|
||||
send("getNetworkState", {}, resp => {
|
||||
if ("error" in resp) return;
|
||||
console.log(resp.result);
|
||||
setNetworkState(resp.result as NetworkState);
|
||||
});
|
||||
}, [send, setNetworkState]);
|
||||
|
||||
const handleRenewLease = useCallback(() => {
|
||||
send("renewDHCPLease", {}, resp => {
|
||||
if ("error" in resp) {
|
||||
|
@ -171,10 +173,6 @@ export default function SettingsNetworkRoute() {
|
|||
setNetworkSettings({ ...networkSettings, lldp_mode: value as LLDPMode });
|
||||
};
|
||||
|
||||
// const handleLldpTxTlvsChange = (value: string[]) => {
|
||||
// setNetworkSettings({ ...networkSettings, lldp_tx_tlvs: value });
|
||||
// };
|
||||
|
||||
const handleMdnsModeChange = (value: mDNSMode | string) => {
|
||||
setNetworkSettings({ ...networkSettings, mdns_mode: value as mDNSMode });
|
||||
};
|
||||
|
@ -258,7 +256,7 @@ export default function SettingsNetworkRoute() {
|
|||
</div>
|
||||
|
||||
<div className="space-y-4">
|
||||
<div className="space-y-4">
|
||||
<div className="space-y-1">
|
||||
<SettingsItem
|
||||
title="Domain"
|
||||
description="Network domain suffix for the device"
|
||||
|
@ -277,19 +275,17 @@ export default function SettingsNetworkRoute() {
|
|||
</div>
|
||||
</SettingsItem>
|
||||
{selectedDomainOption === "custom" && (
|
||||
<div className="flex items-center justify-between gap-x-2">
|
||||
<InputField
|
||||
<div className="mt-2 w-1/3 border-l border-slate-800/10 pl-4 dark:border-slate-300/20">
|
||||
<InputFieldWithLabel
|
||||
size="SM"
|
||||
type="text"
|
||||
label="Custom Domain"
|
||||
placeholder="home"
|
||||
value={customDomain}
|
||||
onChange={e => setCustomDomain(e.target.value)}
|
||||
/>
|
||||
<Button
|
||||
size="SM"
|
||||
theme="primary"
|
||||
text="Save Domain"
|
||||
onClick={() => handleCustomDomainChange(customDomain)}
|
||||
onChange={e => {
|
||||
setCustomDomain(e.target.value);
|
||||
handleCustomDomainChange(e.target.value);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
@ -359,209 +355,35 @@ export default function SettingsNetworkRoute() {
|
|||
])}
|
||||
/>
|
||||
</SettingsItem>
|
||||
{networkState?.dhcp_lease && (
|
||||
<GridCard>
|
||||
<div className="p-4">
|
||||
<div className="space-y-4">
|
||||
<h3 className="text-base font-bold text-slate-900 dark:text-white">
|
||||
DHCP Lease
|
||||
</h3>
|
||||
|
||||
<div className="flex gap-x-6 gap-y-2">
|
||||
<div className="flex-1 space-y-2">
|
||||
{networkState?.dhcp_lease?.ip && (
|
||||
<div className="flex justify-between border-slate-800/10 pt-2 dark:border-slate-300/20">
|
||||
<span className="text-sm text-slate-600 dark:text-slate-400">
|
||||
IP Address
|
||||
</span>
|
||||
<span className="text-sm font-medium">
|
||||
{networkState?.dhcp_lease?.ip}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{networkState?.dhcp_lease?.netmask && (
|
||||
<div className="flex justify-between border-t border-slate-800/10 pt-2 dark:border-slate-300/20">
|
||||
<span className="text-sm text-slate-600 dark:text-slate-400">
|
||||
Subnet Mask
|
||||
</span>
|
||||
<span className="text-sm font-medium">
|
||||
{networkState?.dhcp_lease?.netmask}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{networkState?.dhcp_lease?.dns && (
|
||||
<div className="flex justify-between border-t border-slate-800/10 pt-2 dark:border-slate-300/20">
|
||||
<span className="text-sm text-slate-600 dark:text-slate-400">
|
||||
DNS Servers
|
||||
</span>
|
||||
<span className="text-right text-sm font-medium">
|
||||
{networkState?.dhcp_lease?.dns.map(dns => (
|
||||
<div key={dns}>{dns}</div>
|
||||
))}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{networkState?.dhcp_lease?.broadcast && (
|
||||
<div className="flex justify-between border-t border-slate-800/10 pt-2 dark:border-slate-300/20">
|
||||
<span className="text-sm text-slate-600 dark:text-slate-400">
|
||||
Broadcast
|
||||
</span>
|
||||
<span className="text-sm font-medium">
|
||||
{networkState?.dhcp_lease?.broadcast}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{networkState?.dhcp_lease?.domain && (
|
||||
<div className="flex justify-between border-t border-slate-800/10 pt-2 dark:border-slate-300/20">
|
||||
<span className="text-sm text-slate-600 dark:text-slate-400">
|
||||
Domain
|
||||
</span>
|
||||
<span className="text-sm font-medium">
|
||||
{networkState?.dhcp_lease?.domain}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{networkState?.dhcp_lease?.ntp_servers &&
|
||||
networkState?.dhcp_lease?.ntp_servers.length > 0 && (
|
||||
<div className="flex justify-between gap-x-8 border-t border-slate-800/10 pt-2 dark:border-slate-300/20">
|
||||
<div className="w-full grow text-sm text-slate-600 dark:text-slate-400">
|
||||
NTP Servers
|
||||
</div>
|
||||
<div className="shrink text-right text-sm font-medium">
|
||||
{networkState?.dhcp_lease?.ntp_servers.map(server => (
|
||||
<div key={server}>{server}</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{networkState?.dhcp_lease?.hostname && (
|
||||
<div className="flex justify-between border-t border-slate-800/10 pt-2 dark:border-slate-300/20">
|
||||
<span className="text-sm text-slate-600 dark:text-slate-400">
|
||||
Hostname
|
||||
</span>
|
||||
<span className="text-sm font-medium">
|
||||
{networkState?.dhcp_lease?.hostname}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
<AutoHeight>
|
||||
{!networkSettingsLoaded && !networkState?.dhcp_lease ? (
|
||||
<GridCard>
|
||||
<div className="p-4">
|
||||
<div className="space-y-4">
|
||||
<h3 className="text-base font-bold text-slate-900 dark:text-white">
|
||||
DHCP Lease Information
|
||||
</h3>
|
||||
<div className="animate-pulse space-y-3">
|
||||
<div className="h-4 w-1/3 rounded bg-slate-200 dark:bg-slate-700" />
|
||||
<div className="h-4 w-1/2 rounded bg-slate-200 dark:bg-slate-700" />
|
||||
<div className="h-4 w-1/3 rounded bg-slate-200 dark:bg-slate-700" />
|
||||
</div>
|
||||
|
||||
<div className="flex-1 space-y-2">
|
||||
{networkState?.dhcp_lease?.routers &&
|
||||
networkState?.dhcp_lease?.routers.length > 0 && (
|
||||
<div className="flex justify-between pt-2">
|
||||
<span className="text-sm text-slate-600 dark:text-slate-400">
|
||||
Gateway
|
||||
</span>
|
||||
<span className="text-right text-sm font-medium">
|
||||
{networkState?.dhcp_lease?.routers.map(router => (
|
||||
<div key={router}>{router}</div>
|
||||
))}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{networkState?.dhcp_lease?.server_id && (
|
||||
<div className="flex justify-between border-t border-slate-800/10 pt-2 dark:border-slate-300/20">
|
||||
<span className="text-sm text-slate-600 dark:text-slate-400">
|
||||
DHCP Server
|
||||
</span>
|
||||
<span className="text-sm font-medium">
|
||||
{networkState?.dhcp_lease?.server_id}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{networkState?.dhcp_lease?.lease_expiry && (
|
||||
<div className="flex justify-between border-t border-slate-800/10 pt-2 dark:border-slate-300/20">
|
||||
<span className="text-sm text-slate-600 dark:text-slate-400">
|
||||
Lease Expires
|
||||
</span>
|
||||
<span className="text-sm font-medium">
|
||||
<LifeTimeLabel
|
||||
lifetime={`${networkState?.dhcp_lease?.lease_expiry}`}
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{networkState?.dhcp_lease?.mtu && (
|
||||
<div className="flex justify-between border-t border-slate-800/10 pt-2 dark:border-slate-300/20">
|
||||
<span className="text-sm text-slate-600 dark:text-slate-400">
|
||||
MTU
|
||||
</span>
|
||||
<span className="text-sm font-medium">
|
||||
{networkState?.dhcp_lease?.mtu}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{networkState?.dhcp_lease?.ttl && (
|
||||
<div className="flex justify-between border-t border-slate-800/10 pt-2 dark:border-slate-300/20">
|
||||
<span className="text-sm text-slate-600 dark:text-slate-400">
|
||||
TTL
|
||||
</span>
|
||||
<span className="text-sm font-medium">
|
||||
{networkState?.dhcp_lease?.ttl}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{networkState?.dhcp_lease?.bootp_next_server && (
|
||||
<div className="flex justify-between border-t border-slate-800/10 pt-2 dark:border-slate-300/20">
|
||||
<span className="text-sm text-slate-600 dark:text-slate-400">
|
||||
Boot Next Server
|
||||
</span>
|
||||
<span className="text-sm font-medium">
|
||||
{networkState?.dhcp_lease?.bootp_next_server}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{networkState?.dhcp_lease?.bootp_server_name && (
|
||||
<div className="flex justify-between border-t border-slate-800/10 pt-2 dark:border-slate-300/20">
|
||||
<span className="text-sm text-slate-600 dark:text-slate-400">
|
||||
Boot Server Name
|
||||
</span>
|
||||
<span className="text-sm font-medium">
|
||||
{networkState?.dhcp_lease?.bootp_server_name}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{networkState?.dhcp_lease?.bootp_file && (
|
||||
<div className="flex justify-between border-t border-slate-800/10 pt-2 dark:border-slate-300/20">
|
||||
<span className="text-sm text-slate-600 dark:text-slate-400">
|
||||
Boot File
|
||||
</span>
|
||||
<span className="text-sm font-medium">
|
||||
{networkState?.dhcp_lease?.bootp_file}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Button
|
||||
size="SM"
|
||||
theme="light"
|
||||
className="text-red-500"
|
||||
text="Renew DHCP Lease"
|
||||
LeadingIcon={ArrowPathIcon}
|
||||
onClick={() => setShowRenewLeaseConfirm(true)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</GridCard>
|
||||
)}
|
||||
</GridCard>
|
||||
) : networkState?.dhcp_lease && networkState.dhcp_lease.ip ? (
|
||||
<DhcpLeaseCard
|
||||
networkState={networkState}
|
||||
setShowRenewLeaseConfirm={setShowRenewLeaseConfirm}
|
||||
/>
|
||||
) : (
|
||||
<EmptyCard
|
||||
IconElm={LuEthernetPort}
|
||||
headline="DHCP Information"
|
||||
description="No DHCP lease information available"
|
||||
/>
|
||||
)}
|
||||
</AutoHeight>
|
||||
</div>
|
||||
<div className="space-y-4">
|
||||
<SettingsItem title="IPv6 Mode" description="Configure the IPv6 mode">
|
||||
|
@ -579,93 +401,33 @@ export default function SettingsNetworkRoute() {
|
|||
])}
|
||||
/>
|
||||
</SettingsItem>
|
||||
{networkState?.ipv6_addresses && (
|
||||
<GridCard>
|
||||
<div className="p-4">
|
||||
<div className="space-y-4">
|
||||
<h3 className="text-base font-bold text-slate-900 dark:text-white">
|
||||
IPv6 Information
|
||||
</h3>
|
||||
|
||||
<div className="grid grid-cols-2 gap-x-6 gap-y-2">
|
||||
{networkState?.dhcp_lease?.ip && (
|
||||
<div className="flex flex-col justify-between">
|
||||
<span className="text-sm text-slate-600 dark:text-slate-400">
|
||||
Link-local
|
||||
</span>
|
||||
<span className="text-sm font-medium">
|
||||
{networkState?.ipv6_link_local}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="space-y-3 pt-2">
|
||||
{networkState?.ipv6_addresses &&
|
||||
networkState?.ipv6_addresses.length > 0 && (
|
||||
<div className="space-y-3">
|
||||
<h4 className="text-sm font-semibold">IPv6 Addresses</h4>
|
||||
{networkState.ipv6_addresses.map(addr => (
|
||||
<div
|
||||
key={addr.address}
|
||||
className="rounded-md rounded-l-none border border-slate-500/10 border-l-blue-700/50 bg-slate-100/40 p-4 pl-4 dark:border-blue-500 dark:bg-slate-900"
|
||||
>
|
||||
<div className="grid grid-cols-2 gap-x-8 gap-y-4">
|
||||
<div className="col-span-2 flex flex-col justify-between">
|
||||
<span className="text-sm text-slate-600 dark:text-slate-400">
|
||||
Address
|
||||
</span>
|
||||
<span className="text-sm font-medium">
|
||||
{addr.address}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{addr.valid_lifetime && (
|
||||
<div className="flex flex-col justify-between">
|
||||
<span className="text-sm text-slate-600 dark:text-slate-400">
|
||||
Valid Lifetime
|
||||
</span>
|
||||
<span className="text-sm font-medium">
|
||||
{addr.valid_lifetime === "" ? (
|
||||
<span className="text-slate-400 dark:text-slate-600">
|
||||
N/A
|
||||
</span>
|
||||
) : (
|
||||
<LifeTimeLabel
|
||||
lifetime={`${addr.valid_lifetime}`}
|
||||
/>
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
{addr.preferred_lifetime && (
|
||||
<div className="flex flex-col justify-between">
|
||||
<span className="text-sm text-slate-600 dark:text-slate-400">
|
||||
Preferred Lifetime
|
||||
</span>
|
||||
<span className="text-sm font-medium">
|
||||
{addr.preferred_lifetime === "" ? (
|
||||
<span className="text-slate-400 dark:text-slate-600">
|
||||
N/A
|
||||
</span>
|
||||
) : (
|
||||
<LifeTimeLabel
|
||||
lifetime={`${addr.preferred_lifetime}`}
|
||||
/>
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
<AutoHeight>
|
||||
{!networkSettingsLoaded &&
|
||||
!(networkState?.ipv6_addresses && networkState.ipv6_addresses.length > 0) ? (
|
||||
<GridCard>
|
||||
<div className="p-4">
|
||||
<div className="space-y-4">
|
||||
<h3 className="text-base font-bold text-slate-900 dark:text-white">
|
||||
IPv6 Information
|
||||
</h3>
|
||||
<div className="animate-pulse space-y-3">
|
||||
<div className="h-4 w-1/3 rounded bg-slate-200 dark:bg-slate-700" />
|
||||
<div className="h-4 w-1/2 rounded bg-slate-200 dark:bg-slate-700" />
|
||||
<div className="h-4 w-1/3 rounded bg-slate-200 dark:bg-slate-700" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</GridCard>
|
||||
)}
|
||||
</GridCard>
|
||||
) : networkState?.ipv6_addresses && networkState.ipv6_addresses.length > 0 ? (
|
||||
<Ipv6NetworkCard networkState={networkState} />
|
||||
) : (
|
||||
<EmptyCard
|
||||
IconElm={LuEthernetPort}
|
||||
headline="IPv6 Information"
|
||||
description="No IPv6 addresses configured"
|
||||
/>
|
||||
)}
|
||||
</AutoHeight>
|
||||
</div>
|
||||
<div className="hidden space-y-4">
|
||||
<SettingsItem
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { NavLink, Outlet, useLocation } from "react-router-dom";
|
||||
import {
|
||||
LuSettings,
|
||||
LuMouse,
|
||||
LuKeyboard,
|
||||
LuVideo,
|
||||
LuCpu,
|
||||
|
@ -149,11 +150,23 @@ export default function SettingsRoute() {
|
|||
className={({ isActive }) => (isActive ? "active" : "")}
|
||||
>
|
||||
<div className="flex items-center gap-x-2 rounded-md px-2.5 py-2.5 text-sm transition-colors hover:bg-slate-100 dark:hover:bg-slate-700 in-[.active]:bg-blue-50 in-[.active]:text-blue-700! md:in-[.active]:bg-transparent dark:in-[.active]:bg-blue-900 dark:in-[.active]:text-blue-200! dark:md:in-[.active]:bg-transparent">
|
||||
<LuKeyboard className="h-4 w-4 shrink-0" />
|
||||
|
||||
<LuMouse className="h-4 w-4 shrink-0" />
|
||||
<h1>Mouse</h1>
|
||||
</div>
|
||||
</NavLink>
|
||||
</div>
|
||||
<div className="shrink-0">
|
||||
<NavLink
|
||||
to="keyboard"
|
||||
className={({ isActive }) => (isActive ? "active" : "")}
|
||||
>
|
||||
<div className="flex items-center gap-x-2 rounded-md px-2.5 py-2.5 text-sm transition-colors hover:bg-slate-100 dark:hover:bg-slate-700 [.active_&]:bg-blue-50 [.active_&]:!text-blue-700 md:[.active_&]:bg-transparent dark:[.active_&]:bg-blue-900 dark:[.active_&]:!text-blue-200 dark:md:[.active_&]:bg-transparent">
|
||||
<LuKeyboard className="h-4 w-4 shrink-0" />
|
||||
<h1>Keyboard</h1>
|
||||
</div>
|
||||
</NavLink>
|
||||
</div>
|
||||
<div className="shrink-0">
|
||||
<NavLink
|
||||
to="video"
|
||||
|
|
|
@ -703,12 +703,17 @@ export default function KvmIdRoute() {
|
|||
|
||||
send("getUpdateStatus", {}, async resp => {
|
||||
if ("error" in resp) {
|
||||
notifications.error("Failed to get device version");
|
||||
} else {
|
||||
const result = resp.result as SystemVersionInfo;
|
||||
setAppVersion(result.local.appVersion);
|
||||
setSystemVersion(result.local.systemVersion);
|
||||
notifications.error(`Failed to get device version: ${resp.error}`);
|
||||
return
|
||||
}
|
||||
|
||||
const result = resp.result as SystemVersionInfo;
|
||||
if (result.error) {
|
||||
notifications.error(`Failed to get device version: ${result.error}`);
|
||||
}
|
||||
|
||||
setAppVersion(result.local.appVersion);
|
||||
setSystemVersion(result.local.systemVersion);
|
||||
});
|
||||
}, [appVersion, send, setAppVersion, setSystemVersion]);
|
||||
|
||||
|
|
|
@ -116,7 +116,7 @@ export default function WelcomeLocalModeRoute() {
|
|||
onChange={() => {
|
||||
setSelectedMode(mode as "password" | "noPassword");
|
||||
}}
|
||||
className="absolute top-2 right-2 h-4 w-4 text-blue-600"
|
||||
className="form-radio absolute top-2 right-2 h-4 w-4 text-blue-600"
|
||||
/>
|
||||
</div>
|
||||
</GridCard>
|
||||
|
|
Loading…
Reference in New Issue