mirror of https://github.com/jetkvm/kvm.git
Lint fix pass with all rules enforced.
Bumped devcontainer Bump more packages
This commit is contained in:
parent
e7afa61cc7
commit
c56eb84325
|
|
@ -4,7 +4,7 @@
|
||||||
"features": {
|
"features": {
|
||||||
"ghcr.io/devcontainers/features/node:1": {
|
"ghcr.io/devcontainers/features/node:1": {
|
||||||
// Should match what is defined in ui/package.json
|
// Should match what is defined in ui/package.json
|
||||||
"version": "22.20.0"
|
"version": "22.21.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"mounts": [
|
"mounts": [
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
"features": {
|
"features": {
|
||||||
"ghcr.io/devcontainers/features/node:1": {
|
"ghcr.io/devcontainers/features/node:1": {
|
||||||
// Should match what is defined in ui/package.json
|
// Should match what is defined in ui/package.json
|
||||||
"version": "22.20.0"
|
"version": "22.21.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"runArgs": [
|
"runArgs": [
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "kvm-ui",
|
"name": "kvm-ui",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "2025.11.11.1900",
|
"version": "2025.11.20.0300",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^22.21.1"
|
"node": "^22.21.1"
|
||||||
|
|
@ -47,11 +47,11 @@
|
||||||
"react": "^19.2.0",
|
"react": "^19.2.0",
|
||||||
"react-animate-height": "^3.2.3",
|
"react-animate-height": "^3.2.3",
|
||||||
"react-dom": "^19.2.0",
|
"react-dom": "^19.2.0",
|
||||||
"react-hook-form": "^7.66.0",
|
"react-hook-form": "^7.66.1",
|
||||||
"react-hot-toast": "^2.6.0",
|
"react-hot-toast": "^2.6.0",
|
||||||
"react-icons": "^5.5.0",
|
"react-icons": "^5.5.0",
|
||||||
"react-router": "^7.9.5",
|
"react-router": "^7.9.6",
|
||||||
"react-simple-keyboard": "^3.8.133",
|
"react-simple-keyboard": "^3.8.136",
|
||||||
"react-use-websocket": "^4.13.0",
|
"react-use-websocket": "^4.13.0",
|
||||||
"react-xtermjs": "^1.0.10",
|
"react-xtermjs": "^1.0.10",
|
||||||
"recharts": "^3.4.1",
|
"recharts": "^3.4.1",
|
||||||
|
|
@ -61,11 +61,11 @@
|
||||||
"zustand": "^4.5.2"
|
"zustand": "^4.5.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/compat": "^1.4.1",
|
"@eslint/compat": "^2.0.0",
|
||||||
"@eslint/eslintrc": "^3.3.1",
|
"@eslint/eslintrc": "^3.3.1",
|
||||||
"@eslint/js": "^9.39.1",
|
"@eslint/js": "^9.39.1",
|
||||||
"@inlang/cli": "^3.0.12",
|
"@inlang/cli": "^3.0.12",
|
||||||
"@inlang/paraglide-js": "^2.4.0",
|
"@inlang/paraglide-js": "^2.5.0",
|
||||||
"@inlang/plugin-m-function-matcher": "^2.1.0",
|
"@inlang/plugin-m-function-matcher": "^2.1.0",
|
||||||
"@inlang/plugin-message-format": "^4.0.0",
|
"@inlang/plugin-message-format": "^4.0.0",
|
||||||
"@inlang/sdk": "^2.4.9",
|
"@inlang/sdk": "^2.4.9",
|
||||||
|
|
@ -73,13 +73,13 @@
|
||||||
"@tailwindcss/postcss": "^4.1.17",
|
"@tailwindcss/postcss": "^4.1.17",
|
||||||
"@tailwindcss/typography": "^0.5.19",
|
"@tailwindcss/typography": "^0.5.19",
|
||||||
"@tailwindcss/vite": "^4.1.17",
|
"@tailwindcss/vite": "^4.1.17",
|
||||||
"@types/react": "^19.2.3",
|
"@types/react": "^19.2.6",
|
||||||
"@types/react-dom": "^19.2.2",
|
"@types/react-dom": "^19.2.3",
|
||||||
"@types/semver": "^7.7.1",
|
"@types/semver": "^7.7.1",
|
||||||
"@types/validator": "^13.15.5",
|
"@types/validator": "^13.15.10",
|
||||||
"@typescript-eslint/eslint-plugin": "^8.46.4",
|
"@typescript-eslint/eslint-plugin": "^8.47.0",
|
||||||
"@typescript-eslint/parser": "^8.46.4",
|
"@typescript-eslint/parser": "^8.47.0",
|
||||||
"@vitejs/plugin-react-swc": "^4.2.1",
|
"@vitejs/plugin-react-swc": "^4.2.2",
|
||||||
"autoprefixer": "^10.4.22",
|
"autoprefixer": "^10.4.22",
|
||||||
"eslint": "^9.39.1",
|
"eslint": "^9.39.1",
|
||||||
"eslint-config-prettier": "^10.1.8",
|
"eslint-config-prettier": "^10.1.8",
|
||||||
|
|
@ -95,7 +95,7 @@
|
||||||
"prettier-plugin-tailwindcss": "^0.7.1",
|
"prettier-plugin-tailwindcss": "^0.7.1",
|
||||||
"tailwindcss": "^4.1.17",
|
"tailwindcss": "^4.1.17",
|
||||||
"typescript": "^5.9.3",
|
"typescript": "^5.9.3",
|
||||||
"typescript-eslint": "^8.46.4",
|
"typescript-eslint": "^8.47.0",
|
||||||
"vite": "^7.2.2",
|
"vite": "^7.2.2",
|
||||||
"vite-tsconfig-paths": "^5.1.4"
|
"vite-tsconfig-paths": "^5.1.4"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,12 +6,7 @@ import { Popover, PopoverButton, PopoverPanel } from "@headlessui/react";
|
||||||
import { CommandLineIcon } from "@heroicons/react/20/solid";
|
import { CommandLineIcon } from "@heroicons/react/20/solid";
|
||||||
|
|
||||||
import { cx } from "@/cva.config";
|
import { cx } from "@/cva.config";
|
||||||
import {
|
import { useHidStore, useMountMediaStore, useSettingsStore, useUiStore } from "@hooks/stores";
|
||||||
useHidStore,
|
|
||||||
useMountMediaStore,
|
|
||||||
useSettingsStore,
|
|
||||||
useUiStore,
|
|
||||||
} from "@hooks/stores";
|
|
||||||
import { useDeviceUiNavigation } from "@hooks/useAppNavigation";
|
import { useDeviceUiNavigation } from "@hooks/useAppNavigation";
|
||||||
import { Button } from "@components/Button";
|
import { Button } from "@components/Button";
|
||||||
import Container from "@components/Container";
|
import Container from "@components/Container";
|
||||||
|
|
@ -28,7 +23,8 @@ export default function Actionbar({
|
||||||
}) {
|
}) {
|
||||||
const { navigateTo } = useDeviceUiNavigation();
|
const { navigateTo } = useDeviceUiNavigation();
|
||||||
const { isVirtualKeyboardEnabled, setVirtualKeyboardEnabled } = useHidStore();
|
const { isVirtualKeyboardEnabled, setVirtualKeyboardEnabled } = useHidStore();
|
||||||
const { setDisableVideoFocusTrap, terminalType, setTerminalType, toggleSidebarView } = useUiStore();
|
const { setDisableVideoFocusTrap, terminalType, setTerminalType, toggleSidebarView } =
|
||||||
|
useUiStore();
|
||||||
const { remoteVirtualMediaState } = useMountMediaStore();
|
const { remoteVirtualMediaState } = useMountMediaStore();
|
||||||
const { developerMode } = useSettingsStore();
|
const { developerMode } = useSettingsStore();
|
||||||
|
|
||||||
|
|
@ -246,10 +242,7 @@ export default function Actionbar({
|
||||||
theme="light"
|
theme="light"
|
||||||
text={m.action_bar_connection_stats()}
|
text={m.action_bar_connection_stats()}
|
||||||
LeadingIcon={({ className }) => (
|
LeadingIcon={({ className }) => (
|
||||||
<LuSignal
|
<LuSignal className={cx(className, "mb-0.5 text-green-500")} strokeWidth={4} />
|
||||||
className={cx(className, "mb-0.5 text-green-500")}
|
|
||||||
strokeWidth={4}
|
|
||||||
/>
|
|
||||||
)}
|
)}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
toggleSidebarView("connection-stats");
|
toggleSidebarView("connection-stats");
|
||||||
|
|
@ -264,7 +257,7 @@ export default function Actionbar({
|
||||||
LeadingIcon={LuSettings}
|
LeadingIcon={LuSettings}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setDisableVideoFocusTrap(true);
|
setDisableVideoFocusTrap(true);
|
||||||
navigateTo("/settings")
|
navigateTo("/settings");
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -55,9 +55,7 @@ export default function AuthLayout({
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
<div className="space-y-2 text-center">
|
<div className="space-y-2 text-center">
|
||||||
<h1 className="text-4xl font-semibold text-black dark:text-white">
|
<h1 className="text-4xl font-semibold text-black dark:text-white">{title}</h1>
|
||||||
{title}
|
|
||||||
</h1>
|
|
||||||
<p className="text-slate-600 dark:text-slate-400">{description}</p>
|
<p className="text-slate-600 dark:text-slate-400">{description}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -65,12 +63,8 @@ export default function AuthLayout({
|
||||||
<div className="mx-auto max-w-sm space-y-4">
|
<div className="mx-auto max-w-sm space-y-4">
|
||||||
<form action={`${CLOUD_API}/oidc/google`} method="POST">
|
<form action={`${CLOUD_API}/oidc/google`} method="POST">
|
||||||
{/*This could be the KVM ID*/}
|
{/*This could be the KVM ID*/}
|
||||||
{deviceId ? (
|
{deviceId ? <input type="hidden" name="deviceId" value={deviceId} /> : null}
|
||||||
<input type="hidden" name="deviceId" value={deviceId} />
|
{returnTo ? <input type="hidden" name="returnTo" value={returnTo} /> : null}
|
||||||
) : null}
|
|
||||||
{returnTo ? (
|
|
||||||
<input type="hidden" name="returnTo" value={returnTo} />
|
|
||||||
) : null}
|
|
||||||
<Button
|
<Button
|
||||||
size="LG"
|
size="LG"
|
||||||
theme="light"
|
theme="light"
|
||||||
|
|
@ -80,8 +74,7 @@ export default function AuthLayout({
|
||||||
textAlign="center"
|
textAlign="center"
|
||||||
type="submit"
|
type="submit"
|
||||||
loading={
|
loading={
|
||||||
(navigation.state === "submitting" ||
|
(navigation.state === "submitting" || navigation.state === "loading") &&
|
||||||
navigation.state === "loading") &&
|
|
||||||
navigation.formMethod?.toLowerCase() === "post" &&
|
navigation.formMethod?.toLowerCase() === "post" &&
|
||||||
navigation.formAction?.includes("auth/google")
|
navigation.formAction?.includes("auth/google")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ const sizes = {
|
||||||
const themes = {
|
const themes = {
|
||||||
primary: cx(
|
primary: cx(
|
||||||
// Base styles
|
// Base styles
|
||||||
"bg-blue-700 dark:border-blue-600 border border-blue-900/60 text-white shadow-sm",
|
"border border-blue-900/60 bg-blue-700 text-white shadow-sm dark:border-blue-600",
|
||||||
// Hover states
|
// Hover states
|
||||||
"group-hover:bg-blue-800",
|
"group-hover:bg-blue-800",
|
||||||
// Active states
|
// Active states
|
||||||
|
|
@ -24,9 +24,9 @@ const themes = {
|
||||||
),
|
),
|
||||||
danger: cx(
|
danger: cx(
|
||||||
// Base styles
|
// Base styles
|
||||||
"bg-red-600 text-white border-red-700 shadow-xs shadow-red-200/80 dark:border-red-600 dark:shadow-red-900/20",
|
"border-red-700 bg-red-600 text-white shadow-xs shadow-red-200/80 dark:border-red-600 dark:shadow-red-900/20",
|
||||||
// Hover states
|
// Hover states
|
||||||
"group-hover:bg-red-700 group-hover:border-red-800 dark:group-hover:bg-red-700 dark:group-hover:border-red-600",
|
"group-hover:border-red-800 group-hover:bg-red-700 dark:group-hover:border-red-600 dark:group-hover:bg-red-700",
|
||||||
// Active states
|
// Active states
|
||||||
"group-active:bg-red-800 dark:group-active:bg-red-800",
|
"group-active:bg-red-800 dark:group-active:bg-red-800",
|
||||||
// Focus states
|
// Focus states
|
||||||
|
|
@ -34,7 +34,7 @@ const themes = {
|
||||||
),
|
),
|
||||||
light: cx(
|
light: cx(
|
||||||
// Base styles
|
// Base styles
|
||||||
"bg-white text-black border-slate-800/30 shadow-xs dark:bg-slate-800 dark:border-slate-300/20 dark:text-white",
|
"border-slate-800/30 bg-white text-black shadow-xs dark:border-slate-300/20 dark:bg-slate-800 dark:text-white",
|
||||||
// Hover states
|
// Hover states
|
||||||
"group-hover:bg-blue-50/80 dark:group-hover:bg-slate-700",
|
"group-hover:bg-blue-50/80 dark:group-hover:bg-slate-700",
|
||||||
// Active states
|
// Active states
|
||||||
|
|
@ -44,7 +44,7 @@ const themes = {
|
||||||
),
|
),
|
||||||
lightDanger: cx(
|
lightDanger: cx(
|
||||||
// Base styles
|
// Base styles
|
||||||
"bg-white text-black border-red-400/60 shadow-xs",
|
"border-red-400/60 bg-white text-black shadow-xs",
|
||||||
// Hover states
|
// Hover states
|
||||||
"group-hover:bg-red-50/80",
|
"group-hover:bg-red-50/80",
|
||||||
// Active states
|
// Active states
|
||||||
|
|
@ -54,9 +54,9 @@ const themes = {
|
||||||
),
|
),
|
||||||
blank: cx(
|
blank: cx(
|
||||||
// Base styles
|
// Base styles
|
||||||
"bg-white/0 text-black border-transparent dark:text-white",
|
"border-transparent bg-white/0 text-black dark:text-white",
|
||||||
// Hover states
|
// Hover states
|
||||||
"group-hover:bg-white group-hover:border-slate-800/30 group-hover:shadow-sm dark:group-hover:bg-slate-700 dark:group-hover:border-slate-600",
|
"group-hover:border-slate-800/30 group-hover:bg-white group-hover:shadow-sm dark:group-hover:border-slate-600 dark:group-hover:bg-slate-700",
|
||||||
// Active states
|
// Active states
|
||||||
"group-active:bg-slate-100/80",
|
"group-active:bg-slate-100/80",
|
||||||
),
|
),
|
||||||
|
|
@ -65,16 +65,16 @@ const themes = {
|
||||||
const btnVariants = cva({
|
const btnVariants = cva({
|
||||||
base: cx(
|
base: cx(
|
||||||
// Base styles
|
// Base styles
|
||||||
"border rounded-sm select-none",
|
"rounded-sm border select-none",
|
||||||
// Size classes
|
// Size classes
|
||||||
"justify-center items-center shrink-0",
|
"shrink-0 items-center justify-center",
|
||||||
// Transition classes
|
// Transition classes
|
||||||
"outline-hidden transition-all duration-200",
|
"outline-hidden transition-all duration-200",
|
||||||
// Text classes
|
// Text classes
|
||||||
"font-display text-center font-medium leading-tight",
|
"text-center font-display leading-tight font-medium",
|
||||||
// States
|
// States
|
||||||
"group-focus:outline-hidden group-focus:ring-2 group-focus:ring-offset-2 group-focus:ring-blue-700",
|
"group-focus:ring-2 group-focus:ring-blue-700 group-focus:ring-offset-2 group-focus:outline-hidden",
|
||||||
"group-disabled:opacity-50 group-disabled:pointer-events-none",
|
"group-disabled:pointer-events-none group-disabled:opacity-50",
|
||||||
),
|
),
|
||||||
|
|
||||||
variants: {
|
variants: {
|
||||||
|
|
@ -115,8 +115,7 @@ interface ButtonContentPropsType {
|
||||||
}
|
}
|
||||||
|
|
||||||
function ButtonContent(props: ButtonContentPropsType) {
|
function ButtonContent(props: ButtonContentPropsType) {
|
||||||
const { text, LeadingIcon, TrailingIcon, fullWidth, className, textAlign, loading } =
|
const { text, LeadingIcon, TrailingIcon, fullWidth, className, textAlign, loading } = props;
|
||||||
props;
|
|
||||||
|
|
||||||
// Based on the size prop, we'll use the corresponding variant classnames
|
// Based on the size prop, we'll use the corresponding variant classnames
|
||||||
const iconClassName = iconVariants(props);
|
const iconClassName = iconVariants(props);
|
||||||
|
|
@ -136,9 +135,7 @@ function ButtonContent(props: ButtonContentPropsType) {
|
||||||
<LoadingSpinner className={cx(iconClassName, "animate-spin")} />
|
<LoadingSpinner className={cx(iconClassName, "animate-spin")} />
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
LeadingIcon && (
|
LeadingIcon && <LeadingIcon className={cx(iconClassName, "shrink-0 justify-start")} />
|
||||||
<LeadingIcon className={cx(iconClassName, "shrink-0 justify-start")} />
|
|
||||||
)
|
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{text && typeof text === "string" ? (
|
{text && typeof text === "string" ? (
|
||||||
|
|
@ -147,9 +144,7 @@ function ButtonContent(props: ButtonContentPropsType) {
|
||||||
text
|
text
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{TrailingIcon && (
|
{TrailingIcon && <TrailingIcon className={cx(iconClassName, "shrink-0 justify-end")} />}
|
||||||
<TrailingIcon className={cx(iconClassName, "shrink-0 justify-end")} />
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
@ -175,7 +170,7 @@ type ButtonPropsType = Pick<
|
||||||
export const Button = React.forwardRef<HTMLButtonElement, ButtonPropsType>(
|
export const Button = React.forwardRef<HTMLButtonElement, ButtonPropsType>(
|
||||||
({ type, disabled, onClick, formNoValidate, loading, fetcher, ...props }, ref) => {
|
({ type, disabled, onClick, formNoValidate, loading, fetcher, ...props }, ref) => {
|
||||||
const classes = cx(
|
const classes = cx(
|
||||||
"group outline-hidden cursor-pointer",
|
"group cursor-pointer outline-hidden",
|
||||||
props.fullWidth ? "w-full" : "",
|
props.fullWidth ? "w-full" : "",
|
||||||
loading ? "pointer-events-none" : "",
|
loading ? "pointer-events-none" : "",
|
||||||
);
|
);
|
||||||
|
|
@ -212,7 +207,7 @@ export const Button = React.forwardRef<HTMLButtonElement, ButtonPropsType>(
|
||||||
Button.displayName = "Button";
|
Button.displayName = "Button";
|
||||||
|
|
||||||
type LinkPropsType = Pick<LinkProps, "to"> &
|
type LinkPropsType = Pick<LinkProps, "to"> &
|
||||||
React.ComponentProps<typeof ButtonContent> & { disabled?: boolean, reloadDocument?: boolean };
|
React.ComponentProps<typeof ButtonContent> & { disabled?: boolean; reloadDocument?: boolean };
|
||||||
export const LinkButton = ({ to, ...props }: LinkPropsType) => {
|
export const LinkButton = ({ to, ...props }: LinkPropsType) => {
|
||||||
const classes = cx(
|
const classes = cx(
|
||||||
"group outline-hidden",
|
"group outline-hidden",
|
||||||
|
|
@ -241,7 +236,7 @@ type LabelPropsType = Pick<HTMLLabelElement, "htmlFor"> &
|
||||||
React.ComponentProps<typeof ButtonContent> & { disabled?: boolean };
|
React.ComponentProps<typeof ButtonContent> & { disabled?: boolean };
|
||||||
export const LabelButton = ({ htmlFor, ...props }: LabelPropsType) => {
|
export const LabelButton = ({ htmlFor, ...props }: LabelPropsType) => {
|
||||||
const classes = cx(
|
const classes = cx(
|
||||||
"group outline-hidden block cursor-pointer",
|
"group block cursor-pointer outline-hidden",
|
||||||
props.disabled ? "pointer-events-none opacity-70!" : "",
|
props.disabled ? "pointer-events-none opacity-70!" : "",
|
||||||
props.fullWidth ? "w-full" : "",
|
props.fullWidth ? "w-full" : "",
|
||||||
props.loading ? "pointer-events-none" : "",
|
props.loading ? "pointer-events-none" : "",
|
||||||
|
|
|
||||||
|
|
@ -8,10 +8,12 @@ interface Props {
|
||||||
|
|
||||||
export const CardHeader = ({ headline, description, Button }: Props) => {
|
export const CardHeader = ({ headline, description, Button }: Props) => {
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center justify-between pb-0 gap-x-4">
|
<div className="flex items-center justify-between gap-x-4 pb-0">
|
||||||
<div className="space-y-1 grow">
|
<div className="grow space-y-1">
|
||||||
<h3 className="text-lg font-bold leading-none text-black dark:text-white">{headline}</h3>
|
<h3 className="text-lg leading-none font-bold text-black dark:text-white">{headline}</h3>
|
||||||
{description && <div className="text-sm text-slate-700 dark:text-slate-300">{description}</div>}
|
{description && (
|
||||||
|
<div className="text-sm text-slate-700 dark:text-slate-300">{description}</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
{Button && <div>{Button}</div>}
|
{Button && <div>{Button}</div>}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ const checkboxVariants = cva({
|
||||||
"form-checkbox block rounded",
|
"form-checkbox block rounded",
|
||||||
|
|
||||||
// Colors
|
// 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",
|
"border-slate-300 bg-slate-50 transition-colors checked:accent-blue-700 dark:border-slate-600 dark:bg-slate-800 checked:dark:accent-blue-500",
|
||||||
|
|
||||||
// Hover
|
// Hover
|
||||||
"hover:bg-slate-200/50 dark:hover:bg-slate-700/50",
|
"hover:bg-slate-200/50 dark:hover:bg-slate-700/50",
|
||||||
|
|
@ -24,7 +24,7 @@ const checkboxVariants = cva({
|
||||||
"active:bg-slate-200 dark:active:bg-slate-700",
|
"active:bg-slate-200 dark:active:bg-slate-700",
|
||||||
|
|
||||||
// Focus
|
// Focus
|
||||||
"focus:border-slate-300 dark:focus:border-slate-600 focus:outline-hidden focus:ring-2 focus:ring-blue-700 dark:focus:ring-blue-500 focus:ring-offset-2 dark:focus:ring-offset-slate-900",
|
"focus:border-slate-300 focus:ring-2 focus:ring-blue-700 focus:ring-offset-2 focus:outline-hidden dark:focus:border-slate-600 dark:focus:ring-blue-500 dark:focus:ring-offset-slate-900",
|
||||||
|
|
||||||
// Disabled
|
// Disabled
|
||||||
"disabled:pointer-events-none disabled:opacity-30",
|
"disabled:pointer-events-none disabled:opacity-30",
|
||||||
|
|
@ -41,9 +41,7 @@ const Checkbox = forwardRef<HTMLInputElement, CheckBoxProps>(function Checkbox(
|
||||||
ref,
|
ref,
|
||||||
) {
|
) {
|
||||||
const classes = checkboxVariants({ size });
|
const classes = checkboxVariants({ size });
|
||||||
return (
|
return <input ref={ref} {...props} type="checkbox" className={clsx(classes, className)} />;
|
||||||
<input ref={ref} {...props} type="checkbox" className={clsx(classes, className)} />
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
Checkbox.displayName = "Checkbox";
|
Checkbox.displayName = "Checkbox";
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -74,11 +74,11 @@ export function Combobox({
|
||||||
"dark:bg-slate-800 dark:text-white dark:hover:bg-slate-700 dark:active:bg-slate-800/60",
|
"dark:bg-slate-800 dark:text-white dark:hover:bg-slate-700 dark:active:bg-slate-800/60",
|
||||||
|
|
||||||
// Focus
|
// Focus
|
||||||
"focus:outline-blue-600 focus:ring-2 focus:ring-blue-700 focus:ring-offset-2 dark:focus:outline-blue-500 dark:focus:ring-blue-500",
|
"focus:ring-2 focus:ring-blue-700 focus:ring-offset-2 focus:outline-blue-600 dark:focus:ring-blue-500 dark:focus:outline-blue-500",
|
||||||
|
|
||||||
// Disabled
|
// Disabled
|
||||||
disabled &&
|
disabled &&
|
||||||
"pointer-events-none select-none bg-slate-50 text-slate-500/80 disabled:hover:bg-white dark:bg-slate-800 dark:text-slate-400/80 dark:disabled:hover:bg-slate-800",
|
"pointer-events-none bg-slate-50 text-slate-500/80 select-none disabled:hover:bg-white dark:bg-slate-800 dark:text-slate-400/80 dark:disabled:hover:bg-slate-800",
|
||||||
)}
|
)}
|
||||||
placeholder={disabled ? disabledMessage : placeholder}
|
placeholder={disabled ? disabledMessage : placeholder}
|
||||||
displayValue={displayValue}
|
displayValue={displayValue}
|
||||||
|
|
@ -95,7 +95,7 @@ export function Combobox({
|
||||||
value={option}
|
value={option}
|
||||||
className={clsx(
|
className={clsx(
|
||||||
// General styling
|
// General styling
|
||||||
"cursor-default select-none px-4 py-2",
|
"cursor-default px-4 py-2 select-none",
|
||||||
|
|
||||||
// Hover and active states
|
// Hover and active states
|
||||||
"hover:bg-blue-50/80 ui-active:bg-blue-50/80 ui-active:text-blue-900",
|
"hover:bg-blue-50/80 ui-active:bg-blue-50/80 ui-active:text-blue-900",
|
||||||
|
|
|
||||||
|
|
@ -77,14 +77,13 @@ export function ConfirmDialog({
|
||||||
<div className="pointer-events-auto relative w-full overflow-hidden rounded-lg border border-slate-200 bg-white shadow-sm transition-all dark:border-slate-800 dark:bg-slate-900">
|
<div className="pointer-events-auto relative w-full overflow-hidden rounded-lg border border-slate-200 bg-white shadow-sm transition-all dark:border-slate-800 dark:bg-slate-900">
|
||||||
<div className="p-6">
|
<div className="p-6">
|
||||||
<div className="flex items-start gap-3.5">
|
<div className="flex items-start gap-3.5">
|
||||||
<Icon aria-hidden="true" className={cx("size-[18px] shrink-0 mt-[2px]", iconClass)} />
|
<Icon
|
||||||
<div className="flex-1 min-w-0 space-y-2">
|
aria-hidden="true"
|
||||||
<h2 className="font-semibold text-slate-950 dark:text-white">
|
className={cx("mt-[2px] size-[18px] shrink-0", iconClass)}
|
||||||
{title}
|
/>
|
||||||
</h2>
|
<div className="min-w-0 flex-1 space-y-2">
|
||||||
<div className="text-sm text-slate-700 dark:text-slate-300">
|
<h2 className="font-semibold text-slate-950 dark:text-white">{title}</h2>
|
||||||
{description}
|
<div className="text-sm text-slate-700 dark:text-slate-300">{description}</div>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ import React, { ReactNode } from "react";
|
||||||
import { cx } from "@/cva.config";
|
import { cx } from "@/cva.config";
|
||||||
|
|
||||||
function Container({ children, className }: { children: ReactNode; className?: string }) {
|
function Container({ children, className }: { children: ReactNode; className?: string }) {
|
||||||
return <div className={cx("mx-auto h-full w-full px-8 ", className)}>{children}</div>;
|
return <div className={cx("mx-auto h-full w-full px-8", className)}>{children}</div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
function Article({ children }: { children: React.ReactNode }) {
|
function Article({ children }: { children: React.ReactNode }) {
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,6 @@ import { LifeTimeLabel } from "@routes/devices.$id.settings.network";
|
||||||
import { NetworkState } from "@hooks/stores";
|
import { NetworkState } from "@hooks/stores";
|
||||||
import { m } from "@localizations/messages.js";
|
import { m } from "@localizations/messages.js";
|
||||||
|
|
||||||
|
|
||||||
export default function DhcpLeaseCard({
|
export default function DhcpLeaseCard({
|
||||||
networkState,
|
networkState,
|
||||||
setShowRenewLeaseConfirm,
|
setShowRenewLeaseConfirm,
|
||||||
|
|
@ -54,10 +53,9 @@ export default function DhcpLeaseCard({
|
||||||
<div className="flex justify-between border-slate-800/10 pt-2 dark:border-slate-300/20">
|
<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">
|
<span className="text-sm text-slate-600 dark:text-slate-400">
|
||||||
{m.ip_address()}
|
{m.ip_address()}
|
||||||
</span>
|
|
||||||
<span className="text-sm font-medium">
|
|
||||||
{networkState?.dhcp_lease?.ip}
|
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
|
<span className="text-sm font-medium">{networkState?.dhcp_lease?.ip}</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|
@ -65,10 +63,9 @@ export default function DhcpLeaseCard({
|
||||||
<div className="flex justify-between border-t border-slate-800/10 pt-2 dark:border-slate-300/20">
|
<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">
|
<span className="text-sm text-slate-600 dark:text-slate-400">
|
||||||
{m.subnet_mask()}
|
{m.subnet_mask()}
|
||||||
</span>
|
|
||||||
<span className="text-sm font-medium">
|
|
||||||
{networkState?.dhcp_lease?.netmask}
|
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
|
<span className="text-sm font-medium">{networkState?.dhcp_lease?.netmask}</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|
@ -76,7 +73,8 @@ export default function DhcpLeaseCard({
|
||||||
<div className="flex justify-between border-t border-slate-800/10 pt-2 dark:border-slate-300/20">
|
<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">
|
<span className="text-sm text-slate-600 dark:text-slate-400">
|
||||||
{m.dns_servers()}
|
{m.dns_servers()}
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span className="text-right text-sm font-medium">
|
<span className="text-right text-sm font-medium">
|
||||||
{networkState?.dhcp_lease?.dns_servers.map(dns => (
|
{networkState?.dhcp_lease?.dns_servers.map(dns => (
|
||||||
<div key={dns}>{dns}</div>
|
<div key={dns}>{dns}</div>
|
||||||
|
|
@ -89,10 +87,9 @@ export default function DhcpLeaseCard({
|
||||||
<div className="flex justify-between border-t border-slate-800/10 pt-2 dark:border-slate-300/20">
|
<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">
|
<span className="text-sm text-slate-600 dark:text-slate-400">
|
||||||
{m.dhcp_lease_broadcast()}
|
{m.dhcp_lease_broadcast()}
|
||||||
</span>
|
|
||||||
<span className="text-sm font-medium">
|
|
||||||
{networkState?.dhcp_lease?.broadcast}
|
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
|
<span className="text-sm font-medium">{networkState?.dhcp_lease?.broadcast}</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|
@ -100,10 +97,9 @@ export default function DhcpLeaseCard({
|
||||||
<div className="flex justify-between border-t border-slate-800/10 pt-2 dark:border-slate-300/20">
|
<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">
|
<span className="text-sm text-slate-600 dark:text-slate-400">
|
||||||
{m.dhcp_lease_domain()}
|
{m.dhcp_lease_domain()}
|
||||||
</span>
|
|
||||||
<span className="text-sm font-medium">
|
|
||||||
{networkState?.dhcp_lease?.domain}
|
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
|
<span className="text-sm font-medium">{networkState?.dhcp_lease?.domain}</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|
@ -112,7 +108,8 @@ export default function DhcpLeaseCard({
|
||||||
<div className="flex justify-between gap-x-8 border-t border-slate-800/10 pt-2 dark:border-slate-300/20">
|
<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">
|
<div className="w-full grow text-sm text-slate-600 dark:text-slate-400">
|
||||||
{m.ntp_servers()}
|
{m.ntp_servers()}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="shrink text-right text-sm font-medium">
|
<div className="shrink text-right text-sm font-medium">
|
||||||
{networkState?.dhcp_lease?.ntp_servers.map(server => (
|
{networkState?.dhcp_lease?.ntp_servers.map(server => (
|
||||||
<div key={server}>{server}</div>
|
<div key={server}>{server}</div>
|
||||||
|
|
@ -125,10 +122,9 @@ export default function DhcpLeaseCard({
|
||||||
<div className="flex justify-between border-t border-slate-800/10 pt-2 dark:border-slate-300/20">
|
<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">
|
<span className="text-sm text-slate-600 dark:text-slate-400">
|
||||||
{m.dhcp_lease_hostname()}
|
{m.dhcp_lease_hostname()}
|
||||||
</span>
|
|
||||||
<span className="text-sm font-medium">
|
|
||||||
{networkState?.dhcp_lease?.hostname}
|
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
|
<span className="text-sm font-medium">{networkState?.dhcp_lease?.hostname}</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -139,7 +135,8 @@ export default function DhcpLeaseCard({
|
||||||
<div className="flex justify-between pt-2">
|
<div className="flex justify-between pt-2">
|
||||||
<span className="text-sm text-slate-600 dark:text-slate-400">
|
<span className="text-sm text-slate-600 dark:text-slate-400">
|
||||||
{m.dhcp_lease_gateway()}
|
{m.dhcp_lease_gateway()}
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span className="text-right text-sm font-medium">
|
<span className="text-right text-sm font-medium">
|
||||||
{networkState?.dhcp_lease?.routers.map(router => (
|
{networkState?.dhcp_lease?.routers.map(router => (
|
||||||
<div key={router}>{router}</div>
|
<div key={router}>{router}</div>
|
||||||
|
|
@ -152,10 +149,9 @@ export default function DhcpLeaseCard({
|
||||||
<div className="flex justify-between border-t border-slate-800/10 pt-2 dark:border-slate-300/20">
|
<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">
|
<span className="text-sm text-slate-600 dark:text-slate-400">
|
||||||
{m.dhcp_server()}
|
{m.dhcp_server()}
|
||||||
</span>
|
|
||||||
<span className="text-sm font-medium">
|
|
||||||
{networkState?.dhcp_lease?.server_id}
|
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
|
<span className="text-sm font-medium">{networkState?.dhcp_lease?.server_id}</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|
@ -163,11 +159,10 @@ export default function DhcpLeaseCard({
|
||||||
<div className="flex justify-between border-t border-slate-800/10 pt-2 dark:border-slate-300/20">
|
<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">
|
<span className="text-sm text-slate-600 dark:text-slate-400">
|
||||||
{m.dhcp_lease_lease_expires()}
|
{m.dhcp_lease_lease_expires()}
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span className="text-sm font-medium">
|
<span className="text-sm font-medium">
|
||||||
<LifeTimeLabel
|
<LifeTimeLabel lifetime={`${networkState?.dhcp_lease?.lease_expiry}`} />
|
||||||
lifetime={`${networkState?.dhcp_lease?.lease_expiry}`}
|
|
||||||
/>
|
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
@ -176,10 +171,9 @@ export default function DhcpLeaseCard({
|
||||||
<div className="flex justify-between border-t border-slate-800/10 pt-2 dark:border-slate-300/20">
|
<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">
|
<span className="text-sm text-slate-600 dark:text-slate-400">
|
||||||
{m.dhcp_lease_broadcast()}
|
{m.dhcp_lease_broadcast()}
|
||||||
</span>
|
|
||||||
<span className="text-sm font-medium">
|
|
||||||
{networkState?.dhcp_lease?.broadcast}
|
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
|
<span className="text-sm font-medium">{networkState?.dhcp_lease?.broadcast}</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|
@ -187,10 +181,9 @@ export default function DhcpLeaseCard({
|
||||||
<div className="flex justify-between border-t border-slate-800/10 pt-2 dark:border-slate-300/20">
|
<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">
|
<span className="text-sm text-slate-600 dark:text-slate-400">
|
||||||
{m.dhcp_lease_maximum_transfer_unit()}
|
{m.dhcp_lease_maximum_transfer_unit()}
|
||||||
</span>
|
|
||||||
<span className="text-sm font-medium">
|
|
||||||
{networkState?.dhcp_lease?.mtu}
|
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
|
<span className="text-sm font-medium">{networkState?.dhcp_lease?.mtu}</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|
@ -198,10 +191,9 @@ export default function DhcpLeaseCard({
|
||||||
<div className="flex justify-between border-t border-slate-800/10 pt-2 dark:border-slate-300/20">
|
<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">
|
<span className="text-sm text-slate-600 dark:text-slate-400">
|
||||||
{m.dhcp_lease_time_to_live()}
|
{m.dhcp_lease_time_to_live()}
|
||||||
</span>
|
|
||||||
<span className="text-sm font-medium">
|
|
||||||
{networkState?.dhcp_lease?.ttl}
|
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
|
<span className="text-sm font-medium">{networkState?.dhcp_lease?.ttl}</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|
@ -209,7 +201,8 @@ export default function DhcpLeaseCard({
|
||||||
<div className="flex justify-between border-t border-slate-800/10 pt-2 dark:border-slate-300/20">
|
<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">
|
<span className="text-sm text-slate-600 dark:text-slate-400">
|
||||||
{m.dhcp_lease_boot_next_server()}
|
{m.dhcp_lease_boot_next_server()}
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span className="text-sm font-medium">
|
<span className="text-sm font-medium">
|
||||||
{networkState?.dhcp_lease?.bootp_next_server}
|
{networkState?.dhcp_lease?.bootp_next_server}
|
||||||
</span>
|
</span>
|
||||||
|
|
@ -220,7 +213,8 @@ export default function DhcpLeaseCard({
|
||||||
<div className="flex justify-between border-t border-slate-800/10 pt-2 dark:border-slate-300/20">
|
<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">
|
<span className="text-sm text-slate-600 dark:text-slate-400">
|
||||||
{m.dhcp_lease_boot_server_name()}
|
{m.dhcp_lease_boot_server_name()}
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span className="text-sm font-medium">
|
<span className="text-sm font-medium">
|
||||||
{networkState?.dhcp_lease?.bootp_server_name}
|
{networkState?.dhcp_lease?.bootp_server_name}
|
||||||
</span>
|
</span>
|
||||||
|
|
@ -231,7 +225,8 @@ export default function DhcpLeaseCard({
|
||||||
<div className="flex justify-between border-t border-slate-800/10 pt-2 dark:border-slate-300/20">
|
<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">
|
<span className="text-sm text-slate-600 dark:text-slate-400">
|
||||||
{m.dhcp_lease_boot_file()}
|
{m.dhcp_lease_boot_file()}
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span className="text-sm font-medium">
|
<span className="text-sm font-medium">
|
||||||
{networkState?.dhcp_lease?.bootp_file}
|
{networkState?.dhcp_lease?.bootp_file}
|
||||||
</span>
|
</span>
|
||||||
|
|
@ -242,7 +237,8 @@ export default function DhcpLeaseCard({
|
||||||
<div className="flex justify-between border-t border-slate-800/10 pt-2 dark:border-slate-300/20">
|
<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">
|
<span className="text-sm text-slate-600 dark:text-slate-400">
|
||||||
{m.network_dhcp_client_title()}
|
{m.network_dhcp_client_title()}
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span className="text-sm font-medium">
|
<span className="text-sm font-medium">
|
||||||
{networkState?.dhcp_lease?.dhcp_client}
|
{networkState?.dhcp_lease?.dhcp_client}
|
||||||
</span>
|
</span>
|
||||||
|
|
|
||||||
|
|
@ -11,13 +11,7 @@ interface Props {
|
||||||
className?: string;
|
className?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function EmptyCard({
|
export default function EmptyCard({ IconElm, headline, description, BtnElm, className }: Props) {
|
||||||
IconElm,
|
|
||||||
headline,
|
|
||||||
description,
|
|
||||||
BtnElm,
|
|
||||||
className,
|
|
||||||
}: Props) {
|
|
||||||
return (
|
return (
|
||||||
<GridCard>
|
<GridCard>
|
||||||
<div
|
<div
|
||||||
|
|
@ -28,16 +22,12 @@ export default function EmptyCard({
|
||||||
>
|
>
|
||||||
<div className="max-w-[90%] space-y-1.5 text-center md:max-w-[60%]">
|
<div className="max-w-[90%] space-y-1.5 text-center md:max-w-[60%]">
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
{IconElm && (
|
{IconElm && <IconElm className="mx-auto h-5 w-5 text-blue-600 dark:text-blue-600" />}
|
||||||
<IconElm className="mx-auto h-5 w-5 text-blue-600 dark:text-blue-600" />
|
<h4 className="text-base leading-none font-bold text-black dark:text-white">
|
||||||
)}
|
|
||||||
<h4 className="text-base font-bold leading-none text-black dark:text-white">
|
|
||||||
{headline}
|
{headline}
|
||||||
</h4>
|
</h4>
|
||||||
</div>
|
</div>
|
||||||
<p className="mx-auto text-sm text-slate-600 dark:text-slate-400">
|
<p className="mx-auto text-sm text-slate-600 dark:text-slate-400">{description}</p>
|
||||||
{description}
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
{BtnElm}
|
{BtnElm}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -18,13 +18,10 @@ export function FailsafeModeBanner({ reason }: FailsafeModeBannerProps) {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card>
|
<Card>
|
||||||
<div className="diagonal-stripes flex items-center gap-3 p-4 rounded">
|
<div className="diagonal-stripes flex items-center gap-3 rounded p-4">
|
||||||
<LuTriangleAlert className="h-5 w-5 flex-shrink-0 text-red-600 dark:text-red-400" />
|
<LuTriangleAlert className="h-5 w-5 flex-shrink-0 text-red-600 dark:text-red-400" />
|
||||||
<p className="text-sm font-medium text-red-800 dark:text-white">
|
<p className="text-sm font-medium text-red-800 dark:text-white">{getReasonMessage()}</p>
|
||||||
{getReasonMessage()}
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,8 +14,6 @@ import { DOWNGRADE_VERSION } from "@/ui.config";
|
||||||
|
|
||||||
import { GitHubIcon } from "./Icons";
|
import { GitHubIcon } from "./Icons";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
interface FailSafeModeOverlayProps {
|
interface FailSafeModeOverlayProps {
|
||||||
reason: string;
|
reason: string;
|
||||||
}
|
}
|
||||||
|
|
@ -45,13 +43,12 @@ function Tooltip({ children, text, show }: TooltipProps) {
|
||||||
return <>{children}</>;
|
return <>{children}</>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="group/tooltip relative">
|
<div className="group/tooltip relative">
|
||||||
{children}
|
{children}
|
||||||
<div className="pointer-events-none absolute bottom-full left-1/2 mb-2 hidden -translate-x-1/2 opacity-0 transition-opacity group-hover/tooltip:block group-hover/tooltip:opacity-100">
|
<div className="pointer-events-none absolute bottom-full left-1/2 mb-2 hidden -translate-x-1/2 opacity-0 transition-opacity group-hover/tooltip:block group-hover/tooltip:opacity-100">
|
||||||
<Card>
|
<Card>
|
||||||
<div className="whitespace-nowrap px-2 py-1 text-xs flex items-center gap-1 justify-center">
|
<div className="flex items-center justify-center gap-1 px-2 py-1 text-xs whitespace-nowrap">
|
||||||
<LuInfo className="h-3 w-3 text-slate-700 dark:text-slate-300" />
|
<LuInfo className="h-3 w-3 text-slate-700 dark:text-slate-300" />
|
||||||
{text}
|
{text}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -152,7 +149,7 @@ Please attach the recovery logs file that was downloaded to your computer:
|
||||||
return (
|
return (
|
||||||
<AnimatePresence>
|
<AnimatePresence>
|
||||||
<motion.div
|
<motion.div
|
||||||
className="aspect-video h-full w-full isolate"
|
className="isolate aspect-video h-full w-full"
|
||||||
initial={{ opacity: 0 }}
|
initial={{ opacity: 0 }}
|
||||||
animate={{ opacity: 1 }}
|
animate={{ opacity: 1 }}
|
||||||
exit={{ opacity: 0, transition: { duration: 0 } }}
|
exit={{ opacity: 0, transition: { duration: 0 } }}
|
||||||
|
|
@ -178,10 +175,12 @@ Please attach the recovery logs file that was downloaded to your computer:
|
||||||
size="SM"
|
size="SM"
|
||||||
disabled={isDownloadingLogs}
|
disabled={isDownloadingLogs}
|
||||||
LeadingIcon={GitHubIcon}
|
LeadingIcon={GitHubIcon}
|
||||||
text={isDownloadingLogs ? "Downloading Logs..." : "Download Logs & Report Issue"}
|
text={
|
||||||
|
isDownloadingLogs ? "Downloading Logs..." : "Download Logs & Report Issue"
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div className="h-8 w-px bg-slate-200 dark:bg-slate-700 block" />
|
<div className="block h-8 w-px bg-slate-200 dark:bg-slate-700" />
|
||||||
<Tooltip text="Download logs first" show={!hasDownloadedLogs}>
|
<Tooltip text="Download logs first" show={!hasDownloadedLogs}>
|
||||||
<Button
|
<Button
|
||||||
onClick={() => navigateTo("/settings/general/reboot")}
|
onClick={() => navigateTo("/settings/general/reboot")}
|
||||||
|
|
@ -202,8 +201,6 @@ Please attach the recovery logs file that was downloaded to your computer:
|
||||||
/>
|
/>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -213,4 +210,3 @@ Please attach the recovery logs file that was downloaded to your computer:
|
||||||
</AnimatePresence>
|
</AnimatePresence>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,8 +19,8 @@ export function FeatureFlag({
|
||||||
if (!appVersion) return;
|
if (!appVersion) return;
|
||||||
console.log(
|
console.log(
|
||||||
`Feature '${name}' ${isEnabled ? "ENABLED" : "DISABLED"}: ` +
|
`Feature '${name}' ${isEnabled ? "ENABLED" : "DISABLED"}: ` +
|
||||||
`Current version: ${appVersion}, ` +
|
`Current version: ${appVersion}, ` +
|
||||||
`Required min version: ${minAppVersion || "N/A"}`,
|
`Required min version: ${minAppVersion || "N/A"}`,
|
||||||
);
|
);
|
||||||
}, [isEnabled, name, minAppVersion, appVersion]);
|
}, [isEnabled, name, minAppVersion, appVersion]);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,19 +9,13 @@ interface Props {
|
||||||
description?: string | React.ReactNode | null;
|
description?: string | React.ReactNode | null;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
}
|
}
|
||||||
export default function FieldLabel({
|
export default function FieldLabel({ label, id, as = "label", description, disabled }: Props) {
|
||||||
label,
|
|
||||||
id,
|
|
||||||
as = "label",
|
|
||||||
description,
|
|
||||||
disabled,
|
|
||||||
}: Props) {
|
|
||||||
if (as === "label") {
|
if (as === "label") {
|
||||||
return (
|
return (
|
||||||
<label
|
<label
|
||||||
htmlFor={id}
|
htmlFor={id}
|
||||||
className={cx(
|
className={cx(
|
||||||
"flex select-none flex-col text-left font-display text-[13px] font-semibold leading-snug text-black dark:text-white",
|
"flex flex-col text-left font-display text-[13px] leading-snug font-semibold text-black select-none dark:text-white",
|
||||||
disabled && "opacity-50",
|
disabled && "opacity-50",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
|
|
@ -35,8 +29,8 @@ export default function FieldLabel({
|
||||||
);
|
);
|
||||||
} else if (as === "span") {
|
} else if (as === "span") {
|
||||||
return (
|
return (
|
||||||
<div className="flex select-none flex-col">
|
<div className="flex flex-col select-none">
|
||||||
<span className="font-display text-[13px] font-semibold leading-snug text-black dark:text-white">
|
<span className="font-display text-[13px] leading-snug font-semibold text-black dark:text-white">
|
||||||
{label}
|
{label}
|
||||||
</span>
|
</span>
|
||||||
{description && (
|
{description && (
|
||||||
|
|
|
||||||
|
|
@ -18,11 +18,7 @@ export default function GridBackground() {
|
||||||
</pattern>
|
</pattern>
|
||||||
</defs>
|
</defs>
|
||||||
|
|
||||||
<svg
|
<svg x="50%" y={-1} className="overflow-visible fill-blue-100 dark:fill-blue-900/30">
|
||||||
x="50%"
|
|
||||||
y={-1}
|
|
||||||
className="overflow-visible fill-blue-100 dark:fill-blue-900/30"
|
|
||||||
>
|
|
||||||
<path
|
<path
|
||||||
d="M-200 0h201v201h-201Z M600 0h201v201h-201Z M-400 600h201v201h-201Z M200 800h201v201h-201Z"
|
d="M-200 0h201v201h-201Z M600 0h201v201h-201Z M-400 600h201v201h-201Z M200 800h201v201h-201Z"
|
||||||
strokeWidth={0}
|
strokeWidth={0}
|
||||||
|
|
|
||||||
|
|
@ -59,8 +59,8 @@ export default function DashboardNavbar({
|
||||||
<div className="flex h-14 items-center justify-between">
|
<div className="flex h-14 items-center justify-between">
|
||||||
<div className="flex shrink-0 items-center gap-x-8">
|
<div className="flex shrink-0 items-center gap-x-8">
|
||||||
<div className="inline-block shrink-0">
|
<div className="inline-block shrink-0">
|
||||||
<img src={LogoBlueIcon} alt="" className="h-[24px] dark:hidden" />
|
<img src={LogoBlueIcon} alt="" className="h-6 dark:hidden" />
|
||||||
<img src={LogoWhiteIcon} alt="" className="hidden h-[24px] dark:block" />
|
<img src={LogoWhiteIcon} alt="" className="hidden h-6 dark:block" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex gap-x-2">
|
<div className="flex gap-x-2">
|
||||||
|
|
@ -84,22 +84,16 @@ export default function DashboardNavbar({
|
||||||
{showConnectionStatus && (
|
{showConnectionStatus && (
|
||||||
<>
|
<>
|
||||||
<div className="w-[159px]">
|
<div className="w-[159px]">
|
||||||
<PeerConnectionStatusCard
|
<PeerConnectionStatusCard state={peerConnectionState} title={kvmName} />
|
||||||
state={peerConnectionState}
|
|
||||||
title={kvmName}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="hidden w-[159px] md:block">
|
<div className="hidden w-[159px] md:block">
|
||||||
<USBStateStatus
|
<USBStateStatus state={usbState} peerConnectionState={peerConnectionState} />
|
||||||
state={usbState}
|
|
||||||
peerConnectionState={peerConnectionState}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{isLoggedIn ? (
|
{isLoggedIn ? (
|
||||||
<>
|
<>
|
||||||
<hr className="h-[20px] w-px self-center border-none bg-slate-800/20 dark:bg-slate-300/20" />
|
<hr className="h-5 w-px self-center border-none bg-slate-800/20 dark:bg-slate-300/20" />
|
||||||
<div className="relative inline-block text-left">
|
<div className="relative inline-block text-left">
|
||||||
<Menu>
|
<Menu>
|
||||||
<MenuButton as="div" className="h-full">
|
<MenuButton as="div" className="h-full">
|
||||||
|
|
@ -111,7 +105,7 @@ export default function DashboardNavbar({
|
||||||
className="size-6 rounded-full border-2 border-transparent transition-colors group-hover:border-blue-700"
|
className="size-6 rounded-full border-2 border-transparent transition-colors group-hover:border-blue-700"
|
||||||
/>
|
/>
|
||||||
) : userEmail ? (
|
) : userEmail ? (
|
||||||
<span className="font-display max-w-[200px] truncate text-sm/6 font-semibold">
|
<span className="max-w-[200px] truncate font-display text-sm/6 font-semibold">
|
||||||
{userEmail}
|
{userEmail}
|
||||||
</span>
|
</span>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
@ -129,20 +123,15 @@ export default function DashboardNavbar({
|
||||||
<div className="space-y-1 p-1 dark:text-white">
|
<div className="space-y-1 p-1 dark:text-white">
|
||||||
<div className="border-b border-b-slate-800/20 dark:border-slate-300/20">
|
<div className="border-b border-b-slate-800/20 dark:border-slate-300/20">
|
||||||
<div className="p-2">
|
<div className="p-2">
|
||||||
<div className="font-display text-xs">
|
<div className="font-display text-xs">{m.logged_in_as()}</div>
|
||||||
{m.logged_in_as()}
|
<div className="max-w-[200px] truncate font-display text-sm font-semibold">
|
||||||
</div>
|
|
||||||
<div className="font-display max-w-[200px] truncate text-sm font-semibold">
|
|
||||||
{userEmail}
|
{userEmail}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div
|
<div className="space-y-1 p-1 dark:text-white" onClick={onLogout}>
|
||||||
className="space-y-1 p-1 dark:text-white"
|
|
||||||
onClick={onLogout}
|
|
||||||
>
|
|
||||||
<button className="group flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-sm transition-colors hover:bg-slate-100 dark:hover:bg-slate-700">
|
<button className="group flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-sm transition-colors hover:bg-slate-100 dark:hover:bg-slate-700">
|
||||||
<ArrowLeftEndOnRectangleIcon className="size-4" />
|
<ArrowLeftEndOnRectangleIcon className="size-4" />
|
||||||
<div className="font-display">{m.log_out()}</div>
|
<div className="font-display">{m.log_out()}</div>
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ import {
|
||||||
useRTCStore,
|
useRTCStore,
|
||||||
useSettingsStore,
|
useSettingsStore,
|
||||||
useVideoStore,
|
useVideoStore,
|
||||||
VideoState
|
VideoState,
|
||||||
} from "@hooks/stores";
|
} from "@hooks/stores";
|
||||||
import { useHidRpc } from "@hooks/useHidRpc";
|
import { useHidRpc } from "@hooks/useHidRpc";
|
||||||
import { keys, modifiers } from "@/keyboardMappings";
|
import { keys, modifiers } from "@/keyboardMappings";
|
||||||
|
|
@ -48,10 +48,10 @@ export default function InfoBar() {
|
||||||
}, [keysDownState, showPressedKeys]);
|
}, [keysDownState, showPressedKeys]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="bg-white border-t border-t-slate-800/30 text-slate-800 dark:border-t-slate-300/20 dark:bg-slate-900 dark:text-slate-300">
|
<div className="border-t border-t-slate-800/30 bg-white text-slate-800 dark:border-t-slate-300/20 dark:bg-slate-900 dark:text-slate-300">
|
||||||
<div className="flex flex-wrap items-stretch justify-between gap-1">
|
<div className="flex flex-wrap items-stretch justify-between gap-1">
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<div className="flex flex-wrap items-center pl-2 gap-x-4">
|
<div className="flex flex-wrap items-center gap-x-4 pl-2">
|
||||||
{debugMode ? (
|
{debugMode ? (
|
||||||
<div className="flex">
|
<div className="flex">
|
||||||
<span className="text-xs font-semibold">{m.info_resolution()}</span>{" "}
|
<span className="text-xs font-semibold">{m.info_resolution()}</span>{" "}
|
||||||
|
|
@ -66,18 +66,22 @@ export default function InfoBar() {
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
{(debugMode && mouseMode == "absolute") ? (
|
{debugMode && mouseMode == "absolute" ? (
|
||||||
<div className="flex w-[118px] items-center gap-x-1">
|
<div className="flex w-[118px] items-center gap-x-1">
|
||||||
<span className="text-xs font-semibold">{m.info_pointer()}</span>
|
<span className="text-xs font-semibold">{m.info_pointer()}</span>
|
||||||
<span className="text-xs">{mouseX},{mouseY}</span>
|
<span className="text-xs">
|
||||||
|
{mouseX},{mouseY}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
{(debugMode && mouseMode == "relative") ? (
|
{debugMode && mouseMode == "relative" ? (
|
||||||
<div className="flex w-[118px] items-center gap-x-1">
|
<div className="flex w-[118px] items-center gap-x-1">
|
||||||
<span className="text-xs font-semibold">{m.info_last_move()}</span>
|
<span className="text-xs font-semibold">{m.info_last_move()}</span>
|
||||||
<span className="text-xs">
|
<span className="text-xs">
|
||||||
{mouseMove ? `${mouseMove.x},${mouseMove.y} ${mouseMove.buttons ? `(${mouseMove.buttons})` : ""}` : "N/A"}
|
{mouseMove
|
||||||
|
? `${mouseMove.x},${mouseMove.y} ${mouseMove.buttons ? `(${mouseMove.buttons})` : ""}`
|
||||||
|
: "N/A"}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
@ -119,7 +123,7 @@ export default function InfoBar() {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex items-center divide-x first:divide-l divide-slate-800/20 dark:divide-slate-300/20">
|
<div className="first:divide-l flex items-center divide-x divide-slate-800/20 dark:divide-slate-300/20">
|
||||||
{isTurnServerInUse && (
|
{isTurnServerInUse && (
|
||||||
<div className="shrink-0 p-1 px-1.5 text-xs text-black dark:text-white">
|
<div className="shrink-0 p-1 px-1.5 text-xs text-black dark:text-white">
|
||||||
{m.info_relayed_by_cloudflare()}
|
{m.info_relayed_by_cloudflare()}
|
||||||
|
|
|
||||||
|
|
@ -43,14 +43,18 @@ const InputField = forwardRef<HTMLInputElement, InputFieldProps>(function InputF
|
||||||
"[&:has(:user-invalid)]:ring-2 [&:has(:user-invalid)]:ring-red-600 [&:has(:user-invalid)]:ring-offset-2",
|
"[&:has(:user-invalid)]:ring-2 [&:has(:user-invalid)]:ring-red-600 [&:has(:user-invalid)]:ring-offset-2",
|
||||||
|
|
||||||
// Focus Within
|
// Focus Within
|
||||||
"focus-within:border-slate-300 dark:focus-within:border-slate-600 focus-within:outline-hidden focus-within:ring-2 focus-within:ring-blue-700 focus-within:ring-offset-2",
|
"focus-within:border-slate-300 focus-within:ring-2 focus-within:ring-blue-700 focus-within:ring-offset-2 focus-within:outline-hidden dark:focus-within:border-slate-600",
|
||||||
|
|
||||||
// Disabled Within
|
// Disabled Within
|
||||||
"disabled-within:pointer-events-none disabled-within:select-none disabled-within:bg-slate-50 dark:disabled-within:bg-slate-800 disabled-within:text-slate-500/80",
|
"disabled-within:pointer-events-none disabled-within:bg-slate-50 disabled-within:text-slate-500/80 disabled-within:select-none dark:disabled-within:bg-slate-800",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{LeadingElm && (
|
{LeadingElm && (
|
||||||
<div className={clsx("pointer-events-none border-r border-r-slate-300 dark:border-r-slate-600")}>
|
<div
|
||||||
|
className={clsx(
|
||||||
|
"pointer-events-none border-r border-r-slate-300 dark:border-r-slate-600",
|
||||||
|
)}
|
||||||
|
>
|
||||||
{LeadingElm}
|
{LeadingElm}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
@ -60,12 +64,12 @@ const InputField = forwardRef<HTMLInputElement, InputFieldProps>(function InputF
|
||||||
sizeClasses,
|
sizeClasses,
|
||||||
TrailingElm ? "pr-2" : "",
|
TrailingElm ? "pr-2" : "",
|
||||||
className,
|
className,
|
||||||
"block flex-1 border-0 bg-transparent leading-none placeholder:text-sm placeholder:text-slate-300 dark:placeholder:text-slate-500 focus:ring-0 text-black dark:text-white",
|
"block flex-1 border-0 bg-transparent leading-none text-black placeholder:text-sm placeholder:text-slate-300 focus:ring-0 dark:text-white dark:placeholder:text-slate-500",
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
{TrailingElm && (
|
{TrailingElm && (
|
||||||
<div className="flex items-center pr-3 pointer-events-none">{TrailingElm}</div>
|
<div className="pointer-events-none flex items-center pr-3">{TrailingElm}</div>
|
||||||
)}
|
)}
|
||||||
</Card>
|
</Card>
|
||||||
{error && <FieldError error={error} />}
|
{error && <FieldError error={error} />}
|
||||||
|
|
@ -75,15 +79,10 @@ const InputField = forwardRef<HTMLInputElement, InputFieldProps>(function InputF
|
||||||
InputField.displayName = "InputField";
|
InputField.displayName = "InputField";
|
||||||
|
|
||||||
const InputFieldWithLabel = forwardRef<HTMLInputElement, InputFieldWithLabelProps>(
|
const InputFieldWithLabel = forwardRef<HTMLInputElement, InputFieldWithLabelProps>(
|
||||||
function InputFieldWithLabel(
|
function InputFieldWithLabel({ label, description, id, ...props }, ref: Ref<HTMLInputElement>) {
|
||||||
{ label, description, id, ...props },
|
|
||||||
ref: Ref<HTMLInputElement>,
|
|
||||||
) {
|
|
||||||
return (
|
return (
|
||||||
<div className="w-full space-y-1">
|
<div className="w-full space-y-1">
|
||||||
{(label || description) && (
|
{(label || description) && <FieldLabel label={label} id={id} description={description} />}
|
||||||
<FieldLabel label={label} id={id} description={description} />
|
|
||||||
)}
|
|
||||||
<InputField ref={ref as never} id={id} {...props} />
|
<InputField ref={ref as never} id={id} {...props} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -4,16 +4,14 @@ import { GridCard } from "@components/Card";
|
||||||
import { LifeTimeLabel } from "@routes/devices.$id.settings.network";
|
import { LifeTimeLabel } from "@routes/devices.$id.settings.network";
|
||||||
import { m } from "@localizations/messages.js";
|
import { m } from "@localizations/messages.js";
|
||||||
|
|
||||||
export function FlagLabel({ flag, className }: { flag: string, className?: string }) {
|
export function FlagLabel({ flag, className }: { flag: string; className?: string }) {
|
||||||
const classes = cx(
|
const classes = cx(
|
||||||
"ml-2 rounded-sm bg-red-500 px-2 py-1 text-[10px] font-medium leading-none text-white dark:border",
|
"ml-2 rounded-sm bg-red-500 px-2 py-1 text-[10px] leading-none font-medium text-white dark:border",
|
||||||
"bg-red-500 text-white dark:border-red-700 dark:bg-red-800 dark:text-red-50",
|
"bg-red-500 text-white dark:border-red-700 dark:bg-red-800 dark:text-red-50",
|
||||||
className,
|
className,
|
||||||
);
|
);
|
||||||
|
|
||||||
return <span className={classes}>
|
return <span className={classes}>{flag}</span>;
|
||||||
{flag}
|
|
||||||
</span>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function Ipv6NetworkCard({
|
export default function Ipv6NetworkCard({
|
||||||
|
|
@ -33,28 +31,22 @@ export default function Ipv6NetworkCard({
|
||||||
<div className="flex flex-col justify-between">
|
<div className="flex flex-col justify-between">
|
||||||
<span className="text-sm text-slate-600 dark:text-slate-400">
|
<span className="text-sm text-slate-600 dark:text-slate-400">
|
||||||
{m.ipv6_link_local()}
|
{m.ipv6_link_local()}
|
||||||
</span>
|
|
||||||
<span className="text-sm font-medium">
|
|
||||||
{networkState?.ipv6_link_local}
|
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
|
<span className="text-sm font-medium">{networkState?.ipv6_link_local}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex flex-col justify-between">
|
<div className="flex flex-col justify-between">
|
||||||
<span className="text-sm text-slate-600 dark:text-slate-400">
|
<span className="text-sm text-slate-600 dark:text-slate-400">{m.ipv6_gateway()}</span>
|
||||||
{m.ipv6_gateway()}
|
|
||||||
</span>
|
<span className="text-sm font-medium">{networkState?.ipv6_gateway}</span>
|
||||||
<span className="text-sm font-medium">
|
|
||||||
{networkState?.ipv6_gateway}
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="space-y-3 pt-2">
|
<div className="space-y-3 pt-2">
|
||||||
{networkState?.ipv6_addresses && networkState?.ipv6_addresses.length > 0 && (
|
{networkState?.ipv6_addresses && networkState?.ipv6_addresses.length > 0 && (
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
<h4 className="text-sm font-semibold">
|
<h4 className="text-sm font-semibold">{m.network_ipv6_addresses_header()}</h4>
|
||||||
{m.network_ipv6_addresses_header()}
|
|
||||||
</h4>
|
|
||||||
{networkState.ipv6_addresses.map(addr => (
|
{networkState.ipv6_addresses.map(addr => (
|
||||||
<div
|
<div
|
||||||
key={addr.address}
|
key={addr.address}
|
||||||
|
|
@ -64,12 +56,17 @@ export default function Ipv6NetworkCard({
|
||||||
<div className="col-span-2 flex flex-col justify-between">
|
<div className="col-span-2 flex flex-col justify-between">
|
||||||
<span className="text-sm text-slate-600 dark:text-slate-400">
|
<span className="text-sm text-slate-600 dark:text-slate-400">
|
||||||
{m.ipv6_address_label()}
|
{m.ipv6_address_label()}
|
||||||
</span>
|
</span>
|
||||||
<span className="text-sm font-medium flex">
|
|
||||||
|
<span className="flex text-sm font-medium">
|
||||||
<span className="flex-1">{addr.address}</span>
|
<span className="flex-1">{addr.address}</span>
|
||||||
<span className="text-sm font-medium flex gap-x-1">
|
<span className="flex gap-x-1 text-sm font-medium">
|
||||||
{addr.flag_deprecated ? <FlagLabel flag={m.network_ipv6_flag_deprecated()} /> : null}
|
{addr.flag_deprecated ? (
|
||||||
{addr.flag_dad_failed ? <FlagLabel flag={m.network_ipv6_flag_dad_failed()} /> : null}
|
<FlagLabel flag={m.network_ipv6_flag_deprecated()} />
|
||||||
|
) : null}
|
||||||
|
{addr.flag_dad_failed ? (
|
||||||
|
<FlagLabel flag={m.network_ipv6_flag_dad_failed()} />
|
||||||
|
) : null}
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -78,7 +75,8 @@ export default function Ipv6NetworkCard({
|
||||||
<div className="flex flex-col justify-between">
|
<div className="flex flex-col justify-between">
|
||||||
<span className="text-sm text-slate-600 dark:text-slate-400">
|
<span className="text-sm text-slate-600 dark:text-slate-400">
|
||||||
{m.ipv6_valid_lifetime()}
|
{m.ipv6_valid_lifetime()}
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span className="text-sm font-medium">
|
<span className="text-sm font-medium">
|
||||||
{addr.valid_lifetime === "" ? (
|
{addr.valid_lifetime === "" ? (
|
||||||
<span className="text-slate-400 dark:text-slate-600">
|
<span className="text-slate-400 dark:text-slate-600">
|
||||||
|
|
@ -95,7 +93,8 @@ export default function Ipv6NetworkCard({
|
||||||
<div className="flex flex-col justify-between">
|
<div className="flex flex-col justify-between">
|
||||||
<span className="text-sm text-slate-600 dark:text-slate-400">
|
<span className="text-sm text-slate-600 dark:text-slate-400">
|
||||||
{m.ipv6_preferred_lifetime()}
|
{m.ipv6_preferred_lifetime()}
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span className="text-sm font-medium">
|
<span className="text-sm font-medium">
|
||||||
{addr.preferred_lifetime === "" ? (
|
{addr.preferred_lifetime === "" ? (
|
||||||
<span className="text-slate-400 dark:text-slate-600">
|
<span className="text-slate-400 dark:text-slate-600">
|
||||||
|
|
|
||||||
|
|
@ -72,7 +72,9 @@ export function JigglerSetting({
|
||||||
return (
|
return (
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<h4 className="text-sm font-semibold text-gray-900 dark:text-gray-100">{m.jiggler_examples_label()}</h4>
|
<h4 className="text-sm font-semibold text-gray-900 dark:text-gray-100">
|
||||||
|
{m.jiggler_examples_label()}
|
||||||
|
</h4>
|
||||||
<div className="flex flex-wrap gap-2">
|
<div className="flex flex-wrap gap-2">
|
||||||
{exampleConfigs.map((example, index) => (
|
{exampleConfigs.map((example, index) => (
|
||||||
<Button
|
<Button
|
||||||
|
|
|
||||||
|
|
@ -53,12 +53,10 @@ export default function KvmCard({
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<Card>
|
<Card>
|
||||||
<div className="px-5 py-5 space-y-3">
|
<div className="space-y-3 px-5 py-5">
|
||||||
<div className="flex justify-between items-center">
|
<div className="flex items-center justify-between">
|
||||||
<div className="space-y-1.5">
|
<div className="space-y-1.5">
|
||||||
<div className="text-lg font-bold leading-none text-black dark:text-white">
|
<div className="text-lg leading-none font-bold text-black dark:text-white">{title}</div>
|
||||||
{title}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{online ? (
|
{online ? (
|
||||||
<div className="flex items-center gap-x-1.5">
|
<div className="flex items-center gap-x-1.5">
|
||||||
|
|
@ -67,7 +65,7 @@ export default function KvmCard({
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="flex items-center gap-x-1.5">
|
<div className="flex items-center gap-x-1.5">
|
||||||
<div className="h-2.5 w-2.5 rounded-full border border-slate-400/60 dark:border-slate-500 bg-slate-200 dark:bg-slate-600" />
|
<div className="h-2.5 w-2.5 rounded-full border border-slate-400/60 bg-slate-200 dark:border-slate-500 dark:bg-slate-600" />
|
||||||
<div className="text-sm text-black dark:text-white">
|
<div className="text-sm text-black dark:text-white">
|
||||||
{lastSeen ? (
|
{lastSeen ? (
|
||||||
<>{m.last_online({ time: getRelativeTimeString(lastSeen) })}</>
|
<>{m.last_online({ time: getRelativeTimeString(lastSeen) })}</>
|
||||||
|
|
@ -109,14 +107,14 @@ export default function KvmCard({
|
||||||
></MenuButton>
|
></MenuButton>
|
||||||
<MenuItems
|
<MenuItems
|
||||||
transition
|
transition
|
||||||
className="data-closed:scale-95 data-closed:transform data-closed:opacity-0 data-enter:duration-100 data-leave:duration-75 data-enter:ease-out data-leave:ease-in"
|
className="data-closed:scale-95 data-closed:transform data-closed:opacity-0 data-enter:duration-100 data-enter:ease-out data-leave:duration-75 data-leave:ease-in"
|
||||||
>
|
>
|
||||||
<Card className="absolute right-0 z-10 w-56 px-1 mt-2 transition origin-top-right ring-1 ring-black/50 focus:outline-hidden">
|
<Card className="absolute right-0 z-10 mt-2 w-56 origin-top-right px-1 ring-1 ring-black/50 transition focus:outline-hidden">
|
||||||
<div className="divide-y divide-slate-800/20 dark:divide-slate-300/20">
|
<div className="divide-y divide-slate-800/20 dark:divide-slate-300/20">
|
||||||
<MenuItem>
|
<MenuItem>
|
||||||
<div>
|
<div>
|
||||||
<div className="block w-full">
|
<div className="block w-full">
|
||||||
<div className="flex items-center px-2 my-1 text-sm transition-colors rounded-md gap-x-2 hover:bg-slate-100 dark:hover:bg-slate-700">
|
<div className="my-1 flex items-center gap-x-2 rounded-md px-2 text-sm transition-colors hover:bg-slate-100 dark:hover:bg-slate-700">
|
||||||
<Link
|
<Link
|
||||||
className="block w-full py-1.5 text-black dark:text-white"
|
className="block w-full py-1.5 text-black dark:text-white"
|
||||||
to={`./${id}/rename`}
|
to={`./${id}/rename`}
|
||||||
|
|
@ -130,7 +128,7 @@ export default function KvmCard({
|
||||||
<MenuItem>
|
<MenuItem>
|
||||||
<div>
|
<div>
|
||||||
<div className="block w-full">
|
<div className="block w-full">
|
||||||
<div className="flex items-center px-2 my-1 text-sm transition-colors rounded-md gap-x-2 hover:bg-slate-100 dark:hover:bg-slate-700">
|
<div className="my-1 flex items-center gap-x-2 rounded-md px-2 text-sm transition-colors hover:bg-slate-100 dark:hover:bg-slate-700">
|
||||||
<Link
|
<Link
|
||||||
className="block w-full py-1.5 text-black dark:text-white"
|
className="block w-full py-1.5 text-black dark:text-white"
|
||||||
to={`./${id}/deregister`}
|
to={`./${id}/deregister`}
|
||||||
|
|
|
||||||
|
|
@ -1,25 +1,14 @@
|
||||||
import clsx from "clsx";
|
import clsx from "clsx";
|
||||||
|
|
||||||
export default function LoadingSpinner({
|
export default function LoadingSpinner({ className }: { className: string | undefined }) {
|
||||||
className,
|
|
||||||
}: {
|
|
||||||
className: string | undefined;
|
|
||||||
}) {
|
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
className={clsx(className, "shrink-0 animate-spin p-[2px]")}
|
className={clsx(className, "shrink-0 animate-spin p-0.5")}
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
fill="none"
|
fill="none"
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
>
|
>
|
||||||
<circle
|
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4" />
|
||||||
className="opacity-25"
|
|
||||||
cx="12"
|
|
||||||
cy="12"
|
|
||||||
r="10"
|
|
||||||
stroke="currentColor"
|
|
||||||
strokeWidth="4"
|
|
||||||
/>
|
|
||||||
<path
|
<path
|
||||||
// className="opacity-75"
|
// className="opacity-75"
|
||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ export default function MacroBar() {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container className="bg-white dark:bg-slate-900 border-b border-b-slate-800/20 dark:border-b-slate-300/20">
|
<Container className="border-b border-b-slate-800/20 bg-white dark:border-b-slate-300/20 dark:bg-slate-900">
|
||||||
<div className="flex items-center gap-x-2 py-1.5">
|
<div className="flex items-center gap-x-2 py-1.5">
|
||||||
<div className="absolute -ml-5">
|
<div className="absolute -ml-5">
|
||||||
<LuCommand className="h-4 w-4 text-slate-500" />
|
<LuCommand className="h-4 w-4 text-slate-500" />
|
||||||
|
|
@ -45,4 +45,4 @@ export default function MacroBar() {
|
||||||
</div>
|
</div>
|
||||||
</Container>
|
</Container>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,11 +8,7 @@ import FieldLabel from "@components/FieldLabel";
|
||||||
import Fieldset from "@components/Fieldset";
|
import Fieldset from "@components/Fieldset";
|
||||||
import { InputFieldWithLabel, FieldError } from "@components/InputField";
|
import { InputFieldWithLabel, FieldError } from "@components/InputField";
|
||||||
import { MacroStepCard } from "@components/MacroStepCard";
|
import { MacroStepCard } from "@components/MacroStepCard";
|
||||||
import {
|
import { DEFAULT_DELAY, MAX_STEPS_PER_MACRO, MAX_KEYS_PER_STEP } from "@/constants/macros";
|
||||||
DEFAULT_DELAY,
|
|
||||||
MAX_STEPS_PER_MACRO,
|
|
||||||
MAX_KEYS_PER_STEP,
|
|
||||||
} from "@/constants/macros";
|
|
||||||
import { m } from "@localizations/messages.js";
|
import { m } from "@localizations/messages.js";
|
||||||
|
|
||||||
interface ValidationErrors {
|
interface ValidationErrors {
|
||||||
|
|
@ -61,8 +57,8 @@ export function MacroForm({
|
||||||
newErrors.name = m.macro_name_too_long();
|
newErrors.name = m.macro_name_too_long();
|
||||||
}
|
}
|
||||||
|
|
||||||
const steps = (macro.steps || []);
|
const steps = macro.steps || [];
|
||||||
|
|
||||||
if (steps.length) {
|
if (steps.length) {
|
||||||
const hasKeyOrModifier = steps.some(
|
const hasKeyOrModifier = steps.some(
|
||||||
step => step.keys.length > 0 || step.modifiers.length > 0,
|
step => step.keys.length > 0 || step.modifiers.length > 0,
|
||||||
|
|
@ -91,7 +87,9 @@ export function MacroForm({
|
||||||
await onSubmit(macro);
|
await onSubmit(macro);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error instanceof Error) {
|
if (error instanceof Error) {
|
||||||
showTemporaryError(m.macro_save_failed_error({error: error.message || m.unknown_error()}));
|
showTemporaryError(
|
||||||
|
m.macro_save_failed_error({ error: error.message || m.unknown_error() }),
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
showTemporaryError(m.macro_save_failed());
|
showTemporaryError(m.macro_save_failed());
|
||||||
}
|
}
|
||||||
|
|
@ -196,13 +194,13 @@ export function MacroForm({
|
||||||
<div>
|
<div>
|
||||||
<div className="flex items-center justify-between text-sm">
|
<div className="flex items-center justify-between text-sm">
|
||||||
<div className="flex items-center gap-1">
|
<div className="flex items-center gap-1">
|
||||||
<FieldLabel
|
<FieldLabel label={m.macro_steps_label()} description={m.macro_steps_description()} />
|
||||||
label={m.macro_steps_label()}
|
|
||||||
description={m.macro_steps_description()}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
<span className="text-slate-500 dark:text-slate-400">
|
<span className="text-slate-500 dark:text-slate-400">
|
||||||
{m.macro_step_count({ steps: macro.steps?.length || 0, max: MAX_STEPS_PER_MACRO })}
|
{m.macro_step_count({
|
||||||
|
steps: macro.steps?.length || 0,
|
||||||
|
max: MAX_STEPS_PER_MACRO,
|
||||||
|
})}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
{errors.steps?.[0]?.keys && (
|
{errors.steps?.[0]?.keys && (
|
||||||
|
|
@ -220,10 +218,10 @@ export function MacroForm({
|
||||||
onDelete={
|
onDelete={
|
||||||
macro.steps && macro.steps.length > 1
|
macro.steps && macro.steps.length > 1
|
||||||
? () => {
|
? () => {
|
||||||
const newSteps = [...(macro.steps || [])];
|
const newSteps = [...(macro.steps || [])];
|
||||||
newSteps.splice(stepIndex, 1);
|
newSteps.splice(stepIndex, 1);
|
||||||
setMacro(prev => ({ ...prev, steps: newSteps }));
|
setMacro(prev => ({ ...prev, steps: newSteps }));
|
||||||
}
|
}
|
||||||
: undefined
|
: undefined
|
||||||
}
|
}
|
||||||
onMoveUp={() => handleStepMove(stepIndex, "up")}
|
onMoveUp={() => handleStepMove(stepIndex, "up")}
|
||||||
|
|
@ -231,9 +229,7 @@ export function MacroForm({
|
||||||
onKeySelect={option => handleKeySelect(stepIndex, option)}
|
onKeySelect={option => handleKeySelect(stepIndex, option)}
|
||||||
onKeyQueryChange={query => handleKeyQueryChange(stepIndex, query)}
|
onKeyQueryChange={query => handleKeyQueryChange(stepIndex, query)}
|
||||||
keyQuery={keyQueries[stepIndex] || ""}
|
keyQuery={keyQueries[stepIndex] || ""}
|
||||||
onModifierChange={modifiers =>
|
onModifierChange={modifiers => handleModifierChange(stepIndex, modifiers)}
|
||||||
handleModifierChange(stepIndex, modifiers)
|
|
||||||
}
|
|
||||||
onDelayChange={delay => handleDelayChange(stepIndex, delay)}
|
onDelayChange={delay => handleDelayChange(stepIndex, delay)}
|
||||||
isLastStep={stepIndex === (macro.steps?.length || 0) - 1}
|
isLastStep={stepIndex === (macro.steps?.length || 0) - 1}
|
||||||
keyboard={selectedKeyboard}
|
keyboard={selectedKeyboard}
|
||||||
|
|
@ -248,7 +244,11 @@ export function MacroForm({
|
||||||
theme="light"
|
theme="light"
|
||||||
fullWidth
|
fullWidth
|
||||||
LeadingIcon={LuPlus}
|
LeadingIcon={LuPlus}
|
||||||
text={m.macro_add_step({ maxed_out: isMaxStepsReached ? m.macro_max_steps_reached({ max: MAX_STEPS_PER_MACRO }) : "" })}
|
text={m.macro_add_step({
|
||||||
|
maxed_out: isMaxStepsReached
|
||||||
|
? m.macro_max_steps_reached({ max: MAX_STEPS_PER_MACRO })
|
||||||
|
: "",
|
||||||
|
})}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (isMaxStepsReached) {
|
if (isMaxStepsReached) {
|
||||||
showTemporaryError(m.macro_max_steps_error({ max: MAX_STEPS_PER_MACRO }));
|
showTemporaryError(m.macro_max_steps_error({ max: MAX_STEPS_PER_MACRO }));
|
||||||
|
|
@ -257,10 +257,7 @@ export function MacroForm({
|
||||||
|
|
||||||
setMacro(prev => ({
|
setMacro(prev => ({
|
||||||
...prev,
|
...prev,
|
||||||
steps: [
|
steps: [...(prev.steps || []), { keys: [], modifiers: [], delay: DEFAULT_DELAY }],
|
||||||
...(prev.steps || []),
|
|
||||||
{ keys: [], modifiers: [], delay: DEFAULT_DELAY },
|
|
||||||
],
|
|
||||||
}));
|
}));
|
||||||
setErrors({});
|
setErrors({});
|
||||||
}}
|
}}
|
||||||
|
|
|
||||||
|
|
@ -72,7 +72,7 @@ const ensureArray = <T,>(arr: T[] | null | undefined): T[] => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const keyDisplay = (keyDisplayMap: Record<string, string>, key: string): string => {
|
const keyDisplay = (keyDisplayMap: Record<string, string>, key: string): string => {
|
||||||
return keyDisplayMap[key] || key
|
return keyDisplayMap[key] || key;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function MacroStepCard({
|
export function MacroStepCard({
|
||||||
|
|
@ -113,9 +113,7 @@ export function MacroStepCard({
|
||||||
|
|
||||||
const filteredKeys = useMemo(() => {
|
const filteredKeys = useMemo(() => {
|
||||||
const selectedKeys = ensureArray(step.keys);
|
const selectedKeys = ensureArray(step.keys);
|
||||||
const availableKeys = keyOptions.filter(
|
const availableKeys = keyOptions.filter(option => !selectedKeys.includes(option.value));
|
||||||
option => !selectedKeys.includes(option.value),
|
|
||||||
);
|
|
||||||
|
|
||||||
if (keyQuery === "") {
|
if (keyQuery === "") {
|
||||||
return availableKeys;
|
return availableKeys;
|
||||||
|
|
@ -186,9 +184,7 @@ export function MacroStepCard({
|
||||||
key={option.value}
|
key={option.value}
|
||||||
size="XS"
|
size="XS"
|
||||||
theme={
|
theme={
|
||||||
ensureArray(step.modifiers).includes(option.value)
|
ensureArray(step.modifiers).includes(option.value) ? "primary" : "light"
|
||||||
? "primary"
|
|
||||||
: "light"
|
|
||||||
}
|
}
|
||||||
text={option.label.split(" ")[1] || option.label}
|
text={option.label.split(" ")[1] || option.label}
|
||||||
onClick={() => handleModifierToggle(option.value)}
|
onClick={() => handleModifierToggle(option.value)}
|
||||||
|
|
@ -221,9 +217,7 @@ export function MacroStepCard({
|
||||||
className=""
|
className=""
|
||||||
theme="blank"
|
theme="blank"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
const newKeys = step.keys.filter(
|
const newKeys = step.keys.filter((_, i) => i !== keyIndex);
|
||||||
(_, i) => i !== keyIndex,
|
|
||||||
);
|
|
||||||
onKeySelect({ value: null, keys: newKeys });
|
onKeySelect({ value: null, keys: newKeys });
|
||||||
}}
|
}}
|
||||||
LeadingIcon={LuX}
|
LeadingIcon={LuX}
|
||||||
|
|
|
||||||
|
|
@ -35,10 +35,7 @@ interface MetricProps<T, K extends keyof T> {
|
||||||
* @param metrics - Expected to be ordered from oldest to newest.
|
* @param metrics - Expected to be ordered from oldest to newest.
|
||||||
* @param metricName - Name of the metric to create a chart array for.
|
* @param metricName - Name of the metric to create a chart array for.
|
||||||
*/
|
*/
|
||||||
export function createChartArray<T, K extends keyof T>(
|
export function createChartArray<T, K extends keyof T>(metrics: Map<number, T>, metricName: K) {
|
||||||
metrics: Map<number, T>,
|
|
||||||
metricName: K,
|
|
||||||
) {
|
|
||||||
const result: { date: number; metric: number | null }[] = [];
|
const result: { date: number; metric: number | null }[] = [];
|
||||||
const iter = metrics.entries();
|
const iter = metrics.entries();
|
||||||
let next = iter.next() as IteratorResult<[number, T]>;
|
let next = iter.next() as IteratorResult<[number, T]>;
|
||||||
|
|
@ -146,12 +143,7 @@ export function Metric<T, K extends keyof T>({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<MetricHeader
|
<MetricHeader title={title} description={description} badge={badge} badgeTheme={badgeTheme} />
|
||||||
title={title}
|
|
||||||
description={description}
|
|
||||||
badge={badge}
|
|
||||||
badgeTheme={badgeTheme}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<GridCard>
|
<GridCard>
|
||||||
<div
|
<div
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ import {
|
||||||
YAxis,
|
YAxis,
|
||||||
} from "recharts";
|
} from "recharts";
|
||||||
|
|
||||||
import { getLocale } from '@localizations/runtime.js';
|
import { getLocale } from "@localizations/runtime.js";
|
||||||
import CustomTooltip, { CustomTooltipProps } from "@components/CustomTooltip";
|
import CustomTooltip, { CustomTooltipProps } from "@components/CustomTooltip";
|
||||||
|
|
||||||
export default function MetricsChart({
|
export default function MetricsChart({
|
||||||
|
|
|
||||||
|
|
@ -18,27 +18,25 @@ const Modal = React.memo(function Modal({
|
||||||
<Dialog open={open} onClose={onClose} className="relative z-20">
|
<Dialog open={open} onClose={onClose} className="relative z-20">
|
||||||
<DialogBackdrop
|
<DialogBackdrop
|
||||||
transition
|
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"
|
className="fixed inset-0 bg-gray-500/75 transition-opacity data-closed:opacity-0 data-enter:duration-500 data-enter:ease-out data-leave:duration-200 data-leave:ease-in dark:bg-slate-900/90"
|
||||||
/>
|
/>
|
||||||
<div className="fixed inset-0 z-20 w-screen overflow-y-auto" style={{
|
<div
|
||||||
scrollbarGutter: 'stable'
|
className="fixed inset-0 z-20 w-screen overflow-y-auto"
|
||||||
}}>
|
style={{ scrollbarGutter: "stable" }}
|
||||||
|
>
|
||||||
{/* TODO: This doesn't work well with other-sessions */}
|
{/* 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">
|
<div className="flex min-h-full items-end justify-center p-4 text-center md:items-baseline md:p-4">
|
||||||
<DialogPanel
|
<DialogPanel
|
||||||
transition
|
transition
|
||||||
className={cx(
|
className={cx(
|
||||||
"pointer-events-none relative w-full md:my-8 md:mt-[10vh]!",
|
"pointer-events-none relative w-full md:my-8 md:mt-[10vh]!",
|
||||||
"transform transition-all data-closed:translate-y-8 data-closed:opacity-0 data-enter:duration-500 data-leave:duration-200 data-enter:ease-out data-leave:ease-in",
|
"transform transition-all data-closed:translate-y-8 data-closed:opacity-0 data-enter:duration-500 data-enter:ease-out data-leave:duration-200 data-leave:ease-in",
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<div className="pointer-events-auto inline-block w-full text-left">
|
<div className="pointer-events-auto inline-block w-full text-left">
|
||||||
<div className="flex justify-center" onClick={onClose}>
|
<div className="flex justify-center" onClick={onClose}>
|
||||||
<div
|
<div className="pointer-events-none w-full" onClick={e => e.stopPropagation()}>
|
||||||
className="pointer-events-none w-full"
|
|
||||||
onClick={e => e.stopPropagation()}
|
|
||||||
>
|
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ export function NestedSettingsGroup(props: NestedSettingsGroupProps) {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={cx(
|
className={cx(
|
||||||
"space-y-4 border-l-2 border-slate-200 ml-2 pl-4 dark:border-slate-700",
|
"ml-2 space-y-4 border-l-2 border-slate-200 pl-4 dark:border-slate-700",
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
|
|
@ -19,4 +19,3 @@ export function NestedSettingsGroup(props: NestedSettingsGroupProps) {
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,6 @@ import { JsonRpcResponse, useJsonRpc } from "@hooks/useJsonRpc";
|
||||||
import notifications from "@/notifications";
|
import notifications from "@/notifications";
|
||||||
import { formatters } from "@/utils";
|
import { formatters } from "@/utils";
|
||||||
|
|
||||||
|
|
||||||
const TimeAgoLabel = ({ date }: { date: Date }) => {
|
const TimeAgoLabel = ({ date }: { date: Date }) => {
|
||||||
const [timeAgo, setTimeAgo] = useState<string | undefined>(formatters.timeAgo(date));
|
const [timeAgo, setTimeAgo] = useState<string | undefined>(formatters.timeAgo(date));
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
@ -19,11 +18,7 @@ const TimeAgoLabel = ({ date }: { date: Date }) => {
|
||||||
return () => clearInterval(interval);
|
return () => clearInterval(interval);
|
||||||
}, [date]);
|
}, [date]);
|
||||||
|
|
||||||
return (
|
return <span className="text-sm text-slate-600 select-none dark:text-slate-400">{timeAgo}</span>;
|
||||||
<span className="text-sm text-slate-600 dark:text-slate-400 select-none">
|
|
||||||
{timeAgo}
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function PublicIPCard() {
|
export default function PublicIPCard() {
|
||||||
|
|
@ -34,19 +29,23 @@ export default function PublicIPCard() {
|
||||||
send("getPublicIPAddresses", { refresh: true }, (resp: JsonRpcResponse) => {
|
send("getPublicIPAddresses", { refresh: true }, (resp: JsonRpcResponse) => {
|
||||||
setPublicIPs([]);
|
setPublicIPs([]);
|
||||||
if ("error" in resp) {
|
if ("error" in resp) {
|
||||||
notifications.error(m.public_ip_card_refresh_error({ error: resp.error.data || m.unknown_error() }));
|
notifications.error(
|
||||||
|
m.public_ip_card_refresh_error({ error: resp.error.data || m.unknown_error() }),
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const publicIPs = resp.result as PublicIP[];
|
const publicIPs = resp.result as PublicIP[];
|
||||||
// sort the public IPs by IP address
|
// sort the public IPs by IP address
|
||||||
// IPv6 addresses are sorted after IPv4 addresses
|
// IPv6 addresses are sorted after IPv4 addresses
|
||||||
setPublicIPs(publicIPs.sort(({ ip: aIp }, { ip: bIp }) => {
|
setPublicIPs(
|
||||||
const aIsIPv6 = aIp.includes(":");
|
publicIPs.sort(({ ip: aIp }, { ip: bIp }) => {
|
||||||
const bIsIPv6 = bIp.includes(":");
|
const aIsIPv6 = aIp.includes(":");
|
||||||
if (aIsIPv6 && !bIsIPv6) return 1;
|
const bIsIPv6 = bIp.includes(":");
|
||||||
if (!aIsIPv6 && bIsIPv6) return -1;
|
if (aIsIPv6 && !bIsIPv6) return 1;
|
||||||
return aIp.localeCompare(bIp);
|
if (!aIsIPv6 && bIsIPv6) return -1;
|
||||||
}));
|
return aIp.localeCompare(bIp);
|
||||||
|
}),
|
||||||
|
);
|
||||||
});
|
});
|
||||||
}, [send, setPublicIPs]);
|
}, [send, setPublicIPs]);
|
||||||
|
|
||||||
|
|
@ -89,10 +88,11 @@ export default function PublicIPCard() {
|
||||||
<div className="flex gap-x-6 gap-y-2">
|
<div className="flex gap-x-6 gap-y-2">
|
||||||
<div className="flex-1 space-y-2">
|
<div className="flex-1 space-y-2">
|
||||||
{publicIPs?.map(ip => (
|
{publicIPs?.map(ip => (
|
||||||
<div key={ip.ip} className="flex justify-between border-slate-800/10 pt-2 dark:border-slate-300/20">
|
<div
|
||||||
<span className="text-sm font-medium">
|
key={ip.ip}
|
||||||
{ip.ip}
|
className="flex justify-between border-slate-800/10 pt-2 dark:border-slate-300/20"
|
||||||
</span>
|
>
|
||||||
|
<span className="text-sm font-medium">{ip.ip}</span>
|
||||||
{ip.last_updated && <TimeAgoLabel date={new Date(ip.last_updated)} />}
|
{ip.last_updated && <TimeAgoLabel date={new Date(ip.last_updated)} />}
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
|
|
||||||
|
|
@ -82,10 +82,10 @@ export const SelectMenuBasic = React.forwardRef<HTMLSelectElement, SelectMenuPro
|
||||||
"invalid:ring-2 invalid:ring-red-600 invalid:ring-offset-2",
|
"invalid:ring-2 invalid:ring-red-600 invalid:ring-offset-2",
|
||||||
|
|
||||||
// Focus
|
// Focus
|
||||||
"focus:outline-blue-600 focus:ring-2 focus:ring-blue-700 focus:ring-offset-2 dark:focus:outline-blue-500 dark:focus:ring-blue-500",
|
"focus:ring-2 focus:ring-blue-700 focus:ring-offset-2 focus:outline-blue-600 dark:focus:ring-blue-500 dark:focus:outline-blue-500",
|
||||||
|
|
||||||
// Disabled
|
// Disabled
|
||||||
"disabled:pointer-events-none disabled:select-none disabled:bg-slate-50 disabled:text-slate-500/80 dark:disabled:bg-slate-800 dark:disabled:text-slate-400/80",
|
"disabled:pointer-events-none disabled:bg-slate-50 disabled:text-slate-500/80 disabled:select-none dark:disabled:bg-slate-800 dark:disabled:text-slate-400/80",
|
||||||
)}
|
)}
|
||||||
value={value}
|
value={value}
|
||||||
id={id}
|
id={id}
|
||||||
|
|
|
||||||
|
|
@ -15,17 +15,14 @@ export function SettingsItem(props: SettingsItemProps) {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<label
|
<label
|
||||||
className={cx(
|
className={cx("flex items-center justify-between gap-x-8 rounded select-none", className)}
|
||||||
"flex select-none items-center justify-between gap-x-8 rounded",
|
|
||||||
className,
|
|
||||||
)}
|
|
||||||
>
|
>
|
||||||
<div className="space-y-0.5">
|
<div className="space-y-0.5">
|
||||||
<div className="flex items-center gap-x-2">
|
<div className="flex items-center gap-x-2">
|
||||||
<div className="flex items-center text-base font-semibold text-black dark:text-white">
|
<div className="flex items-center text-base font-semibold text-black dark:text-white">
|
||||||
{title}
|
{title}
|
||||||
{badge && (
|
{badge && (
|
||||||
<span className="ml-2 rounded-full bg-red-500 px-2 py-1 text-[10px] font-medium leading-none text-white dark:border dark:border-red-700 dark:bg-red-800 dark:text-red-50">
|
<span className="ml-2 rounded-full bg-red-500 px-2 py-1 text-[10px] leading-none font-medium text-white dark:border dark:border-red-700 dark:bg-red-800 dark:text-red-50">
|
||||||
{badge}
|
{badge}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,4 @@
|
||||||
export default function SettingsNestedSection({
|
export default function SettingsNestedSection({ children }: { children: React.ReactNode }) {
|
||||||
children,
|
|
||||||
}: {
|
|
||||||
children: React.ReactNode;
|
|
||||||
}) {
|
|
||||||
return (
|
return (
|
||||||
<div className="ml-2 border-l border-slate-800/30 pl-4 dark:border-slate-300/30">
|
<div className="ml-2 border-l border-slate-800/30 pl-4 dark:border-slate-300/30">
|
||||||
{children}
|
{children}
|
||||||
|
|
|
||||||
|
|
@ -18,4 +18,4 @@ export function SettingsPageHeader({
|
||||||
{action && <div className="">{action}</div>}
|
{action && <div className="">{action}</div>}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,9 +11,9 @@ export default function SidebarHeader({
|
||||||
setSidebarView: (view: AvailableSidebarViews | null) => void;
|
setSidebarView: (view: AvailableSidebarViews | null) => void;
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center justify-between border-b border-b-slate-800/20 bg-white px-4 py-1.5 font-semibold text-black dark:bg-slate-900 dark:border-b-slate-300/20">
|
<div className="flex items-center justify-between border-b border-b-slate-800/20 bg-white px-4 py-1.5 font-semibold text-black dark:border-b-slate-300/20 dark:bg-slate-900">
|
||||||
<div className="min-w-0" style={{ flex: 1 }}>
|
<div className="min-w-0" style={{ flex: 1 }}>
|
||||||
<h2 className="text-sm text-black truncate dark:text-white">{title}</h2>
|
<h2 className="truncate text-sm text-black dark:text-white">{title}</h2>
|
||||||
</div>
|
</div>
|
||||||
<Button
|
<Button
|
||||||
size="XS"
|
size="XS"
|
||||||
|
|
|
||||||
|
|
@ -5,16 +5,19 @@ import LogoBlueIcon from "@assets/logo-blue.png";
|
||||||
import LogoWhiteIcon from "@assets/logo-white.svg";
|
import LogoWhiteIcon from "@assets/logo-white.svg";
|
||||||
import Container from "@components/Container";
|
import Container from "@components/Container";
|
||||||
|
|
||||||
interface Props { logoHref?: string; actionElement?: React.ReactNode }
|
interface Props {
|
||||||
|
logoHref?: string;
|
||||||
|
actionElement?: React.ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
export default function SimpleNavbar({ logoHref, actionElement }: Props) {
|
export default function SimpleNavbar({ logoHref, actionElement }: Props) {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Container>
|
<Container>
|
||||||
<div className="pb-4 my-4 border-b border-b-800/20 isolate dark:border-b-slate-300/20">
|
<div className="border-b-800/20 isolate my-4 border-b pb-4 dark:border-b-slate-300/20">
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<Link to={logoHref ?? "/"} className="hidden h-[26px] dark:inline-block">
|
<Link to={logoHref ?? "/"} className="hidden h-[26px] dark:inline-block">
|
||||||
<img src={LogoWhiteIcon} alt="" className="h-[26px] dark:block hidden" />
|
<img src={LogoWhiteIcon} alt="" className="hidden h-[26px] dark:block" />
|
||||||
<img src={LogoBlueIcon} alt="" className="h-[26px] dark:hidden" />
|
<img src={LogoBlueIcon} alt="" className="h-[26px] dark:hidden" />
|
||||||
</Link>
|
</Link>
|
||||||
<div>{actionElement}</div>
|
<div>{actionElement}</div>
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ export default function StaticIpv4Card() {
|
||||||
|
|
||||||
const ipv4StaticAddress = watch("ipv4_static.address");
|
const ipv4StaticAddress = watch("ipv4_static.address");
|
||||||
const hideSubnetMask = ipv4StaticAddress?.includes("/");
|
const hideSubnetMask = ipv4StaticAddress?.includes("/");
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const parts = ipv4StaticAddress?.split("/", 2);
|
const parts = ipv4StaticAddress?.split("/", 2);
|
||||||
if (parts?.length !== 2) return;
|
if (parts?.length !== 2) return;
|
||||||
|
|
@ -38,12 +38,13 @@ export default function StaticIpv4Card() {
|
||||||
}, [ipv4StaticAddress, setValue]);
|
}, [ipv4StaticAddress, setValue]);
|
||||||
|
|
||||||
const ipv4Validation = (value: string) => {
|
const ipv4Validation = (value: string) => {
|
||||||
if (!validator.isIP(value, 4)) return m.network_ipv4_invalid()
|
if (!validator.isIP(value, 4)) return m.network_ipv4_invalid();
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
const validateIsIPOrCIDR4 = (value: string) => {
|
const validateIsIPOrCIDR4 = (value: string) => {
|
||||||
if (!validator.isIP(value) && !validator.isIPRange(value, 4)) return m.network_ipv4_invalid_cidr();
|
if (!validator.isIP(value) && !validator.isIPRange(value, 4))
|
||||||
|
return m.network_ipv4_invalid_cidr();
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -55,27 +56,35 @@ export default function StaticIpv4Card() {
|
||||||
{m.network_static_ipv4_header()}
|
{m.network_static_ipv4_header()}
|
||||||
</h3>
|
</h3>
|
||||||
|
|
||||||
<div className={cx("grid grid-cols-1 gap-4", hideSubnetMask ? "md:grid-cols-1" : "md:grid-cols-2")}>
|
<div
|
||||||
|
className={cx(
|
||||||
|
"grid grid-cols-1 gap-4",
|
||||||
|
hideSubnetMask ? "md:grid-cols-1" : "md:grid-cols-2",
|
||||||
|
)}
|
||||||
|
>
|
||||||
<InputFieldWithLabel
|
<InputFieldWithLabel
|
||||||
label={m.network_ipv4_address()}
|
label={m.network_ipv4_address()}
|
||||||
type="text"
|
type="text"
|
||||||
size="SM"
|
size="SM"
|
||||||
placeholder="192.168.1.100"
|
placeholder="192.168.1.100"
|
||||||
{
|
{...register("ipv4_static.address", {
|
||||||
...register("ipv4_static.address", {
|
validate: (value: string | undefined) => validateIsIPOrCIDR4(value ?? ""),
|
||||||
validate: (value: string | undefined) => validateIsIPOrCIDR4(value ?? "")
|
|
||||||
})}
|
})}
|
||||||
error={formState.errors.ipv4_static?.address?.message}
|
error={formState.errors.ipv4_static?.address?.message}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{!hideSubnetMask && <InputFieldWithLabel
|
{!hideSubnetMask && (
|
||||||
label={m.network_ipv4_netmask()}
|
<InputFieldWithLabel
|
||||||
type="text"
|
label={m.network_ipv4_netmask()}
|
||||||
size="SM"
|
type="text"
|
||||||
placeholder="255.255.255.0"
|
size="SM"
|
||||||
{...register("ipv4_static.netmask", { validate: (value: string | undefined) => ipv4Validation(value ?? "") })}
|
placeholder="255.255.255.0"
|
||||||
error={formState.errors.ipv4_static?.netmask?.message}
|
{...register("ipv4_static.netmask", {
|
||||||
/>}
|
validate: (value: string | undefined) => ipv4Validation(value ?? ""),
|
||||||
|
})}
|
||||||
|
error={formState.errors.ipv4_static?.netmask?.message}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<InputFieldWithLabel
|
<InputFieldWithLabel
|
||||||
|
|
@ -83,7 +92,9 @@ export default function StaticIpv4Card() {
|
||||||
type="text"
|
type="text"
|
||||||
size="SM"
|
size="SM"
|
||||||
placeholder="192.168.1.1"
|
placeholder="192.168.1.1"
|
||||||
{...register("ipv4_static.gateway", { validate: (value: string | undefined) => ipv4Validation(value ?? "") })}
|
{...register("ipv4_static.gateway", {
|
||||||
|
validate: (value: string | undefined) => ipv4Validation(value ?? ""),
|
||||||
|
})}
|
||||||
error={formState.errors.ipv4_static?.gateway?.message}
|
error={formState.errors.ipv4_static?.gateway?.message}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
|
@ -99,10 +110,9 @@ export default function StaticIpv4Card() {
|
||||||
type="text"
|
type="text"
|
||||||
size="SM"
|
size="SM"
|
||||||
placeholder="1.1.1.1"
|
placeholder="1.1.1.1"
|
||||||
{...register(
|
{...register(`ipv4_static.dns.${index}`, {
|
||||||
`ipv4_static.dns.${index}`,
|
validate: (value: string | undefined) => ipv4Validation(value ?? ""),
|
||||||
{ validate: (value: string | undefined) => ipv4Validation(value ?? "") }
|
})}
|
||||||
)}
|
|
||||||
error={formState.errors.ipv4_static?.dns?.[index]?.message}
|
error={formState.errors.ipv4_static?.dns?.[index]?.message}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,7 @@ export default function StaticIpv6Card() {
|
||||||
};
|
};
|
||||||
|
|
||||||
const ipv6Validation = (value: string) => {
|
const ipv6Validation = (value: string) => {
|
||||||
if (!validator.isIP(value, 6)) return m.network_ipv6_invalid()
|
if (!validator.isIP(value, 6)) return m.network_ipv6_invalid();
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -56,7 +56,9 @@ export default function StaticIpv6Card() {
|
||||||
type="text"
|
type="text"
|
||||||
size="SM"
|
size="SM"
|
||||||
placeholder="2001:db8::1/64"
|
placeholder="2001:db8::1/64"
|
||||||
{...register("ipv6_static.prefix", { validate: (value: string | undefined) => cidrValidation(value ?? "") })}
|
{...register("ipv6_static.prefix", {
|
||||||
|
validate: (value: string | undefined) => cidrValidation(value ?? ""),
|
||||||
|
})}
|
||||||
error={formState.errors.ipv6_static?.prefix?.message}
|
error={formState.errors.ipv6_static?.prefix?.message}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
|
@ -65,7 +67,9 @@ export default function StaticIpv6Card() {
|
||||||
type="text"
|
type="text"
|
||||||
size="SM"
|
size="SM"
|
||||||
placeholder="2001:db8::1"
|
placeholder="2001:db8::1"
|
||||||
{...register("ipv6_static.gateway", { validate: (value: string | undefined) => ipv6Validation(value ?? "") })}
|
{...register("ipv6_static.gateway", {
|
||||||
|
validate: (value: string | undefined) => ipv6Validation(value ?? ""),
|
||||||
|
})}
|
||||||
error={formState.errors.ipv6_static?.gateway?.message}
|
error={formState.errors.ipv6_static?.gateway?.message}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
|
@ -81,7 +85,9 @@ export default function StaticIpv6Card() {
|
||||||
type="text"
|
type="text"
|
||||||
size="SM"
|
size="SM"
|
||||||
placeholder="2001:4860:4860::8888"
|
placeholder="2001:4860:4860::8888"
|
||||||
{...register(`ipv6_static.dns.${index}`, { validate: (value: string | undefined) => ipv6Validation(value ?? "") })}
|
{...register(`ipv6_static.dns.${index}`, {
|
||||||
|
validate: (value: string | undefined) => ipv6Validation(value ?? ""),
|
||||||
|
})}
|
||||||
error={formState.errors.ipv6_static?.dns?.[index]?.message}
|
error={formState.errors.ipv6_static?.dns?.[index]?.message}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ export default function StatusCard({
|
||||||
statusIndicatorClassName,
|
statusIndicatorClassName,
|
||||||
}: Props) {
|
}: Props) {
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center gap-x-3 rounded-md border bg-white dark:border-slate-600 dark:bg-slate-800 dark:text-white border-slate-800/20 px-2 py-1.5">
|
<div className="flex items-center gap-x-3 rounded-md border border-slate-800/20 bg-white px-2 py-1.5 dark:border-slate-600 dark:bg-slate-800 dark:text-white">
|
||||||
{Icon ? (
|
{Icon ? (
|
||||||
<span>
|
<span>
|
||||||
<Icon className={cx(iconClassName, "shrink-0")} />
|
<Icon className={cx(iconClassName, "shrink-0")} />
|
||||||
|
|
@ -26,16 +26,11 @@ export default function StatusCard({
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
<div className="text-xs font-semibold leading-none transition text-ellipsis">
|
<div className="text-xs leading-none font-semibold text-ellipsis transition">{title}</div>
|
||||||
{title}
|
|
||||||
</div>
|
|
||||||
<div className="text-xs leading-none">
|
<div className="text-xs leading-none">
|
||||||
<div className="flex items-center gap-x-1">
|
<div className="flex items-center gap-x-1">
|
||||||
<div
|
<div
|
||||||
className={cx(
|
className={cx("h-2 w-2 rounded-full border transition", statusIndicatorClassName)}
|
||||||
"h-2 w-2 rounded-full border transition",
|
|
||||||
statusIndicatorClassName,
|
|
||||||
)}
|
|
||||||
/>
|
/>
|
||||||
<span className={cx("transition")}>{status}</span>
|
<span className={cx("transition")}>{status}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ const variants = cva({
|
||||||
export default function StepCounter({ nSteps, currStepIdx, size = "MD" }: Props) {
|
export default function StepCounter({ nSteps, currStepIdx, size = "MD" }: Props) {
|
||||||
const textStyle = variants({ size });
|
const textStyle = variants({ size });
|
||||||
return (
|
return (
|
||||||
<Card className="inline-flex! w-auto select-none items-center justify-center gap-x-2 rounded-lg p-1">
|
<Card className="inline-flex! w-auto items-center justify-center gap-x-2 rounded-lg p-1 select-none">
|
||||||
{[...Array(nSteps).keys()].map(i => {
|
{[...Array(nSteps).keys()].map(i => {
|
||||||
if (i < currStepIdx) {
|
if (i < currStepIdx) {
|
||||||
return (
|
return (
|
||||||
|
|
|
||||||
|
|
@ -54,7 +54,7 @@ const TERMINAL_CONFIG = {
|
||||||
// Add these configurations:
|
// Add these configurations:
|
||||||
cursorStyle: "block",
|
cursorStyle: "block",
|
||||||
rendererType: "canvas", // Ensure we're using the canvas renderer
|
rendererType: "canvas", // Ensure we're using the canvas renderer
|
||||||
unicode: { activeVersion: "11" }
|
unicode: { activeVersion: "11" },
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
function Terminal({
|
function Terminal({
|
||||||
|
|
@ -162,10 +162,7 @@ function Terminal({
|
||||||
}, [instance]);
|
}, [instance]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div onKeyDown={e => e.stopPropagation()} onKeyUp={e => e.stopPropagation()}>
|
||||||
onKeyDown={e => e.stopPropagation()}
|
|
||||||
onKeyUp={e => e.stopPropagation()}
|
|
||||||
>
|
|
||||||
<div>
|
<div>
|
||||||
<div
|
<div
|
||||||
className={cx(
|
className={cx(
|
||||||
|
|
@ -184,7 +181,7 @@ function Terminal({
|
||||||
>
|
>
|
||||||
<div className="h-[500px] w-full bg-[#0f172a]">
|
<div className="h-[500px] w-full bg-[#0f172a]">
|
||||||
<div className="flex items-center justify-center border-y border-y-slate-800/30 bg-white px-2 py-1 dark:border-y-slate-300/20 dark:bg-slate-800">
|
<div className="flex items-center justify-center border-y border-y-slate-800/30 bg-white px-2 py-1 dark:border-y-slate-300/20 dark:bg-slate-800">
|
||||||
<h2 className="select-none self-center font-sans text-[12px] text-slate-700 dark:text-slate-300">
|
<h2 className="self-center font-sans text-[12px] text-slate-700 select-none dark:text-slate-300">
|
||||||
{title}
|
{title}
|
||||||
</h2>
|
</h2>
|
||||||
<div className="absolute right-2">
|
<div className="absolute right-2">
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ const TextArea = React.forwardRef<HTMLTextAreaElement, TextAreaProps>(
|
||||||
className={cx(
|
className={cx(
|
||||||
"relative w-full",
|
"relative w-full",
|
||||||
"invalid-within::ring-2 invalid-within::ring-red-600 invalid-within::ring-offset-2",
|
"invalid-within::ring-2 invalid-within::ring-red-600 invalid-within::ring-offset-2",
|
||||||
"focus-within:border-slate-300 focus-within:outline-hidden focus-within:ring-1 focus-within:ring-blue-700 dark:focus-within:border-slate-600",
|
"focus-within:border-slate-300 focus-within:ring-1 focus-within:ring-blue-700 focus-within:outline-hidden dark:focus-within:border-slate-600",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<textarea
|
<textarea
|
||||||
|
|
@ -25,7 +25,7 @@ const TextArea = React.forwardRef<HTMLTextAreaElement, TextAreaProps>(
|
||||||
{...props}
|
{...props}
|
||||||
id="asd"
|
id="asd"
|
||||||
className={clsx(
|
className={clsx(
|
||||||
"block w-full rounded-sm border-transparent bg-transparent text-black placeholder:text-slate-300 focus:ring-0 disabled:pointer-events-none disabled:select-none disabled:bg-slate-50 disabled:text-slate-300 dark:text-white dark:placeholder:text-slate-500 dark:disabled:bg-slate-800 sm:text-sm",
|
"block w-full rounded-sm border-transparent bg-transparent text-black placeholder:text-slate-300 focus:ring-0 disabled:pointer-events-none disabled:bg-slate-50 disabled:text-slate-300 disabled:select-none sm:text-sm dark:text-white dark:placeholder:text-slate-500 dark:disabled:bg-slate-800",
|
||||||
props.className,
|
props.className,
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|
@ -41,17 +41,16 @@ type TextAreaWithLabelProps = {
|
||||||
error?: string | null;
|
error?: string | null;
|
||||||
} & React.ComponentProps<typeof TextArea>;
|
} & React.ComponentProps<typeof TextArea>;
|
||||||
|
|
||||||
export const TextAreaWithLabel = React.forwardRef<
|
export const TextAreaWithLabel = React.forwardRef<HTMLTextAreaElement, TextAreaWithLabelProps>(
|
||||||
HTMLTextAreaElement,
|
function TextAreaWithLabel({ label, error, id, description, ...props }, ref) {
|
||||||
TextAreaWithLabelProps
|
return (
|
||||||
>(function TextAreaWithLabel({ label, error, id, description, ...props }, ref) {
|
<div className="space-y-1">
|
||||||
return (
|
<FieldLabel label={label} id={id} description={description} />
|
||||||
<div className="space-y-1">
|
<TextArea ref={ref} {...props} />
|
||||||
<FieldLabel label={label} id={id} description={description} />
|
{error && <FieldError error={error} />}
|
||||||
<TextArea ref={ref} {...props} />
|
</div>
|
||||||
{error && <FieldError error={error} />}
|
);
|
||||||
</div>
|
},
|
||||||
);
|
);
|
||||||
});
|
|
||||||
|
|
||||||
export default TextArea;
|
export default TextArea;
|
||||||
|
|
|
||||||
|
|
@ -64,7 +64,6 @@ export default function USBStateStatus({
|
||||||
state: USBStates;
|
state: USBStates;
|
||||||
peerConnectionState?: RTCPeerConnectionState | null;
|
peerConnectionState?: RTCPeerConnectionState | null;
|
||||||
}) {
|
}) {
|
||||||
|
|
||||||
const props = StatusCardProps[state];
|
const props = StatusCardProps[state];
|
||||||
if (!props) {
|
if (!props) {
|
||||||
console.warn("Unsupported USB state: ", state);
|
console.warn("Unsupported USB state: ", state);
|
||||||
|
|
@ -73,11 +72,7 @@ export default function USBStateStatus({
|
||||||
|
|
||||||
// If the peer connection is not connected, show the USB cable as disconnected
|
// If the peer connection is not connected, show the USB cable as disconnected
|
||||||
if (peerConnectionState !== "connected") {
|
if (peerConnectionState !== "connected") {
|
||||||
const {
|
const { icon: Icon, iconClassName, statusIndicatorClassName } = StatusCardProps["not attached"];
|
||||||
icon: Icon,
|
|
||||||
iconClassName,
|
|
||||||
statusIndicatorClassName,
|
|
||||||
} = StatusCardProps["not attached"];
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StatusCard
|
<StatusCard
|
||||||
|
|
@ -90,7 +85,5 @@ export default function USBStateStatus({
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return <StatusCard title={m.usb()} status={USBStateMap[state]} {...StatusCardProps[state]} />;
|
||||||
<StatusCard title={m.usb()} status={USBStateMap[state]} {...StatusCardProps[state]} />
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,20 +9,18 @@ export default function UpdateInProgressStatusCard() {
|
||||||
const { navigateTo } = useDeviceUiNavigation();
|
const { navigateTo } = useDeviceUiNavigation();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-full select-none opacity-100 transition-all duration-300 ease-in-out">
|
<div className="w-full opacity-100 transition-all duration-300 ease-in-out select-none">
|
||||||
<GridCard cardClassName="shadow-xl!">
|
<GridCard cardClassName="shadow-xl!">
|
||||||
<div className="flex items-center justify-between gap-x-3 px-2.5 py-2.5 text-black dark:text-white">
|
<div className="flex items-center justify-between gap-x-3 px-2.5 py-2.5 text-black dark:text-white">
|
||||||
<div className="flex items-center gap-x-3">
|
<div className="flex items-center gap-x-3">
|
||||||
<LoadingSpinner className={cx("h-5 w-5", "shrink-0 text-blue-700")} />
|
<LoadingSpinner className={cx("h-5 w-5", "shrink-0 text-blue-700")} />
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
<div className="text-ellipsis text-sm font-semibold leading-none transition">
|
<div className="text-sm leading-none font-semibold text-ellipsis transition">
|
||||||
{m.update_in_progress()}
|
{m.update_in_progress()}
|
||||||
</div>
|
</div>
|
||||||
<div className="text-sm leading-none">
|
<div className="text-sm leading-none">
|
||||||
<div className="flex items-center gap-x-1">
|
<div className="flex items-center gap-x-1">
|
||||||
<span className={cx("transition")}>
|
<span className={cx("transition")}>{m.updating_leave_device_on()}</span>
|
||||||
{m.updating_leave_device_on()}
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
import { CheckCircleIcon } from "@heroicons/react/24/solid"; // adjust import if you use a different icon set
|
import { CheckCircleIcon } from "@heroicons/react/24/solid"; // adjust import if you use a different icon set
|
||||||
|
|
||||||
import LoadingSpinner from "@components/LoadingSpinner"; // adjust import path if needed
|
import LoadingSpinner from "@components/LoadingSpinner"; // adjust import path if needed
|
||||||
|
|
@ -11,13 +10,7 @@ export interface UpdatePart {
|
||||||
complete: boolean;
|
complete: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function UpdatingStatusCard({
|
export default function UpdatingStatusCard({ label, part }: { label: string; part: UpdatePart }) {
|
||||||
label,
|
|
||||||
part,
|
|
||||||
}: {
|
|
||||||
label: string;
|
|
||||||
part: UpdatePart;
|
|
||||||
}) {
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
|
|
@ -34,7 +27,7 @@ export default function UpdatingStatusCard({
|
||||||
aria-valuemin={0}
|
aria-valuemin={0}
|
||||||
aria-valuemax={100}
|
aria-valuemax={100}
|
||||||
aria-valuenow={Math.round(part.progress)}
|
aria-valuenow={Math.round(part.progress)}
|
||||||
aria-label={m.general_update_status_progress({part: label})}
|
aria-label={m.general_update_status_progress({ part: label })}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className="h-2.5 rounded-full bg-blue-700 transition-all duration-500 ease-linear dark:bg-blue-500"
|
className="h-2.5 rounded-full bg-blue-700 transition-all duration-500 ease-linear dark:bg-blue-500"
|
||||||
|
|
|
||||||
|
|
@ -64,8 +64,7 @@ export function UsbDeviceSetting() {
|
||||||
const { send } = useJsonRpc();
|
const { send } = useJsonRpc();
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
|
|
||||||
const [usbDeviceConfig, setUsbDeviceConfig] =
|
const [usbDeviceConfig, setUsbDeviceConfig] = useState<UsbDeviceConfig>(defaultUsbDeviceConfig);
|
||||||
useState<UsbDeviceConfig>(defaultUsbDeviceConfig);
|
|
||||||
const [selectedPreset, setSelectedPreset] = useState<string>("default");
|
const [selectedPreset, setSelectedPreset] = useState<string>("default");
|
||||||
|
|
||||||
const syncUsbDeviceConfig = useCallback(() => {
|
const syncUsbDeviceConfig = useCallback(() => {
|
||||||
|
|
@ -134,9 +133,7 @@ export function UsbDeviceSetting() {
|
||||||
setSelectedPreset(newPreset);
|
setSelectedPreset(newPreset);
|
||||||
|
|
||||||
if (newPreset !== "custom") {
|
if (newPreset !== "custom") {
|
||||||
const presetConfig = usbPresets.find(
|
const presetConfig = usbPresets.find(preset => preset.value === newPreset)?.config;
|
||||||
preset => preset.value === newPreset,
|
|
||||||
)?.config;
|
|
||||||
|
|
||||||
if (presetConfig) {
|
if (presetConfig) {
|
||||||
handleUsbConfigChange(presetConfig);
|
handleUsbConfigChange(presetConfig);
|
||||||
|
|
@ -176,10 +173,13 @@ export function UsbDeviceSetting() {
|
||||||
</SettingsItem>
|
</SettingsItem>
|
||||||
|
|
||||||
{selectedPreset === "custom" && (
|
{selectedPreset === "custom" && (
|
||||||
<div className="ml-2 border-l border-slate-800/10 pl-4 dark:border-slate-300/20 ">
|
<div className="ml-2 border-l border-slate-800/10 pl-4 dark:border-slate-300/20">
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<SettingsItem title={m.usb_device_enable_keyboard_title()} description={m.usb_device_enable_keyboard_description()}>
|
<SettingsItem
|
||||||
|
title={m.usb_device_enable_keyboard_title()}
|
||||||
|
description={m.usb_device_enable_keyboard_description()}
|
||||||
|
>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
checked={usbDeviceConfig.keyboard}
|
checked={usbDeviceConfig.keyboard}
|
||||||
onChange={onUsbConfigItemChange("keyboard")}
|
onChange={onUsbConfigItemChange("keyboard")}
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,6 @@ export interface USBConfig {
|
||||||
product: string;
|
product: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const usbConfigs = [
|
const usbConfigs = [
|
||||||
{
|
{
|
||||||
label: m.usb_config_default(),
|
label: m.usb_config_default(),
|
||||||
|
|
@ -127,7 +126,10 @@ export function UsbInfoSetting() {
|
||||||
await sleep(2000);
|
await sleep(2000);
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
notifications.success(
|
notifications.success(
|
||||||
m.usb_config_set_success({ manufacturer: usbConfig.manufacturer, product: usbConfig.product }),
|
m.usb_config_set_success({
|
||||||
|
manufacturer: usbConfig.manufacturer,
|
||||||
|
product: usbConfig.product,
|
||||||
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
syncUsbConfigProduct();
|
syncUsbConfigProduct();
|
||||||
|
|
@ -174,13 +176,11 @@ export function UsbInfoSetting() {
|
||||||
/>
|
/>
|
||||||
</SettingsItem>
|
</SettingsItem>
|
||||||
{usbConfigProduct === "custom" && (
|
{usbConfigProduct === "custom" && (
|
||||||
<div className="ml-2 space-y-4 border-l border-slate-800/10 pl-4 dark:border-slate-300/20 ">
|
<div className="ml-2 space-y-4 border-l border-slate-800/10 pl-4 dark:border-slate-300/20">
|
||||||
<USBConfigDialog
|
<USBConfigDialog
|
||||||
loading={loading}
|
loading={loading}
|
||||||
onSetUsbConfig={usbConfig => handleUsbConfigChange(usbConfig)}
|
onSetUsbConfig={usbConfig => handleUsbConfigChange(usbConfig)}
|
||||||
onRestoreToDefault={() =>
|
onRestoreToDefault={() => handleUsbConfigChange(usbConfigData[usbConfigs[0].value])}
|
||||||
handleUsbConfigChange(usbConfigData[usbConfigs[0].value])
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,6 @@ import LogoWhite from "@/assets/logo-white.svg";
|
||||||
import { isOnDevice } from "@/main";
|
import { isOnDevice } from "@/main";
|
||||||
import { sleep } from "@/utils";
|
import { sleep } from "@/utils";
|
||||||
|
|
||||||
|
|
||||||
interface OverlayContentProps {
|
interface OverlayContentProps {
|
||||||
readonly children: React.ReactNode;
|
readonly children: React.ReactNode;
|
||||||
}
|
}
|
||||||
|
|
@ -86,9 +85,7 @@ export function LoadingConnectionOverlay({ show, text }: LoadingConnectionOverla
|
||||||
<div className="animate flex h-12 w-12 items-center justify-center">
|
<div className="animate flex h-12 w-12 items-center justify-center">
|
||||||
<LoadingSpinner className="h-8 w-8 text-blue-800 dark:text-blue-200" />
|
<LoadingSpinner className="h-8 w-8 text-blue-800 dark:text-blue-200" />
|
||||||
</div>
|
</div>
|
||||||
<p className="text-center text-sm text-slate-700 dark:text-slate-300">
|
<p className="text-center text-sm text-slate-700 dark:text-slate-300">{text}</p>
|
||||||
{text}
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
</OverlayContent>
|
</OverlayContent>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
|
@ -125,7 +122,9 @@ export function ConnectionFailedOverlay({
|
||||||
<div className="text-left text-sm text-slate-700 dark:text-slate-300">
|
<div className="text-left text-sm text-slate-700 dark:text-slate-300">
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<div className="space-y-2 text-black dark:text-white">
|
<div className="space-y-2 text-black dark:text-white">
|
||||||
<h2 className="text-xl font-bold">{m.video_overlay_connection_issue_title()}</h2>
|
<h2 className="text-xl font-bold">
|
||||||
|
{m.video_overlay_connection_issue_title()}
|
||||||
|
</h2>
|
||||||
<ul className="list-disc space-y-2 pl-4 text-left">
|
<ul className="list-disc space-y-2 pl-4 text-left">
|
||||||
<li>{m.video_overlay_conn_verify_power()}</li>
|
<li>{m.video_overlay_conn_verify_power()}</li>
|
||||||
<li>{m.video_overlay_conn_check_cables()}</li>
|
<li>{m.video_overlay_conn_check_cables()}</li>
|
||||||
|
|
@ -163,9 +162,7 @@ interface PeerConnectionDisconnectedOverlay {
|
||||||
readonly show: boolean;
|
readonly show: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function PeerConnectionDisconnectedOverlay({
|
export function PeerConnectionDisconnectedOverlay({ show }: PeerConnectionDisconnectedOverlay) {
|
||||||
show,
|
|
||||||
}: PeerConnectionDisconnectedOverlay) {
|
|
||||||
return (
|
return (
|
||||||
<AnimatePresence>
|
<AnimatePresence>
|
||||||
{show && (
|
{show && (
|
||||||
|
|
@ -185,7 +182,9 @@ export function PeerConnectionDisconnectedOverlay({
|
||||||
<div className="text-left text-sm text-slate-700 dark:text-slate-300">
|
<div className="text-left text-sm text-slate-700 dark:text-slate-300">
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<div className="space-y-2 text-black dark:text-white">
|
<div className="space-y-2 text-black dark:text-white">
|
||||||
<h2 className="text-xl font-bold">{m.video_overlay_connection_issue_title()}</h2>
|
<h2 className="text-xl font-bold">
|
||||||
|
{m.video_overlay_connection_issue_title()}
|
||||||
|
</h2>
|
||||||
<ul className="list-disc space-y-2 pl-4 text-left">
|
<ul className="list-disc space-y-2 pl-4 text-left">
|
||||||
<li>{m.video_overlay_conn_verify_power()}</li>
|
<li>{m.video_overlay_conn_verify_power()}</li>
|
||||||
<li>{m.video_overlay_conn_check_cables()}</li>
|
<li>{m.video_overlay_conn_check_cables()}</li>
|
||||||
|
|
@ -405,7 +404,7 @@ export function RebootingOverlay({ show, postRebootAction }: RebootingOverlayPro
|
||||||
|
|
||||||
// Check if we've already seen the connection drop (confirms reboot actually started)
|
// Check if we've already seen the connection drop (confirms reboot actually started)
|
||||||
const [hasSeenDisconnect, setHasSeenDisconnect] = useState(
|
const [hasSeenDisconnect, setHasSeenDisconnect] = useState(
|
||||||
['disconnected', 'closed', 'failed'].includes(peerConnectionState ?? '')
|
["disconnected", "closed", "failed"].includes(peerConnectionState ?? ""),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Track if we've timed out
|
// Track if we've timed out
|
||||||
|
|
@ -416,8 +415,8 @@ export function RebootingOverlay({ show, postRebootAction }: RebootingOverlayPro
|
||||||
if (!show) return;
|
if (!show) return;
|
||||||
if (hasSeenDisconnect) return;
|
if (hasSeenDisconnect) return;
|
||||||
|
|
||||||
if (['disconnected', 'closed', 'failed'].includes(peerConnectionState ?? '')) {
|
if (["disconnected", "closed", "failed"].includes(peerConnectionState ?? "")) {
|
||||||
console.log('hasSeenDisconnect', hasSeenDisconnect);
|
console.log("hasSeenDisconnect", hasSeenDisconnect);
|
||||||
setHasSeenDisconnect(true);
|
setHasSeenDisconnect(true);
|
||||||
}
|
}
|
||||||
}, [show, peerConnectionState, hasSeenDisconnect]);
|
}, [show, peerConnectionState, hasSeenDisconnect]);
|
||||||
|
|
@ -438,7 +437,6 @@ export function RebootingOverlay({ show, postRebootAction }: RebootingOverlayPro
|
||||||
};
|
};
|
||||||
}, [show]);
|
}, [show]);
|
||||||
|
|
||||||
|
|
||||||
// Poll suggested IP in device mode to detect when it's available
|
// Poll suggested IP in device mode to detect when it's available
|
||||||
const abortControllerRef = useRef<AbortController | null>(null);
|
const abortControllerRef = useRef<AbortController | null>(null);
|
||||||
const isFetchingRef = useRef(false);
|
const isFetchingRef = useRef(false);
|
||||||
|
|
@ -465,17 +463,16 @@ export function RebootingOverlay({ show, postRebootAction }: RebootingOverlayPro
|
||||||
abortControllerRef.current = abortController;
|
abortControllerRef.current = abortController;
|
||||||
isFetchingRef.current = true;
|
isFetchingRef.current = true;
|
||||||
|
|
||||||
console.log('Checking post-reboot health endpoint:', postRebootAction.healthCheck);
|
console.log("Checking post-reboot health endpoint:", postRebootAction.healthCheck);
|
||||||
const timeoutId = window.setTimeout(() => abortController.abort(), 2000);
|
const timeoutId = window.setTimeout(() => abortController.abort(), 2000);
|
||||||
try {
|
try {
|
||||||
const response = await fetch(
|
const response = await fetch(postRebootAction.healthCheck, {
|
||||||
postRebootAction.healthCheck,
|
signal: abortController.signal,
|
||||||
{ signal: abortController.signal, }
|
});
|
||||||
);
|
|
||||||
|
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
// Device is available, redirect to the specified URL
|
// Device is available, redirect to the specified URL
|
||||||
console.log('Device is available, redirecting to:', postRebootAction.redirectTo);
|
console.log("Device is available, redirecting to:", postRebootAction.redirectTo);
|
||||||
|
|
||||||
// URL constructor handles all cases elegantly:
|
// URL constructor handles all cases elegantly:
|
||||||
// - Absolute paths: resolved against current origin
|
// - Absolute paths: resolved against current origin
|
||||||
|
|
@ -492,8 +489,8 @@ export function RebootingOverlay({ show, postRebootAction }: RebootingOverlayPro
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// Ignore errors - they're expected while device is rebooting
|
// Ignore errors - they're expected while device is rebooting
|
||||||
// Only log if it's not an abort error
|
// Only log if it's not an abort error
|
||||||
if (err instanceof Error && err.name !== 'AbortError') {
|
if (err instanceof Error && err.name !== "AbortError") {
|
||||||
console.debug('Error checking post-reboot health:', err);
|
console.debug("Error checking post-reboot health:", err);
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
clearTimeout(timeoutId);
|
clearTimeout(timeoutId);
|
||||||
|
|
@ -531,8 +528,7 @@ export function RebootingOverlay({ show, postRebootAction }: RebootingOverlayPro
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<OverlayContent>
|
<OverlayContent>
|
||||||
|
<div className="flex w-full max-w-md flex-col items-start gap-y-4">
|
||||||
<div className="flex flex-col items-start gap-y-4 w-full max-w-md">
|
|
||||||
<div className="h-[24px]">
|
<div className="h-[24px]">
|
||||||
<img src={LogoBlue} alt="" className="h-full dark:hidden" />
|
<img src={LogoBlue} alt="" className="h-full dark:hidden" />
|
||||||
<img src={LogoWhite} alt="" className="hidden h-full dark:block" />
|
<img src={LogoWhite} alt="" className="hidden h-full dark:block" />
|
||||||
|
|
@ -540,17 +536,16 @@ export function RebootingOverlay({ show, postRebootAction }: RebootingOverlayPro
|
||||||
<div className="text-left text-sm text-slate-700 dark:text-slate-300">
|
<div className="text-left text-sm text-slate-700 dark:text-slate-300">
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<div className="space-y-2 text-black dark:text-white">
|
<div className="space-y-2 text-black dark:text-white">
|
||||||
<h2 className="text-xl font-bold">{hasTimedOut ? m.video_overlay_reboot_unable_to_reconnect() : m.video_overlay_reboot_device_is_rebooting()}</h2>
|
<h2 className="text-xl font-bold">
|
||||||
|
{hasTimedOut
|
||||||
|
? m.video_overlay_reboot_unable_to_reconnect()
|
||||||
|
: m.video_overlay_reboot_device_is_rebooting()}
|
||||||
|
</h2>
|
||||||
<p className="text-sm text-slate-700 dark:text-slate-300">
|
<p className="text-sm text-slate-700 dark:text-slate-300">
|
||||||
{hasTimedOut ? (
|
{hasTimedOut ? (
|
||||||
<>
|
<>{m.video_overlay_reboot_different_ip_message()}</>
|
||||||
{m.video_overlay_reboot_different_ip_message()}
|
|
||||||
</>
|
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>{m.video_overlay_reboot_please_wait_message()}</>
|
||||||
{m.video_overlay_reboot_please_wait_message()}
|
|
||||||
|
|
||||||
</>
|
|
||||||
)}
|
)}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -21,8 +21,7 @@ export const DetachIcon = ({ className }: { className?: string }) => {
|
||||||
|
|
||||||
function KeyboardWrapper() {
|
function KeyboardWrapper() {
|
||||||
const keyboardRef = useRef<HTMLDivElement>(null);
|
const keyboardRef = useRef<HTMLDivElement>(null);
|
||||||
const { isAttachedVirtualKeyboardVisible, setAttachedVirtualKeyboardVisibility } =
|
const { isAttachedVirtualKeyboardVisible, setAttachedVirtualKeyboardVisibility } = useUiStore();
|
||||||
useUiStore();
|
|
||||||
const { keyboardLedState, keysDownState, isVirtualKeyboardEnabled, setVirtualKeyboardEnabled } =
|
const { keyboardLedState, keysDownState, isVirtualKeyboardEnabled, setVirtualKeyboardEnabled } =
|
||||||
useHidStore();
|
useHidStore();
|
||||||
const { handleKeyPress, executeMacro } = useKeyboard();
|
const { handleKeyPress, executeMacro } = useKeyboard();
|
||||||
|
|
@ -159,9 +158,7 @@ function KeyboardWrapper() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (key === "AltMetaEscape") {
|
if (key === "AltMetaEscape") {
|
||||||
await executeMacro([
|
await executeMacro([{ keys: ["Escape"], modifiers: ["AltLeft", "MetaLeft"], delay: 100 }]);
|
||||||
{ keys: ["Escape"], modifiers: ["AltLeft", "MetaLeft"], delay: 100 },
|
|
||||||
]);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -258,7 +255,7 @@ function KeyboardWrapper() {
|
||||||
{m.virtual_keyboard_header()}
|
{m.virtual_keyboard_header()}
|
||||||
</h2>
|
</h2>
|
||||||
<div className="absolute right-2 flex items-center gap-x-2">
|
<div className="absolute right-2 flex items-center gap-x-2">
|
||||||
<div className="hidden md:flex gap-x-2 items-center">
|
<div className="hidden items-center gap-x-2 md:flex">
|
||||||
<LinkButton
|
<LinkButton
|
||||||
size="XS"
|
size="XS"
|
||||||
to="settings/keyboard"
|
to="settings/keyboard"
|
||||||
|
|
|
||||||
|
|
@ -4,11 +4,7 @@ import { useResizeObserver } from "usehooks-ts";
|
||||||
import { cx } from "@/cva.config";
|
import { cx } from "@/cva.config";
|
||||||
import useKeyboard from "@hooks/useKeyboard";
|
import useKeyboard from "@hooks/useKeyboard";
|
||||||
import useMouse from "@hooks/useMouse";
|
import useMouse from "@hooks/useMouse";
|
||||||
import {
|
import { useRTCStore, useSettingsStore, useVideoStore } from "@hooks/stores";
|
||||||
useRTCStore,
|
|
||||||
useSettingsStore,
|
|
||||||
useVideoStore,
|
|
||||||
} from "@hooks/stores";
|
|
||||||
import VirtualKeyboard from "@components/VirtualKeyboard";
|
import VirtualKeyboard from "@components/VirtualKeyboard";
|
||||||
import Actionbar from "@components/ActionBar";
|
import Actionbar from "@components/ActionBar";
|
||||||
import MacroBar from "@components/MacroBar";
|
import MacroBar from "@components/MacroBar";
|
||||||
|
|
@ -32,7 +28,8 @@ export default function WebRTCVideo({ hasConnectionIssues }: { hasConnectionIssu
|
||||||
const [isPointerLockActive, setIsPointerLockActive] = useState(false);
|
const [isPointerLockActive, setIsPointerLockActive] = useState(false);
|
||||||
const [isKeyboardLockActive, setIsKeyboardLockActive] = useState(false);
|
const [isKeyboardLockActive, setIsKeyboardLockActive] = useState(false);
|
||||||
|
|
||||||
const isPointerLockPossible = window.location.protocol === "https:" || window.location.hostname === "localhost";
|
const isPointerLockPossible =
|
||||||
|
window.location.protocol === "https:" || window.location.hostname === "localhost";
|
||||||
|
|
||||||
// Store hooks
|
// Store hooks
|
||||||
const settings = useSettingsStore();
|
const settings = useSettingsStore();
|
||||||
|
|
@ -71,7 +68,7 @@ export default function WebRTCVideo({ hasConnectionIssues }: { hasConnectionIssu
|
||||||
setVideoClientSize(width || 0, height || 0);
|
setVideoClientSize(width || 0, height || 0);
|
||||||
setVideoSize(videoElm.current.videoWidth, videoElm.current.videoHeight);
|
setVideoSize(videoElm.current.videoWidth, videoElm.current.videoHeight);
|
||||||
},
|
},
|
||||||
[setVideoClientSize, setVideoSize]
|
[setVideoClientSize, setVideoSize],
|
||||||
);
|
);
|
||||||
|
|
||||||
useResizeObserver({
|
useResizeObserver({
|
||||||
|
|
@ -119,9 +116,7 @@ export default function WebRTCVideo({ hasConnectionIssues }: { hasConnectionIssu
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const requestPointerLock = useCallback(async () => {
|
const requestPointerLock = useCallback(async () => {
|
||||||
if (!isPointerLockPossible
|
if (!isPointerLockPossible || videoElm.current === null || document.pointerLockElement) return;
|
||||||
|| videoElm.current === null
|
|
||||||
|| document.pointerLockElement) return;
|
|
||||||
|
|
||||||
const isPointerLockGranted = await checkNavigatorPermissions("pointer-lock");
|
const isPointerLockGranted = await checkNavigatorPermissions("pointer-lock");
|
||||||
|
|
||||||
|
|
@ -151,7 +146,11 @@ export default function WebRTCVideo({ hasConnectionIssues }: { hasConnectionIssu
|
||||||
}, [checkNavigatorPermissions, setIsKeyboardLockActive]);
|
}, [checkNavigatorPermissions, setIsKeyboardLockActive]);
|
||||||
|
|
||||||
const releaseKeyboardLock = useCallback(async () => {
|
const releaseKeyboardLock = useCallback(async () => {
|
||||||
if (fullscreenContainerRef.current === null || document.fullscreenElement !== fullscreenContainerRef.current) return;
|
if (
|
||||||
|
fullscreenContainerRef.current === null ||
|
||||||
|
document.fullscreenElement !== fullscreenContainerRef.current
|
||||||
|
)
|
||||||
|
return;
|
||||||
|
|
||||||
if (navigator && "keyboard" in navigator) {
|
if (navigator && "keyboard" in navigator) {
|
||||||
try {
|
try {
|
||||||
|
|
@ -216,24 +215,19 @@ export default function WebRTCVideo({ hasConnectionIssues }: { hasConnectionIssu
|
||||||
}, [releaseKeyboardLock]);
|
}, [releaseKeyboardLock]);
|
||||||
|
|
||||||
const absMouseMoveHandler = useMemo(
|
const absMouseMoveHandler = useMemo(
|
||||||
() => getAbsMouseMoveHandler({
|
() =>
|
||||||
videoClientWidth,
|
getAbsMouseMoveHandler({
|
||||||
videoClientHeight,
|
videoClientWidth,
|
||||||
videoWidth,
|
videoClientHeight,
|
||||||
videoHeight,
|
videoWidth,
|
||||||
}),
|
videoHeight,
|
||||||
|
}),
|
||||||
[getAbsMouseMoveHandler, videoClientWidth, videoClientHeight, videoWidth, videoHeight],
|
[getAbsMouseMoveHandler, videoClientWidth, videoClientHeight, videoWidth, videoHeight],
|
||||||
);
|
);
|
||||||
|
|
||||||
const relMouseMoveHandler = useMemo(
|
const relMouseMoveHandler = useMemo(() => getRelMouseMoveHandler(), [getRelMouseMoveHandler]);
|
||||||
() => getRelMouseMoveHandler(),
|
|
||||||
[getRelMouseMoveHandler],
|
|
||||||
);
|
|
||||||
|
|
||||||
const mouseWheelHandler = useMemo(
|
const mouseWheelHandler = useMemo(() => getMouseWheelHandler(), [getMouseWheelHandler]);
|
||||||
() => getMouseWheelHandler(),
|
|
||||||
[getMouseWheelHandler],
|
|
||||||
);
|
|
||||||
|
|
||||||
function getAdjustedKeyCode(e: KeyboardEvent) {
|
function getAdjustedKeyCode(e: KeyboardEvent) {
|
||||||
const key = e.key;
|
const key = e.key;
|
||||||
|
|
@ -263,7 +257,7 @@ export default function WebRTCVideo({ hasConnectionIssues }: { hasConnectionIssu
|
||||||
// event, so we need to clear the keys after a short delay
|
// event, so we need to clear the keys after a short delay
|
||||||
// https://bugs.chromium.org/p/chromium/issues/detail?id=28089
|
// https://bugs.chromium.org/p/chromium/issues/detail?id=28089
|
||||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=1299553
|
// https://bugzilla.mozilla.org/show_bug.cgi?id=1299553
|
||||||
if (e.metaKey && hidKey < 0xE0) {
|
if (e.metaKey && hidKey < 0xe0) {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
console.debug(`Forcing the meta key release of associated key: ${hidKey}`);
|
console.debug(`Forcing the meta key release of associated key: ${hidKey}`);
|
||||||
handleKeyPress(hidKey, false);
|
handleKeyPress(hidKey, false);
|
||||||
|
|
@ -403,7 +397,7 @@ export default function WebRTCVideo({ hasConnectionIssues }: { hasConnectionIssu
|
||||||
const videoElmRefValue = videoElm.current;
|
const videoElmRefValue = videoElm.current;
|
||||||
if (!videoElmRefValue) return;
|
if (!videoElmRefValue) return;
|
||||||
|
|
||||||
const isRelativeMouseMode = (settings.mouseMode === "relative");
|
const isRelativeMouseMode = settings.mouseMode === "relative";
|
||||||
const mouseHandler = isRelativeMouseMode ? relMouseMoveHandler : absMouseMoveHandler;
|
const mouseHandler = isRelativeMouseMode ? relMouseMoveHandler : absMouseMoveHandler;
|
||||||
|
|
||||||
const abortController = new AbortController();
|
const abortController = new AbortController();
|
||||||
|
|
@ -418,7 +412,8 @@ export default function WebRTCVideo({ hasConnectionIssues }: { hasConnectionIssu
|
||||||
});
|
});
|
||||||
|
|
||||||
if (isRelativeMouseMode) {
|
if (isRelativeMouseMode) {
|
||||||
videoElmRefValue.addEventListener("click",
|
videoElmRefValue.addEventListener(
|
||||||
|
"click",
|
||||||
() => {
|
() => {
|
||||||
if (isPointerLockPossible && !isPointerLockActive && !document.pointerLockElement) {
|
if (isPointerLockPossible && !isPointerLockActive && !document.pointerLockElement) {
|
||||||
requestPointerLock();
|
requestPointerLock();
|
||||||
|
|
@ -469,7 +464,15 @@ export default function WebRTCVideo({ hasConnectionIssues }: { hasConnectionIssu
|
||||||
if (!isPlaying) return false;
|
if (!isPlaying) return false;
|
||||||
if (videoHeight === 0 || videoWidth === 0) return false;
|
if (videoHeight === 0 || videoWidth === 0) return false;
|
||||||
return true;
|
return true;
|
||||||
}, [isPlaying, isPointerLockActive, isPointerLockPossible, isVideoLoading, settings.mouseMode, videoHeight, videoWidth]);
|
}, [
|
||||||
|
isPlaying,
|
||||||
|
isPointerLockActive,
|
||||||
|
isPointerLockPossible,
|
||||||
|
isVideoLoading,
|
||||||
|
settings.mouseMode,
|
||||||
|
videoHeight,
|
||||||
|
videoWidth,
|
||||||
|
]);
|
||||||
|
|
||||||
// Conditionally set the filter style so we don't fallback to software rendering if these values are default of 1.0
|
// Conditionally set the filter style so we don't fallback to software rendering if these values are default of 1.0
|
||||||
const videoStyle = useMemo(() => {
|
const videoStyle = useMemo(() => {
|
||||||
|
|
@ -477,19 +480,15 @@ export default function WebRTCVideo({ hasConnectionIssues }: { hasConnectionIssu
|
||||||
return isDefault
|
return isDefault
|
||||||
? {} // No filter if all settings are default (1.0)
|
? {} // No filter if all settings are default (1.0)
|
||||||
: {
|
: {
|
||||||
filter: `saturate(${videoSaturation}) brightness(${videoBrightness}) contrast(${videoContrast})`,
|
filter: `saturate(${videoSaturation}) brightness(${videoBrightness}) contrast(${videoContrast})`,
|
||||||
};
|
};
|
||||||
}, [videoSaturation, videoBrightness, videoContrast]);
|
}, [videoSaturation, videoBrightness, videoContrast]);
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="grid h-full w-full grid-rows-(--grid-layout)">
|
<div className="grid h-full w-full grid-rows-(--grid-layout)">
|
||||||
<div className="flex min-h-[39.5px] flex-col">
|
<div className="flex min-h-[39.5px] flex-col">
|
||||||
<div className="flex flex-col">
|
<div className="flex flex-col">
|
||||||
<fieldset
|
<fieldset disabled={peerConnection?.connectionState !== "connected"} className="contents">
|
||||||
disabled={peerConnection?.connectionState !== "connected"}
|
|
||||||
className="contents"
|
|
||||||
>
|
|
||||||
<Actionbar requestFullscreen={requestFullscreen} />
|
<Actionbar requestFullscreen={requestFullscreen} />
|
||||||
<MacroBar />
|
<MacroBar />
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
@ -546,7 +545,7 @@ export default function WebRTCVideo({ hasConnectionIssues }: { hasConnectionIssu
|
||||||
{peerConnection?.connectionState == "connected" && !hasConnectionIssues && (
|
{peerConnection?.connectionState == "connected" && !hasConnectionIssues && (
|
||||||
<div
|
<div
|
||||||
style={{ animationDuration: "500ms" }}
|
style={{ animationDuration: "500ms" }}
|
||||||
className="animate-slideUpFade pointer-events-none absolute inset-0 flex items-center justify-center"
|
className="pointer-events-none absolute inset-0 flex animate-slideUpFade items-center justify-center"
|
||||||
>
|
>
|
||||||
<div className="relative h-full w-full rounded-md">
|
<div className="relative h-full w-full rounded-md">
|
||||||
<LoadingVideoOverlay show={isVideoLoading} />
|
<LoadingVideoOverlay show={isVideoLoading} />
|
||||||
|
|
|
||||||
|
|
@ -18,12 +18,12 @@ interface ATXState {
|
||||||
|
|
||||||
export function ATXPowerControl() {
|
export function ATXPowerControl() {
|
||||||
const [isPowerPressed, setIsPowerPressed] = useState(false);
|
const [isPowerPressed, setIsPowerPressed] = useState(false);
|
||||||
const [powerPressTimer, setPowerPressTimer] = useState<ReturnType<
|
const [powerPressTimer, setPowerPressTimer] = useState<ReturnType<typeof setTimeout> | null>(
|
||||||
typeof setTimeout
|
null,
|
||||||
> | null>(null);
|
);
|
||||||
const [atxState, setAtxState] = useState<ATXState | null>(null);
|
const [atxState, setAtxState] = useState<ATXState | null>(null);
|
||||||
|
|
||||||
const { send } = useJsonRpc(function onRequest(resp) {
|
const { send } = useJsonRpc(function onRequest(resp) {
|
||||||
if (resp.method === "atxState") {
|
if (resp.method === "atxState") {
|
||||||
setAtxState(resp.params as ATXState);
|
setAtxState(resp.params as ATXState);
|
||||||
}
|
}
|
||||||
|
|
@ -33,7 +33,9 @@ export function ATXPowerControl() {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
send("getATXState", {}, (resp: JsonRpcResponse) => {
|
send("getATXState", {}, (resp: JsonRpcResponse) => {
|
||||||
if ("error" in resp) {
|
if ("error" in resp) {
|
||||||
notifications.error(m.atx_power_control_get_state_error({ error: resp.error.data || m.unknown_error() }));
|
notifications.error(
|
||||||
|
m.atx_power_control_get_state_error({ error: resp.error.data || m.unknown_error() }),
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
setAtxState(resp.result as ATXState);
|
setAtxState(resp.result as ATXState);
|
||||||
|
|
@ -54,7 +56,12 @@ export function ATXPowerControl() {
|
||||||
console.log("Sending long press ATX power action");
|
console.log("Sending long press ATX power action");
|
||||||
send("setATXPowerAction", { action: "power-long" }, (resp: JsonRpcResponse) => {
|
send("setATXPowerAction", { action: "power-long" }, (resp: JsonRpcResponse) => {
|
||||||
if ("error" in resp) {
|
if ("error" in resp) {
|
||||||
notifications.error(m.atx_power_control_send_action_error({ action: m.atx_power_control_long_power_button(), error: resp.error.data || m.unknown_error() }));
|
notifications.error(
|
||||||
|
m.atx_power_control_send_action_error({
|
||||||
|
action: m.atx_power_control_long_power_button(),
|
||||||
|
error: resp.error.data || m.unknown_error(),
|
||||||
|
}),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
setIsPowerPressed(false);
|
setIsPowerPressed(false);
|
||||||
});
|
});
|
||||||
|
|
@ -73,7 +80,12 @@ export function ATXPowerControl() {
|
||||||
console.log("Sending short press ATX power action");
|
console.log("Sending short press ATX power action");
|
||||||
send("setATXPowerAction", { action: "power-short" }, (resp: JsonRpcResponse) => {
|
send("setATXPowerAction", { action: "power-short" }, (resp: JsonRpcResponse) => {
|
||||||
if ("error" in resp) {
|
if ("error" in resp) {
|
||||||
notifications.error(m.atx_power_control_send_action_error({ action: m.atx_power_control_short_power_button(), error: resp.error.data || m.unknown_error() }));
|
notifications.error(
|
||||||
|
m.atx_power_control_send_action_error({
|
||||||
|
action: m.atx_power_control_short_power_button(),
|
||||||
|
error: resp.error.data || m.unknown_error(),
|
||||||
|
}),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -123,7 +135,12 @@ export function ATXPowerControl() {
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
send("setATXPowerAction", { action: "reset" }, (resp: JsonRpcResponse) => {
|
send("setATXPowerAction", { action: "reset" }, (resp: JsonRpcResponse) => {
|
||||||
if ("error" in resp) {
|
if ("error" in resp) {
|
||||||
notifications.error(m.atx_power_control_send_action_error({ action: m.atx_power_control_reset_button(), error: resp.error.data || m.unknown_error() }));
|
notifications.error(
|
||||||
|
m.atx_power_control_send_action_error({
|
||||||
|
action: m.atx_power_control_reset_button(),
|
||||||
|
error: resp.error.data || m.unknown_error(),
|
||||||
|
}),
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
@ -149,9 +166,7 @@ export function ATXPowerControl() {
|
||||||
<span className="text-sm text-slate-600 dark:text-slate-400">
|
<span className="text-sm text-slate-600 dark:text-slate-400">
|
||||||
<LuHardDrive
|
<LuHardDrive
|
||||||
strokeWidth={3}
|
strokeWidth={3}
|
||||||
className={`mr-1 inline ${
|
className={`mr-1 inline ${atxState?.hdd ? "text-blue-400" : "text-slate-300"}`}
|
||||||
atxState?.hdd ? "text-blue-400" : "text-slate-300"
|
|
||||||
}`}
|
|
||||||
/>
|
/>
|
||||||
{m.atx_power_control_hdd_led()}
|
{m.atx_power_control_hdd_led()}
|
||||||
</span>
|
</span>
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ import Card from "@components/Card";
|
||||||
import { SettingsPageHeader } from "@components/SettingsPageheader";
|
import { SettingsPageHeader } from "@components/SettingsPageheader";
|
||||||
import FieldLabel from "@components/FieldLabel";
|
import FieldLabel from "@components/FieldLabel";
|
||||||
import LoadingSpinner from "@components/LoadingSpinner";
|
import LoadingSpinner from "@components/LoadingSpinner";
|
||||||
import {SelectMenuBasic} from "@components/SelectMenuBasic";
|
import { SelectMenuBasic } from "@components/SelectMenuBasic";
|
||||||
import notifications from "@/notifications";
|
import notifications from "@/notifications";
|
||||||
|
|
||||||
interface DCPowerState {
|
interface DCPowerState {
|
||||||
|
|
@ -26,7 +26,9 @@ export function DCPowerControl() {
|
||||||
const getDCPowerState = useCallback(() => {
|
const getDCPowerState = useCallback(() => {
|
||||||
send("getDCPowerState", {}, (resp: JsonRpcResponse) => {
|
send("getDCPowerState", {}, (resp: JsonRpcResponse) => {
|
||||||
if ("error" in resp) {
|
if ("error" in resp) {
|
||||||
notifications.error(m.dc_power_control_get_state_error({ error: resp.error.data || m.unknown_error() }));
|
notifications.error(
|
||||||
|
m.dc_power_control_get_state_error({ error: resp.error.data || m.unknown_error() }),
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
setPowerState(resp.result as DCPowerState);
|
setPowerState(resp.result as DCPowerState);
|
||||||
|
|
@ -36,7 +38,12 @@ export function DCPowerControl() {
|
||||||
const handlePowerToggle = (enabled: boolean) => {
|
const handlePowerToggle = (enabled: boolean) => {
|
||||||
send("setDCPowerState", { enabled }, (resp: JsonRpcResponse) => {
|
send("setDCPowerState", { enabled }, (resp: JsonRpcResponse) => {
|
||||||
if ("error" in resp) {
|
if ("error" in resp) {
|
||||||
notifications.error(m.dc_power_control_set_power_state_error({ enabled: enabled, error: resp.error.data || m.unknown_error() }));
|
notifications.error(
|
||||||
|
m.dc_power_control_set_power_state_error({
|
||||||
|
enabled: enabled,
|
||||||
|
error: resp.error.data || m.unknown_error(),
|
||||||
|
}),
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
getDCPowerState(); // Refresh state after change
|
getDCPowerState(); // Refresh state after change
|
||||||
|
|
@ -46,7 +53,12 @@ export function DCPowerControl() {
|
||||||
// const state = powerState?.restoreState === 0 ? 1 : powerState?.restoreState === 1 ? 2 : 0;
|
// const state = powerState?.restoreState === 0 ? 1 : powerState?.restoreState === 1 ? 2 : 0;
|
||||||
send("setDCRestoreState", { state }, (resp: JsonRpcResponse) => {
|
send("setDCRestoreState", { state }, (resp: JsonRpcResponse) => {
|
||||||
if ("error" in resp) {
|
if ("error" in resp) {
|
||||||
notifications.error(m.dc_power_control_set_restore_state_error({ state: state, error: resp.error.data || m.unknown_error() }));
|
notifications.error(
|
||||||
|
m.dc_power_control_set_restore_state_error({
|
||||||
|
state: state,
|
||||||
|
error: resp.error.data || m.unknown_error(),
|
||||||
|
}),
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
getDCPowerState(); // Refresh state after change
|
getDCPowerState(); // Refresh state after change
|
||||||
|
|
@ -96,15 +108,15 @@ export function DCPowerControl() {
|
||||||
{powerState.restoreState > -1 ? (
|
{powerState.restoreState > -1 ? (
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<SelectMenuBasic
|
<SelectMenuBasic
|
||||||
size="SM"
|
size="SM"
|
||||||
label={m.dc_power_control_restore_power_state()}
|
label={m.dc_power_control_restore_power_state()}
|
||||||
value={powerState.restoreState}
|
value={powerState.restoreState}
|
||||||
onChange={e => handleRestoreChange(parseInt(e.target.value))}
|
onChange={e => handleRestoreChange(parseInt(e.target.value))}
|
||||||
options={[
|
options={[
|
||||||
{ value: '0', label: m.dc_power_control_power_off_state()},
|
{ value: "0", label: m.dc_power_control_power_off_state() },
|
||||||
{ value: '1', label: m.dc_power_control_power_on_state()},
|
{ value: "1", label: m.dc_power_control_power_on_state() },
|
||||||
{ value: '2', label: m.dc_power_control_restore_last_state()},
|
{ value: "2", label: m.dc_power_control_restore_last_state() },
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
@ -125,7 +137,7 @@ export function DCPowerControl() {
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
<FieldLabel label={m.dc_power_control_power()}/>
|
<FieldLabel label={m.dc_power_control_power()} />
|
||||||
<p className="text-sm font-medium text-slate-900 dark:text-slate-100">
|
<p className="text-sm font-medium text-slate-900 dark:text-slate-100">
|
||||||
{powerState.power.toFixed(1)} {m.dc_power_control_power_unit()}
|
{powerState.power.toFixed(1)} {m.dc_power_control_power_unit()}
|
||||||
</p>
|
</p>
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,9 @@ export function SerialConsole() {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
send("getSerialSettings", {}, (resp: JsonRpcResponse) => {
|
send("getSerialSettings", {}, (resp: JsonRpcResponse) => {
|
||||||
if ("error" in resp) {
|
if ("error" in resp) {
|
||||||
notifications.error(m.serial_console_get_settings_error({ error: resp.error.data || m.unknown_error() }));
|
notifications.error(
|
||||||
|
m.serial_console_get_settings_error({ error: resp.error.data || m.unknown_error() }),
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
setSettings(resp.result as SerialSettings);
|
setSettings(resp.result as SerialSettings);
|
||||||
|
|
@ -40,7 +42,12 @@ export function SerialConsole() {
|
||||||
const newSettings = { ...settings, [setting]: value };
|
const newSettings = { ...settings, [setting]: value };
|
||||||
send("setSerialSettings", { settings: newSettings }, (resp: JsonRpcResponse) => {
|
send("setSerialSettings", { settings: newSettings }, (resp: JsonRpcResponse) => {
|
||||||
if ("error" in resp) {
|
if ("error" in resp) {
|
||||||
notifications.error(m.serial_console_set_settings_error({ settings: setting, error: resp.error.data || m.unknown_error() }));
|
notifications.error(
|
||||||
|
m.serial_console_set_settings_error({
|
||||||
|
settings: setting,
|
||||||
|
error: resp.error.data || m.unknown_error(),
|
||||||
|
}),
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
setSettings(newSettings);
|
setSettings(newSettings);
|
||||||
|
|
|
||||||
|
|
@ -61,7 +61,9 @@ export default function ExtensionPopover() {
|
||||||
send("setActiveExtension", { extensionId: extension?.id || "" }, (resp: JsonRpcResponse) => {
|
send("setActiveExtension", { extensionId: extension?.id || "" }, (resp: JsonRpcResponse) => {
|
||||||
if ("error" in resp) {
|
if ("error" in resp) {
|
||||||
notifications.error(
|
notifications.error(
|
||||||
m.extension_popover_set_error_notification({ error: resp.error.data || m.unknown_error() }),
|
m.extension_popover_set_error_notification({
|
||||||
|
error: resp.error.data || m.unknown_error(),
|
||||||
|
}),
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -93,7 +95,7 @@ export default function ExtensionPopover() {
|
||||||
{renderActiveExtension()}
|
{renderActiveExtension()}
|
||||||
|
|
||||||
<div
|
<div
|
||||||
className="flex animate-fadeIn opacity-0 items-center justify-end space-x-2"
|
className="flex animate-fadeIn items-center justify-end space-x-2 opacity-0"
|
||||||
style={{
|
style={{
|
||||||
animationDuration: "0.7s",
|
animationDuration: "0.7s",
|
||||||
animationDelay: "0.2s",
|
animationDelay: "0.2s",
|
||||||
|
|
@ -114,15 +116,12 @@ export default function ExtensionPopover() {
|
||||||
title={m.extensions_popover_extensions()}
|
title={m.extensions_popover_extensions()}
|
||||||
description={m.extension_popover_load_and_manage_extensions()}
|
description={m.extension_popover_load_and_manage_extensions()}
|
||||||
/>
|
/>
|
||||||
<Card className="animate-fadeIn opacity-0" >
|
<Card className="animate-fadeIn opacity-0">
|
||||||
<div className="w-full divide-y divide-slate-700/30 dark:divide-slate-600/30">
|
<div className="w-full divide-y divide-slate-700/30 dark:divide-slate-600/30">
|
||||||
{AVAILABLE_EXTENSIONS.map(extension => (
|
{AVAILABLE_EXTENSIONS.map(extension => (
|
||||||
<div
|
<div key={extension.id} className="flex items-center justify-between p-3">
|
||||||
key={extension.id}
|
|
||||||
className="flex items-center justify-between p-3"
|
|
||||||
>
|
|
||||||
<div className="space-y-0.5">
|
<div className="space-y-0.5">
|
||||||
<p className="text-sm font-semibold leading-none text-slate-900 dark:text-slate-100">
|
<p className="text-sm leading-none font-semibold text-slate-900 dark:text-slate-100">
|
||||||
{extension.name}
|
{extension.name}
|
||||||
</p>
|
</p>
|
||||||
<p className="text-sm text-slate-600 dark:text-slate-400">
|
<p className="text-sm text-slate-600 dark:text-slate-400">
|
||||||
|
|
|
||||||
|
|
@ -51,7 +51,7 @@ const MountPopopover = forwardRef<HTMLDivElement, object>((_props, ref) => {
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
<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">
|
||||||
{m.mount_no_mounted_media()}
|
{m.mount_no_mounted_media()}
|
||||||
</h3>
|
</h3>
|
||||||
<p className="text-xs leading-none text-slate-700 dark:text-slate-300">
|
<p className="text-xs leading-none text-slate-700 dark:text-slate-300">
|
||||||
|
|
@ -138,7 +138,7 @@ const MountPopopover = forwardRef<HTMLDivElement, object>((_props, ref) => {
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
className="animate-fadeIn opacity-0 space-y-2"
|
className="animate-fadeIn space-y-2 opacity-0"
|
||||||
style={{
|
style={{
|
||||||
animationDuration: "0.7s",
|
animationDuration: "0.7s",
|
||||||
animationDelay: "0.1s",
|
animationDelay: "0.1s",
|
||||||
|
|
@ -156,11 +156,13 @@ const MountPopopover = forwardRef<HTMLDivElement, object>((_props, ref) => {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{remoteVirtualMediaState ? (
|
{remoteVirtualMediaState ? (
|
||||||
<div className="flex select-none items-center justify-between text-xs">
|
<div className="flex items-center justify-between text-xs select-none">
|
||||||
<div className="select-none text-white dark:text-slate-300">
|
<div className="text-white select-none dark:text-slate-300">
|
||||||
<span>{m.mount_mounted_as()}</span>{" "}
|
<span>{m.mount_mounted_as()}</span>{" "}
|
||||||
<span className="font-semibold">
|
<span className="font-semibold">
|
||||||
{remoteVirtualMediaState.mode === "Disk" ? m.mount_mode_disk() : m.mount_mode_cdrom()}
|
{remoteVirtualMediaState.mode === "Disk"
|
||||||
|
? m.mount_mode_disk()
|
||||||
|
: m.mount_mode_cdrom()}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -189,10 +191,7 @@ const MountPopopover = forwardRef<HTMLDivElement, object>((_props, ref) => {
|
||||||
d="M4.99933 0.775635L0 5.77546H10L4.99933 0.775635Z"
|
d="M4.99933 0.775635L0 5.77546H10L4.99933 0.775635Z"
|
||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
/>
|
/>
|
||||||
<path
|
<path d="M10 7.49976H0V9.22453H10V7.49976Z" fill="currentColor" />
|
||||||
d="M10 7.49976H0V9.22453H10V7.49976Z"
|
|
||||||
fill="currentColor"
|
|
||||||
/>
|
|
||||||
</g>
|
</g>
|
||||||
<defs>
|
<defs>
|
||||||
<clipPath id="clip0_3137_1186">
|
<clipPath id="clip0_3137_1186">
|
||||||
|
|
@ -213,7 +212,7 @@ const MountPopopover = forwardRef<HTMLDivElement, object>((_props, ref) => {
|
||||||
|
|
||||||
{!remoteVirtualMediaState && (
|
{!remoteVirtualMediaState && (
|
||||||
<div
|
<div
|
||||||
className="flex animate-fadeIn opacity-0 items-center justify-end space-x-2"
|
className="flex animate-fadeIn items-center justify-end space-x-2 opacity-0"
|
||||||
style={{
|
style={{
|
||||||
animationDuration: "0.7s",
|
animationDuration: "0.7s",
|
||||||
animationDelay: "0.2s",
|
animationDelay: "0.2s",
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,7 @@ export default function PasteModal() {
|
||||||
const close = useClose();
|
const close = useClose();
|
||||||
|
|
||||||
const debugMode = useSettingsStore(state => state.debugMode);
|
const debugMode = useSettingsStore(state => state.debugMode);
|
||||||
const delayClassName = useMemo(() => debugMode ? "" : "hidden", [debugMode]);
|
const delayClassName = useMemo(() => (debugMode ? "" : "hidden"), [debugMode]);
|
||||||
|
|
||||||
const { setKeyboardLayout } = useSettingsStore();
|
const { setKeyboardLayout } = useSettingsStore();
|
||||||
const { selectedKeyboard } = useKeyboardLayout();
|
const { selectedKeyboard } = useKeyboardLayout();
|
||||||
|
|
@ -66,7 +66,7 @@ export default function PasteModal() {
|
||||||
const macroSteps: MacroStep[] = [];
|
const macroSteps: MacroStep[] = [];
|
||||||
|
|
||||||
for (const char of text) {
|
for (const char of text) {
|
||||||
const normalizedChar = char.normalize('NFC');
|
const normalizedChar = char.normalize("NFC");
|
||||||
const keyprops = selectedKeyboard.chars[normalizedChar];
|
const keyprops = selectedKeyboard.chars[normalizedChar];
|
||||||
if (!keyprops) continue;
|
if (!keyprops) continue;
|
||||||
|
|
||||||
|
|
@ -94,7 +94,7 @@ export default function PasteModal() {
|
||||||
macroSteps.push({
|
macroSteps.push({
|
||||||
keys: [String(key)],
|
keys: [String(key)],
|
||||||
modifiers: modifiers.length > 0 ? modifiers : null,
|
modifiers: modifiers.length > 0 ? modifiers : null,
|
||||||
delay
|
delay,
|
||||||
});
|
});
|
||||||
|
|
||||||
// if what was requested was a dead key, we need to send an unmodified space to emit
|
// if what was requested was a dead key, we need to send an unmodified space to emit
|
||||||
|
|
@ -123,10 +123,7 @@ export default function PasteModal() {
|
||||||
<div className="grid h-full grid-rows-(--grid-headerBody)">
|
<div className="grid h-full grid-rows-(--grid-headerBody)">
|
||||||
<div className="h-full space-y-4">
|
<div className="h-full space-y-4">
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<SettingsPageHeader
|
<SettingsPageHeader title={m.paste_text()} description={m.paste_text_description()} />
|
||||||
title={m.paste_text()}
|
|
||||||
description={m.paste_text_description()}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<div
|
<div
|
||||||
className="animate-fadeIn space-y-2 opacity-0"
|
className="animate-fadeIn space-y-2 opacity-0"
|
||||||
|
|
@ -165,7 +162,7 @@ export default function PasteModal() {
|
||||||
...new Set(
|
...new Set(
|
||||||
// @ts-expect-error TS doesn't recognize Intl.Segmenter in some environments
|
// @ts-expect-error TS doesn't recognize Intl.Segmenter in some environments
|
||||||
[...new Intl.Segmenter().segment(value)]
|
[...new Intl.Segmenter().segment(value)]
|
||||||
.map(x => x.segment.normalize('NFC'))
|
.map(x => x.segment.normalize("NFC"))
|
||||||
.filter(char => !selectedKeyboard.chars[char]),
|
.filter(char => !selectedKeyboard.chars[char]),
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
|
|
@ -178,8 +175,7 @@ export default function PasteModal() {
|
||||||
<div className="mt-2 flex items-center gap-x-2">
|
<div className="mt-2 flex items-center gap-x-2">
|
||||||
<ExclamationCircleIcon className="h-4 w-4 text-red-500 dark:text-red-400" />
|
<ExclamationCircleIcon className="h-4 w-4 text-red-500 dark:text-red-400" />
|
||||||
<span className="text-xs text-red-500 dark:text-red-400">
|
<span className="text-xs text-red-500 dark:text-red-400">
|
||||||
{m.paste_modal_invalid_chars_intro()}{" "}
|
{m.paste_modal_invalid_chars_intro()} {invalidChars.join(", ")}
|
||||||
{invalidChars.join(", ")}
|
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
@ -197,18 +193,22 @@ export default function PasteModal() {
|
||||||
setDelayValue(parseInt(e.target.value, 10));
|
setDelayValue(parseInt(e.target.value, 10));
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
{delayValue < 50 || delayValue > 65534 && (
|
{delayValue < 50 ||
|
||||||
<div className="mt-2 flex items-center gap-x-2">
|
(delayValue > 65534 && (
|
||||||
<ExclamationCircleIcon className="h-4 w-4 text-red-500 dark:text-red-400" />
|
<div className="mt-2 flex items-center gap-x-2">
|
||||||
<span className="text-xs text-red-500 dark:text-red-400">
|
<ExclamationCircleIcon className="h-4 w-4 text-red-500 dark:text-red-400" />
|
||||||
{m.paste_modal_delay_out_of_range({ min: 50, max: 65534 })}
|
<span className="text-xs text-red-500 dark:text-red-400">
|
||||||
</span>
|
{m.paste_modal_delay_out_of_range({ min: 50, max: 65534 })}
|
||||||
</div>
|
</span>
|
||||||
)}
|
</div>
|
||||||
|
))}
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<p className="text-xs text-slate-600 dark:text-slate-400">
|
<p className="text-xs text-slate-600 dark:text-slate-400">
|
||||||
{m.paste_modal_sending_using_layout({ iso: selectedKeyboard.isoCode, name: selectedKeyboard.name })}
|
{m.paste_modal_sending_using_layout({
|
||||||
|
iso: selectedKeyboard.isoCode,
|
||||||
|
name: selectedKeyboard.name,
|
||||||
|
})}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ export default function AddDeviceForm({
|
||||||
return (
|
return (
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<div
|
<div
|
||||||
className="animate-fadeIn opacity-0 space-y-4"
|
className="animate-fadeIn space-y-4 opacity-0"
|
||||||
style={{
|
style={{
|
||||||
animationDuration: "0.5s",
|
animationDuration: "0.5s",
|
||||||
animationFillMode: "forwards",
|
animationFillMode: "forwards",
|
||||||
|
|
@ -74,7 +74,7 @@ export default function AddDeviceForm({
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
className="flex animate-fadeIn opacity-0 items-center justify-end space-x-2"
|
className="flex animate-fadeIn items-center justify-end space-x-2 opacity-0"
|
||||||
style={{
|
style={{
|
||||||
animationDuration: "0.7s",
|
animationDuration: "0.7s",
|
||||||
animationDelay: "0.2s",
|
animationDelay: "0.2s",
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ export default function DeviceList({
|
||||||
{storedDevices.map((device, index) => (
|
{storedDevices.map((device, index) => (
|
||||||
<div key={index} className="flex items-center justify-between gap-x-2 p-3">
|
<div key={index} className="flex items-center justify-between gap-x-2 p-3">
|
||||||
<div className="space-y-0.5">
|
<div className="space-y-0.5">
|
||||||
<p className="text-sm font-semibold leading-none text-slate-900 dark:text-slate-100">
|
<p className="text-sm leading-none font-semibold text-slate-900 dark:text-slate-100">
|
||||||
{device?.name}
|
{device?.name}
|
||||||
</p>
|
</p>
|
||||||
<p className="text-sm text-slate-600 dark:text-slate-400">
|
<p className="text-sm text-slate-600 dark:text-slate-400">
|
||||||
|
|
@ -64,7 +64,7 @@ export default function DeviceList({
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
<div
|
<div
|
||||||
className="flex animate-fadeIn opacity-0 items-center justify-end space-x-2"
|
className="flex animate-fadeIn items-center justify-end space-x-2 opacity-0"
|
||||||
style={{
|
style={{
|
||||||
animationDuration: "0.7s",
|
animationDuration: "0.7s",
|
||||||
animationDelay: "0.2s",
|
animationDelay: "0.2s",
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ export default function EmptyStateCard({
|
||||||
setShowAddForm: (show: boolean) => void;
|
setShowAddForm: (show: boolean) => void;
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<div className="select-none space-y-4">
|
<div className="space-y-4 select-none">
|
||||||
<Card className="animate-fadeIn opacity-0">
|
<Card className="animate-fadeIn opacity-0">
|
||||||
<div className="flex items-center justify-center py-8 text-center">
|
<div className="flex items-center justify-center py-8 text-center">
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
|
|
@ -25,7 +25,7 @@ export default function EmptyStateCard({
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
</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">
|
||||||
{m.wake_on_lan_empty_no_devices_added()}
|
{m.wake_on_lan_empty_no_devices_added()}
|
||||||
</h3>
|
</h3>
|
||||||
<p className="text-xs leading-none text-slate-700 dark:text-slate-300">
|
<p className="text-xs leading-none text-slate-700 dark:text-slate-300">
|
||||||
|
|
@ -36,7 +36,7 @@ export default function EmptyStateCard({
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
<div
|
<div
|
||||||
className="flex animate-fadeIn opacity-0 items-center justify-end space-x-2"
|
className="flex animate-fadeIn items-center justify-end space-x-2 opacity-0"
|
||||||
style={{
|
style={{
|
||||||
animationDuration: "0.7s",
|
animationDuration: "0.7s",
|
||||||
animationDelay: "0.2s",
|
animationDelay: "0.2s",
|
||||||
|
|
|
||||||
|
|
@ -69,13 +69,17 @@ export default function WakeOnLanModal() {
|
||||||
(index: number) => {
|
(index: number) => {
|
||||||
const updatedDevices = storedDevices.filter((_, i) => i !== index);
|
const updatedDevices = storedDevices.filter((_, i) => i !== index);
|
||||||
|
|
||||||
send("setWakeOnLanDevices", { params: { devices: updatedDevices } }, (resp: JsonRpcResponse) => {
|
send(
|
||||||
if ("error" in resp) {
|
"setWakeOnLanDevices",
|
||||||
console.error("Failed to update Wake-on-LAN devices:", resp.error);
|
{ params: { devices: updatedDevices } },
|
||||||
} else {
|
(resp: JsonRpcResponse) => {
|
||||||
syncStoredDevices();
|
if ("error" in resp) {
|
||||||
}
|
console.error("Failed to update Wake-on-LAN devices:", resp.error);
|
||||||
});
|
} else {
|
||||||
|
syncStoredDevices();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
},
|
},
|
||||||
[send, storedDevices, syncStoredDevices],
|
[send, storedDevices, syncStoredDevices],
|
||||||
);
|
);
|
||||||
|
|
@ -85,15 +89,19 @@ export default function WakeOnLanModal() {
|
||||||
if (!name || !macAddress) return;
|
if (!name || !macAddress) return;
|
||||||
const updatedDevices = [...storedDevices, { name, macAddress }];
|
const updatedDevices = [...storedDevices, { name, macAddress }];
|
||||||
console.log("updatedDevices", updatedDevices);
|
console.log("updatedDevices", updatedDevices);
|
||||||
send("setWakeOnLanDevices", { params: { devices: updatedDevices } }, (resp: JsonRpcResponse) => {
|
send(
|
||||||
if ("error" in resp) {
|
"setWakeOnLanDevices",
|
||||||
console.error("Failed to add Wake-on-LAN device:", resp.error);
|
{ params: { devices: updatedDevices } },
|
||||||
setAddDeviceErrorMessage(m.wake_on_lan_failed_add_device());
|
(resp: JsonRpcResponse) => {
|
||||||
} else {
|
if ("error" in resp) {
|
||||||
setShowAddForm(false);
|
console.error("Failed to add Wake-on-LAN device:", resp.error);
|
||||||
syncStoredDevices();
|
setAddDeviceErrorMessage(m.wake_on_lan_failed_add_device());
|
||||||
}
|
} else {
|
||||||
});
|
setShowAddForm(false);
|
||||||
|
syncStoredDevices();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
},
|
},
|
||||||
[send, storedDevices, syncStoredDevices],
|
[send, storedDevices, syncStoredDevices],
|
||||||
);
|
);
|
||||||
|
|
@ -103,10 +111,7 @@ export default function WakeOnLanModal() {
|
||||||
<div className="space-y-4 p-4 py-3">
|
<div className="space-y-4 p-4 py-3">
|
||||||
<div className="grid h-full grid-rows-(--grid-headerBody)">
|
<div className="grid h-full grid-rows-(--grid-headerBody)">
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<SettingsPageHeader
|
<SettingsPageHeader title={m.wake_on_lan()} description={m.wake_on_lan_description()} />
|
||||||
title={m.wake_on_lan()}
|
|
||||||
description={m.wake_on_lan_description()}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{showAddForm ? (
|
{showAddForm ? (
|
||||||
<AddDeviceForm
|
<AddDeviceForm
|
||||||
|
|
|
||||||
|
|
@ -124,17 +124,28 @@ export default function ConnectionStatsSidebar() {
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<GridCard cardClassName="rounded-r-none">
|
<GridCard cardClassName="rounded-r-none">
|
||||||
<div className="h-[34px] flex items-center text-xs select-all text-black font-mono dark:text-white px-3 ">
|
<div className="flex h-[34px] items-center px-3 font-mono text-xs text-black select-all dark:text-white">
|
||||||
{remoteIPAddress}
|
{remoteIPAddress}
|
||||||
</div>
|
</div>
|
||||||
</GridCard>
|
</GridCard>
|
||||||
<Button className="rounded-l-none border-l-slate-800/30 dark:border-slate-300/20" size="SM" type="button" theme="light" LeadingIcon={LuCopy} onClick={async () => {
|
<Button
|
||||||
if (await copy(remoteIPAddress)) {
|
className="rounded-l-none border-l-slate-800/30 dark:border-slate-300/20"
|
||||||
notifications.success((m.connection_stats_remote_ip_address_copy_success({ ip: remoteIPAddress })));
|
size="SM"
|
||||||
} else {
|
type="button"
|
||||||
notifications.error(m.connection_stats_remote_ip_address_copy_error());
|
theme="light"
|
||||||
}
|
LeadingIcon={LuCopy}
|
||||||
}} />
|
onClick={async () => {
|
||||||
|
if (await copy(remoteIPAddress)) {
|
||||||
|
notifications.success(
|
||||||
|
m.connection_stats_remote_ip_address_copy_success({
|
||||||
|
ip: remoteIPAddress,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
notifications.error(m.connection_stats_remote_ip_address_copy_error());
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
@ -184,10 +195,7 @@ export default function ConnectionStatsSidebar() {
|
||||||
data={jitterBufferAvgDelayData}
|
data={jitterBufferAvgDelayData}
|
||||||
gate={inboundVideoRtpStats}
|
gate={inboundVideoRtpStats}
|
||||||
supported={
|
supported={
|
||||||
someIterable(
|
someIterable(inboundVideoRtpStats, ([, x]) => x.jitterBufferDelay != null) &&
|
||||||
inboundVideoRtpStats,
|
|
||||||
([, x]) => x.jitterBufferDelay != null,
|
|
||||||
) &&
|
|
||||||
someIterable(
|
someIterable(
|
||||||
inboundVideoRtpStats,
|
inboundVideoRtpStats,
|
||||||
([, x]) => x.jitterBufferEmittedCount != null,
|
([, x]) => x.jitterBufferEmittedCount != null,
|
||||||
|
|
|
||||||
|
|
@ -3,47 +3,50 @@ import { useCallback, useState } from "react";
|
||||||
export function useCopyToClipboard(resetInterval = 2000) {
|
export function useCopyToClipboard(resetInterval = 2000) {
|
||||||
const [isCopied, setIsCopied] = useState(false);
|
const [isCopied, setIsCopied] = useState(false);
|
||||||
|
|
||||||
const copy = useCallback(async (text: string) => {
|
const copy = useCallback(
|
||||||
if (!text) return false;
|
async (text: string) => {
|
||||||
|
if (!text) return false;
|
||||||
|
|
||||||
let success = false;
|
let success = false;
|
||||||
|
|
||||||
if (navigator.clipboard && window.isSecureContext) {
|
if (navigator.clipboard && window.isSecureContext) {
|
||||||
try {
|
try {
|
||||||
await navigator.clipboard.writeText(text);
|
await navigator.clipboard.writeText(text);
|
||||||
success = true;
|
success = true;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.warn("Clipboard API failed:", err);
|
console.warn("Clipboard API failed:", err);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Fallback for insecure contexts
|
// Fallback for insecure contexts
|
||||||
if (!success) {
|
if (!success) {
|
||||||
const textarea = document.createElement("textarea");
|
const textarea = document.createElement("textarea");
|
||||||
textarea.value = text;
|
textarea.value = text;
|
||||||
textarea.style.position = "fixed";
|
textarea.style.position = "fixed";
|
||||||
textarea.style.opacity = "0";
|
textarea.style.opacity = "0";
|
||||||
document.body.appendChild(textarea);
|
document.body.appendChild(textarea);
|
||||||
textarea.focus();
|
textarea.focus();
|
||||||
textarea.select();
|
textarea.select();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
success = document.execCommand("copy");
|
success = document.execCommand("copy");
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error("Fallback copy failed:", err);
|
console.error("Fallback copy failed:", err);
|
||||||
success = false;
|
success = false;
|
||||||
} finally {
|
} finally {
|
||||||
document.body.removeChild(textarea);
|
document.body.removeChild(textarea);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
setIsCopied(success);
|
setIsCopied(success);
|
||||||
if (success && resetInterval > 0) {
|
if (success && resetInterval > 0) {
|
||||||
setTimeout(() => setIsCopied(false), resetInterval);
|
setTimeout(() => setIsCopied(false), resetInterval);
|
||||||
}
|
}
|
||||||
|
|
||||||
return success;
|
return success;
|
||||||
}, [resetInterval]);
|
},
|
||||||
|
[resetInterval],
|
||||||
|
);
|
||||||
|
|
||||||
return { copy, isCopied };
|
return { copy, isCopied };
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,4 +2,4 @@ export const DEFAULT_DELAY = 50;
|
||||||
export const MAX_STEPS_PER_MACRO = 10;
|
export const MAX_STEPS_PER_MACRO = 10;
|
||||||
export const MAX_KEYS_PER_STEP = 10;
|
export const MAX_KEYS_PER_STEP = 10;
|
||||||
export const MAX_TOTAL_MACROS = 25;
|
export const MAX_TOTAL_MACROS = 25;
|
||||||
export const COPY_SUFFIX = "(copy)";
|
export const COPY_SUFFIX = "(copy)";
|
||||||
|
|
|
||||||
|
|
@ -1,449 +1,428 @@
|
||||||
import { hidKeyBufferSize, KeyboardLedState, KeysDownState } from "./stores";
|
import { hidKeyBufferSize, KeyboardLedState, KeysDownState } from "./stores";
|
||||||
|
|
||||||
export const HID_RPC_MESSAGE_TYPES = {
|
export const HID_RPC_MESSAGE_TYPES = {
|
||||||
Handshake: 0x01,
|
Handshake: 0x01,
|
||||||
KeyboardReport: 0x02,
|
KeyboardReport: 0x02,
|
||||||
PointerReport: 0x03,
|
PointerReport: 0x03,
|
||||||
WheelReport: 0x04,
|
WheelReport: 0x04,
|
||||||
KeypressReport: 0x05,
|
KeypressReport: 0x05,
|
||||||
KeypressKeepAliveReport: 0x09,
|
KeypressKeepAliveReport: 0x09,
|
||||||
MouseReport: 0x06,
|
MouseReport: 0x06,
|
||||||
KeyboardMacroReport: 0x07,
|
KeyboardMacroReport: 0x07,
|
||||||
CancelKeyboardMacroReport: 0x08,
|
CancelKeyboardMacroReport: 0x08,
|
||||||
KeyboardLedState: 0x32,
|
KeyboardLedState: 0x32,
|
||||||
KeysDownState: 0x33,
|
KeysDownState: 0x33,
|
||||||
KeyboardMacroState: 0x34,
|
KeyboardMacroState: 0x34,
|
||||||
}
|
};
|
||||||
|
|
||||||
export type HidRpcMessageType = typeof HID_RPC_MESSAGE_TYPES[keyof typeof HID_RPC_MESSAGE_TYPES];
|
export type HidRpcMessageType = (typeof HID_RPC_MESSAGE_TYPES)[keyof typeof HID_RPC_MESSAGE_TYPES];
|
||||||
|
|
||||||
export const HID_RPC_VERSION = 0x01;
|
export const HID_RPC_VERSION = 0x01;
|
||||||
|
|
||||||
const withinUint8Range = (value: number) => {
|
const withinUint8Range = (value: number) => {
|
||||||
return value >= 0 && value <= 255;
|
return value >= 0 && value <= 255;
|
||||||
};
|
};
|
||||||
|
|
||||||
const fromInt32toUint8 = (n: number) => {
|
const fromInt32toUint8 = (n: number) => {
|
||||||
if (n !== n >> 0) {
|
if (n !== n >> 0) {
|
||||||
throw new Error(`Number ${n} is not within the int32 range`);
|
throw new Error(`Number ${n} is not within the int32 range`);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Uint8Array([
|
return new Uint8Array([(n >> 24) & 0xff, (n >> 16) & 0xff, (n >> 8) & 0xff, n & 0xff]);
|
||||||
(n >> 24) & 0xFF,
|
|
||||||
(n >> 16) & 0xFF,
|
|
||||||
(n >> 8) & 0xFF,
|
|
||||||
n & 0xFF,
|
|
||||||
]);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const fromUint16toUint8 = (n: number) => {
|
const fromUint16toUint8 = (n: number) => {
|
||||||
if (n > 65535 || n < 0) {
|
if (n > 65535 || n < 0) {
|
||||||
throw new Error(`Number ${n} is not within the uint16 range`);
|
throw new Error(`Number ${n} is not within the uint16 range`);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Uint8Array([
|
return new Uint8Array([(n >> 8) & 0xff, n & 0xff]);
|
||||||
(n >> 8) & 0xFF,
|
|
||||||
n & 0xFF,
|
|
||||||
]);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const fromUint32toUint8 = (n: number) => {
|
const fromUint32toUint8 = (n: number) => {
|
||||||
if (n > 4294967295 || n < 0) {
|
if (n > 4294967295 || n < 0) {
|
||||||
throw new Error(`Number ${n} is not within the uint32 range`);
|
throw new Error(`Number ${n} is not within the uint32 range`);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Uint8Array([
|
return new Uint8Array([(n >> 24) & 0xff, (n >> 16) & 0xff, (n >> 8) & 0xff, n & 0xff]);
|
||||||
(n >> 24) & 0xFF,
|
|
||||||
(n >> 16) & 0xFF,
|
|
||||||
(n >> 8) & 0xFF,
|
|
||||||
n & 0xFF,
|
|
||||||
]);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const fromInt8ToUint8 = (n: number) => {
|
const fromInt8ToUint8 = (n: number) => {
|
||||||
if (n < -128 || n > 127) {
|
if (n < -128 || n > 127) {
|
||||||
throw new Error(`Number ${n} is not within the int8 range`);
|
throw new Error(`Number ${n} is not within the int8 range`);
|
||||||
}
|
}
|
||||||
|
|
||||||
return n & 0xFF;
|
return n & 0xff;
|
||||||
};
|
};
|
||||||
|
|
||||||
const keyboardLedStateMasks = {
|
const keyboardLedStateMasks = {
|
||||||
num_lock: 1 << 0,
|
num_lock: 1 << 0,
|
||||||
caps_lock: 1 << 1,
|
caps_lock: 1 << 1,
|
||||||
scroll_lock: 1 << 2,
|
scroll_lock: 1 << 2,
|
||||||
compose: 1 << 3,
|
compose: 1 << 3,
|
||||||
kana: 1 << 4,
|
kana: 1 << 4,
|
||||||
shift: 1 << 6,
|
shift: 1 << 6,
|
||||||
}
|
};
|
||||||
|
|
||||||
export class RpcMessage {
|
export class RpcMessage {
|
||||||
messageType: HidRpcMessageType;
|
messageType: HidRpcMessageType;
|
||||||
|
|
||||||
constructor(messageType: HidRpcMessageType) {
|
constructor(messageType: HidRpcMessageType) {
|
||||||
this.messageType = messageType;
|
this.messageType = messageType;
|
||||||
}
|
}
|
||||||
|
|
||||||
marshal(): Uint8Array {
|
marshal(): Uint8Array {
|
||||||
throw new Error("Not implemented");
|
throw new Error("Not implemented");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static unmarshal(_data: Uint8Array): RpcMessage | undefined {
|
public static unmarshal(_data: Uint8Array): RpcMessage | undefined {
|
||||||
throw new Error("Not implemented");
|
throw new Error("Not implemented");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class HandshakeMessage extends RpcMessage {
|
export class HandshakeMessage extends RpcMessage {
|
||||||
version: number;
|
version: number;
|
||||||
|
|
||||||
constructor(version: number) {
|
constructor(version: number) {
|
||||||
super(HID_RPC_MESSAGE_TYPES.Handshake);
|
super(HID_RPC_MESSAGE_TYPES.Handshake);
|
||||||
this.version = version;
|
this.version = version;
|
||||||
|
}
|
||||||
|
|
||||||
|
marshal(): Uint8Array {
|
||||||
|
return new Uint8Array([this.messageType, this.version]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static unmarshal(data: Uint8Array): HandshakeMessage | undefined {
|
||||||
|
if (data.length < 1) {
|
||||||
|
throw new Error(`Invalid handshake message length: ${data.length}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
marshal(): Uint8Array {
|
return new HandshakeMessage(data[0]);
|
||||||
return new Uint8Array([this.messageType, this.version]);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public static unmarshal(data: Uint8Array): HandshakeMessage | undefined {
|
|
||||||
if (data.length < 1) {
|
|
||||||
throw new Error(`Invalid handshake message length: ${data.length}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new HandshakeMessage(data[0]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class KeypressReportMessage extends RpcMessage {
|
export class KeypressReportMessage extends RpcMessage {
|
||||||
private _key = 0;
|
private _key = 0;
|
||||||
private _press = false;
|
private _press = false;
|
||||||
|
|
||||||
get key(): number {
|
get key(): number {
|
||||||
return this._key;
|
return this._key;
|
||||||
|
}
|
||||||
|
|
||||||
|
set key(value: number) {
|
||||||
|
if (!withinUint8Range(value)) {
|
||||||
|
throw new Error(`Key ${value} is not within the uint8 range`);
|
||||||
}
|
}
|
||||||
|
|
||||||
set key(value: number) {
|
this._key = value;
|
||||||
if (!withinUint8Range(value)) {
|
}
|
||||||
throw new Error(`Key ${value} is not within the uint8 range`);
|
|
||||||
}
|
|
||||||
|
|
||||||
this._key = value;
|
get press(): boolean {
|
||||||
|
return this._press;
|
||||||
|
}
|
||||||
|
|
||||||
|
set press(value: boolean) {
|
||||||
|
this._press = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(key: number, press: boolean) {
|
||||||
|
super(HID_RPC_MESSAGE_TYPES.KeypressReport);
|
||||||
|
this.key = key;
|
||||||
|
this.press = press;
|
||||||
|
}
|
||||||
|
|
||||||
|
marshal(): Uint8Array {
|
||||||
|
return new Uint8Array([this.messageType, this.key, this.press ? 1 : 0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static unmarshal(data: Uint8Array): KeypressReportMessage | undefined {
|
||||||
|
if (data.length < 1) {
|
||||||
|
throw new Error(`Invalid keypress report message length: ${data.length}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
get press(): boolean {
|
return new KeypressReportMessage(data[0], data[1] === 1);
|
||||||
return this._press;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
set press(value: boolean) {
|
|
||||||
this._press = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(key: number, press: boolean) {
|
|
||||||
super(HID_RPC_MESSAGE_TYPES.KeypressReport);
|
|
||||||
this.key = key;
|
|
||||||
this.press = press;
|
|
||||||
}
|
|
||||||
|
|
||||||
marshal(): Uint8Array {
|
|
||||||
return new Uint8Array([
|
|
||||||
this.messageType,
|
|
||||||
this.key,
|
|
||||||
this.press ? 1 : 0,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static unmarshal(data: Uint8Array): KeypressReportMessage | undefined {
|
|
||||||
if (data.length < 1) {
|
|
||||||
throw new Error(`Invalid keypress report message length: ${data.length}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new KeypressReportMessage(data[0], data[1] === 1);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class KeyboardReportMessage extends RpcMessage {
|
export class KeyboardReportMessage extends RpcMessage {
|
||||||
private _keys: number[] = [];
|
private _keys: number[] = [];
|
||||||
private _modifier = 0;
|
private _modifier = 0;
|
||||||
|
|
||||||
get keys(): number[] {
|
get keys(): number[] {
|
||||||
return this._keys;
|
return this._keys;
|
||||||
|
}
|
||||||
|
|
||||||
|
set keys(value: number[]) {
|
||||||
|
value.forEach(k => {
|
||||||
|
if (!withinUint8Range(k)) {
|
||||||
|
throw new Error(`Key ${k} is not within the uint8 range`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this._keys = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
get modifier(): number {
|
||||||
|
return this._modifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
set modifier(value: number) {
|
||||||
|
if (!withinUint8Range(value)) {
|
||||||
|
throw new Error(`Modifier ${value} is not within the uint8 range`);
|
||||||
}
|
}
|
||||||
|
|
||||||
set keys(value: number[]) {
|
this._modifier = value;
|
||||||
value.forEach((k) => {
|
}
|
||||||
if (!withinUint8Range(k)) {
|
|
||||||
throw new Error(`Key ${k} is not within the uint8 range`);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
this._keys = value;
|
constructor(keys: number[], modifier: number) {
|
||||||
|
super(HID_RPC_MESSAGE_TYPES.KeyboardReport);
|
||||||
|
this.keys = keys;
|
||||||
|
this.modifier = modifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
marshal(): Uint8Array {
|
||||||
|
return new Uint8Array([this.messageType, this.modifier, ...this.keys]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static unmarshal(data: Uint8Array): KeyboardReportMessage | undefined {
|
||||||
|
if (data.length < 1) {
|
||||||
|
throw new Error(`Invalid keyboard report message length: ${data.length}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
get modifier(): number {
|
return new KeyboardReportMessage(Array.from(data.slice(1)), data[0]);
|
||||||
return this._modifier;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
set modifier(value: number) {
|
|
||||||
if (!withinUint8Range(value)) {
|
|
||||||
throw new Error(`Modifier ${value} is not within the uint8 range`);
|
|
||||||
}
|
|
||||||
|
|
||||||
this._modifier = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(keys: number[], modifier: number) {
|
|
||||||
super(HID_RPC_MESSAGE_TYPES.KeyboardReport);
|
|
||||||
this.keys = keys;
|
|
||||||
this.modifier = modifier;
|
|
||||||
}
|
|
||||||
|
|
||||||
marshal(): Uint8Array {
|
|
||||||
return new Uint8Array([
|
|
||||||
this.messageType,
|
|
||||||
this.modifier,
|
|
||||||
...this.keys,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static unmarshal(data: Uint8Array): KeyboardReportMessage | undefined {
|
|
||||||
if (data.length < 1) {
|
|
||||||
throw new Error(`Invalid keyboard report message length: ${data.length}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new KeyboardReportMessage(Array.from(data.slice(1)), data[0]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface KeyboardMacroStep extends KeysDownState {
|
export interface KeyboardMacroStep extends KeysDownState {
|
||||||
delay: number;
|
delay: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class KeyboardMacroReportMessage extends RpcMessage {
|
export class KeyboardMacroReportMessage extends RpcMessage {
|
||||||
isPaste: boolean;
|
isPaste: boolean;
|
||||||
stepCount: number;
|
stepCount: number;
|
||||||
steps: KeyboardMacroStep[];
|
steps: KeyboardMacroStep[];
|
||||||
|
|
||||||
KEYS_LENGTH = hidKeyBufferSize;
|
KEYS_LENGTH = hidKeyBufferSize;
|
||||||
|
|
||||||
constructor(isPaste: boolean, stepCount: number, steps: KeyboardMacroStep[]) {
|
constructor(isPaste: boolean, stepCount: number, steps: KeyboardMacroStep[]) {
|
||||||
super(HID_RPC_MESSAGE_TYPES.KeyboardMacroReport);
|
super(HID_RPC_MESSAGE_TYPES.KeyboardMacroReport);
|
||||||
this.isPaste = isPaste;
|
this.isPaste = isPaste;
|
||||||
this.stepCount = stepCount;
|
this.stepCount = stepCount;
|
||||||
this.steps = steps;
|
this.steps = steps;
|
||||||
|
}
|
||||||
|
|
||||||
|
marshal(): Uint8Array {
|
||||||
|
// validate if length is correct
|
||||||
|
if (this.stepCount !== this.steps.length) {
|
||||||
|
throw new Error(
|
||||||
|
`Length ${this.stepCount} is not equal to the number of steps ${this.steps.length}`,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
marshal(): Uint8Array {
|
const data = new Uint8Array(this.stepCount * 9 + 6);
|
||||||
// validate if length is correct
|
data.set(
|
||||||
if (this.stepCount !== this.steps.length) {
|
new Uint8Array([
|
||||||
throw new Error(`Length ${this.stepCount} is not equal to the number of steps ${this.steps.length}`);
|
this.messageType,
|
||||||
|
this.isPaste ? 1 : 0,
|
||||||
|
...fromUint32toUint8(this.stepCount),
|
||||||
|
]),
|
||||||
|
0,
|
||||||
|
);
|
||||||
|
|
||||||
|
let offset = 6;
|
||||||
|
|
||||||
|
for (let i = 0; i < this.stepCount; i++) {
|
||||||
|
const step = this.steps[i];
|
||||||
|
if (!withinUint8Range(step.modifier)) {
|
||||||
|
throw new Error(`Modifier ${step.modifier} is not within the uint8 range`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure the keys are within the KEYS_LENGTH range
|
||||||
|
const keys = step.keys;
|
||||||
|
if (keys.length > this.KEYS_LENGTH) {
|
||||||
|
throw new Error(`Keys ${keys} is not within the hidKeyBufferSize range`);
|
||||||
|
} else if (keys.length < this.KEYS_LENGTH) {
|
||||||
|
keys.push(...Array(this.KEYS_LENGTH - keys.length).fill(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const key of keys) {
|
||||||
|
if (!withinUint8Range(key)) {
|
||||||
|
throw new Error(`Key ${key} is not within the uint8 range`);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const data = new Uint8Array(this.stepCount * 9 + 6);
|
const macroBinary = new Uint8Array([
|
||||||
data.set(new Uint8Array([
|
step.modifier,
|
||||||
this.messageType,
|
...keys,
|
||||||
this.isPaste ? 1 : 0,
|
...fromUint16toUint8(step.delay),
|
||||||
...fromUint32toUint8(this.stepCount),
|
]);
|
||||||
]), 0);
|
|
||||||
|
|
||||||
let offset = 6;
|
data.set(macroBinary, offset);
|
||||||
|
offset += 9;
|
||||||
for (let i = 0; i < this.stepCount; i++) {
|
|
||||||
const step = this.steps[i];
|
|
||||||
if (!withinUint8Range(step.modifier)) {
|
|
||||||
throw new Error(`Modifier ${step.modifier} is not within the uint8 range`);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure the keys are within the KEYS_LENGTH range
|
|
||||||
const keys = step.keys;
|
|
||||||
if (keys.length > this.KEYS_LENGTH) {
|
|
||||||
throw new Error(`Keys ${keys} is not within the hidKeyBufferSize range`);
|
|
||||||
} else if (keys.length < this.KEYS_LENGTH) {
|
|
||||||
keys.push(...Array(this.KEYS_LENGTH - keys.length).fill(0));
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const key of keys) {
|
|
||||||
if (!withinUint8Range(key)) {
|
|
||||||
throw new Error(`Key ${key} is not within the uint8 range`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const macroBinary = new Uint8Array([
|
|
||||||
step.modifier,
|
|
||||||
...keys,
|
|
||||||
...fromUint16toUint8(step.delay),
|
|
||||||
]);
|
|
||||||
|
|
||||||
data.set(macroBinary, offset);
|
|
||||||
offset += 9;
|
|
||||||
}
|
|
||||||
|
|
||||||
return data;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class KeyboardMacroStateMessage extends RpcMessage {
|
export class KeyboardMacroStateMessage extends RpcMessage {
|
||||||
state: boolean;
|
state: boolean;
|
||||||
isPaste: boolean;
|
isPaste: boolean;
|
||||||
|
|
||||||
constructor(state: boolean, isPaste: boolean) {
|
constructor(state: boolean, isPaste: boolean) {
|
||||||
super(HID_RPC_MESSAGE_TYPES.KeyboardMacroState);
|
super(HID_RPC_MESSAGE_TYPES.KeyboardMacroState);
|
||||||
this.state = state;
|
this.state = state;
|
||||||
this.isPaste = isPaste;
|
this.isPaste = isPaste;
|
||||||
|
}
|
||||||
|
|
||||||
|
marshal(): Uint8Array {
|
||||||
|
return new Uint8Array([this.messageType, this.state ? 1 : 0, this.isPaste ? 1 : 0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static unmarshal(data: Uint8Array): KeyboardMacroStateMessage | undefined {
|
||||||
|
if (data.length < 1) {
|
||||||
|
throw new Error(`Invalid keyboard macro state report message length: ${data.length}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
marshal(): Uint8Array {
|
return new KeyboardMacroStateMessage(data[0] === 1, data[1] === 1);
|
||||||
return new Uint8Array([
|
}
|
||||||
this.messageType,
|
|
||||||
this.state ? 1 : 0,
|
|
||||||
this.isPaste ? 1 : 0,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static unmarshal(data: Uint8Array): KeyboardMacroStateMessage | undefined {
|
|
||||||
if (data.length < 1) {
|
|
||||||
throw new Error(`Invalid keyboard macro state report message length: ${data.length}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new KeyboardMacroStateMessage(data[0] === 1, data[1] === 1);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class KeyboardLedStateMessage extends RpcMessage {
|
export class KeyboardLedStateMessage extends RpcMessage {
|
||||||
keyboardLedState: KeyboardLedState;
|
keyboardLedState: KeyboardLedState;
|
||||||
|
|
||||||
constructor(keyboardLedState: KeyboardLedState) {
|
constructor(keyboardLedState: KeyboardLedState) {
|
||||||
super(HID_RPC_MESSAGE_TYPES.KeyboardLedState);
|
super(HID_RPC_MESSAGE_TYPES.KeyboardLedState);
|
||||||
this.keyboardLedState = keyboardLedState;
|
this.keyboardLedState = keyboardLedState;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static unmarshal(data: Uint8Array): KeyboardLedStateMessage | undefined {
|
||||||
|
if (data.length < 1) {
|
||||||
|
throw new Error(`Invalid keyboard led state message length: ${data.length}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static unmarshal(data: Uint8Array): KeyboardLedStateMessage | undefined {
|
const s = data[0];
|
||||||
if (data.length < 1) {
|
|
||||||
throw new Error(`Invalid keyboard led state message length: ${data.length}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const s = data[0];
|
const state = {
|
||||||
|
num_lock: (s & keyboardLedStateMasks.num_lock) !== 0,
|
||||||
|
caps_lock: (s & keyboardLedStateMasks.caps_lock) !== 0,
|
||||||
|
scroll_lock: (s & keyboardLedStateMasks.scroll_lock) !== 0,
|
||||||
|
compose: (s & keyboardLedStateMasks.compose) !== 0,
|
||||||
|
kana: (s & keyboardLedStateMasks.kana) !== 0,
|
||||||
|
shift: (s & keyboardLedStateMasks.shift) !== 0,
|
||||||
|
} as KeyboardLedState;
|
||||||
|
|
||||||
const state = {
|
return new KeyboardLedStateMessage(state);
|
||||||
num_lock: (s & keyboardLedStateMasks.num_lock) !== 0,
|
}
|
||||||
caps_lock: (s & keyboardLedStateMasks.caps_lock) !== 0,
|
|
||||||
scroll_lock: (s & keyboardLedStateMasks.scroll_lock) !== 0,
|
|
||||||
compose: (s & keyboardLedStateMasks.compose) !== 0,
|
|
||||||
kana: (s & keyboardLedStateMasks.kana) !== 0,
|
|
||||||
shift: (s & keyboardLedStateMasks.shift) !== 0,
|
|
||||||
} as KeyboardLedState;
|
|
||||||
|
|
||||||
return new KeyboardLedStateMessage(state);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class KeysDownStateMessage extends RpcMessage {
|
export class KeysDownStateMessage extends RpcMessage {
|
||||||
keysDownState: KeysDownState;
|
keysDownState: KeysDownState;
|
||||||
|
|
||||||
constructor(keysDownState: KeysDownState) {
|
constructor(keysDownState: KeysDownState) {
|
||||||
super(HID_RPC_MESSAGE_TYPES.KeysDownState);
|
super(HID_RPC_MESSAGE_TYPES.KeysDownState);
|
||||||
this.keysDownState = keysDownState;
|
this.keysDownState = keysDownState;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static unmarshal(data: Uint8Array): KeysDownStateMessage | undefined {
|
||||||
|
if (data.length < 1) {
|
||||||
|
throw new Error(`Invalid keys down state message length: ${data.length}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static unmarshal(data: Uint8Array): KeysDownStateMessage | undefined {
|
return new KeysDownStateMessage({
|
||||||
if (data.length < 1) {
|
modifier: data[0],
|
||||||
throw new Error(`Invalid keys down state message length: ${data.length}`);
|
keys: Array.from(data.slice(1)),
|
||||||
}
|
});
|
||||||
|
}
|
||||||
return new KeysDownStateMessage({
|
|
||||||
modifier: data[0],
|
|
||||||
keys: Array.from(data.slice(1))
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class PointerReportMessage extends RpcMessage {
|
export class PointerReportMessage extends RpcMessage {
|
||||||
x: number;
|
x: number;
|
||||||
y: number;
|
y: number;
|
||||||
buttons: number;
|
buttons: number;
|
||||||
|
|
||||||
constructor(x: number, y: number, buttons: number) {
|
constructor(x: number, y: number, buttons: number) {
|
||||||
super(HID_RPC_MESSAGE_TYPES.PointerReport);
|
super(HID_RPC_MESSAGE_TYPES.PointerReport);
|
||||||
this.x = x;
|
this.x = x;
|
||||||
this.y = y;
|
this.y = y;
|
||||||
this.buttons = buttons;
|
this.buttons = buttons;
|
||||||
}
|
}
|
||||||
|
|
||||||
marshal(): Uint8Array {
|
marshal(): Uint8Array {
|
||||||
return new Uint8Array([
|
return new Uint8Array([
|
||||||
this.messageType,
|
this.messageType,
|
||||||
...fromInt32toUint8(this.x),
|
...fromInt32toUint8(this.x),
|
||||||
...fromInt32toUint8(this.y),
|
...fromInt32toUint8(this.y),
|
||||||
this.buttons,
|
this.buttons,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class CancelKeyboardMacroReportMessage extends RpcMessage {
|
export class CancelKeyboardMacroReportMessage extends RpcMessage {
|
||||||
|
constructor() {
|
||||||
|
super(HID_RPC_MESSAGE_TYPES.CancelKeyboardMacroReport);
|
||||||
|
}
|
||||||
|
|
||||||
constructor() {
|
marshal(): Uint8Array {
|
||||||
super(HID_RPC_MESSAGE_TYPES.CancelKeyboardMacroReport);
|
return new Uint8Array([this.messageType]);
|
||||||
}
|
}
|
||||||
|
|
||||||
marshal(): Uint8Array {
|
|
||||||
return new Uint8Array([this.messageType]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class MouseReportMessage extends RpcMessage {
|
export class MouseReportMessage extends RpcMessage {
|
||||||
dx: number;
|
dx: number;
|
||||||
dy: number;
|
dy: number;
|
||||||
buttons: number;
|
buttons: number;
|
||||||
|
|
||||||
constructor(dx: number, dy: number, buttons: number) {
|
constructor(dx: number, dy: number, buttons: number) {
|
||||||
super(HID_RPC_MESSAGE_TYPES.MouseReport);
|
super(HID_RPC_MESSAGE_TYPES.MouseReport);
|
||||||
this.dx = dx;
|
this.dx = dx;
|
||||||
this.dy = dy;
|
this.dy = dy;
|
||||||
this.buttons = buttons;
|
this.buttons = buttons;
|
||||||
}
|
}
|
||||||
|
|
||||||
marshal(): Uint8Array {
|
marshal(): Uint8Array {
|
||||||
return new Uint8Array([
|
return new Uint8Array([
|
||||||
this.messageType,
|
this.messageType,
|
||||||
fromInt8ToUint8(this.dx),
|
fromInt8ToUint8(this.dx),
|
||||||
fromInt8ToUint8(this.dy),
|
fromInt8ToUint8(this.dy),
|
||||||
this.buttons,
|
this.buttons,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class KeypressKeepAliveMessage extends RpcMessage {
|
export class KeypressKeepAliveMessage extends RpcMessage {
|
||||||
constructor() {
|
constructor() {
|
||||||
super(HID_RPC_MESSAGE_TYPES.KeypressKeepAliveReport);
|
super(HID_RPC_MESSAGE_TYPES.KeypressKeepAliveReport);
|
||||||
}
|
}
|
||||||
|
|
||||||
marshal(): Uint8Array {
|
marshal(): Uint8Array {
|
||||||
return new Uint8Array([this.messageType]);
|
return new Uint8Array([this.messageType]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const messageRegistry = {
|
export const messageRegistry = {
|
||||||
[HID_RPC_MESSAGE_TYPES.Handshake]: HandshakeMessage,
|
[HID_RPC_MESSAGE_TYPES.Handshake]: HandshakeMessage,
|
||||||
[HID_RPC_MESSAGE_TYPES.KeysDownState]: KeysDownStateMessage,
|
[HID_RPC_MESSAGE_TYPES.KeysDownState]: KeysDownStateMessage,
|
||||||
[HID_RPC_MESSAGE_TYPES.KeyboardLedState]: KeyboardLedStateMessage,
|
[HID_RPC_MESSAGE_TYPES.KeyboardLedState]: KeyboardLedStateMessage,
|
||||||
[HID_RPC_MESSAGE_TYPES.KeyboardReport]: KeyboardReportMessage,
|
[HID_RPC_MESSAGE_TYPES.KeyboardReport]: KeyboardReportMessage,
|
||||||
[HID_RPC_MESSAGE_TYPES.KeypressReport]: KeypressReportMessage,
|
[HID_RPC_MESSAGE_TYPES.KeypressReport]: KeypressReportMessage,
|
||||||
[HID_RPC_MESSAGE_TYPES.KeyboardMacroReport]: KeyboardMacroReportMessage,
|
[HID_RPC_MESSAGE_TYPES.KeyboardMacroReport]: KeyboardMacroReportMessage,
|
||||||
[HID_RPC_MESSAGE_TYPES.CancelKeyboardMacroReport]: CancelKeyboardMacroReportMessage,
|
[HID_RPC_MESSAGE_TYPES.CancelKeyboardMacroReport]: CancelKeyboardMacroReportMessage,
|
||||||
[HID_RPC_MESSAGE_TYPES.KeyboardMacroState]: KeyboardMacroStateMessage,
|
[HID_RPC_MESSAGE_TYPES.KeyboardMacroState]: KeyboardMacroStateMessage,
|
||||||
[HID_RPC_MESSAGE_TYPES.KeypressKeepAliveReport]: KeypressKeepAliveMessage,
|
[HID_RPC_MESSAGE_TYPES.KeypressKeepAliveReport]: KeypressKeepAliveMessage,
|
||||||
}
|
};
|
||||||
|
|
||||||
export const unmarshalHidRpcMessage = (data: Uint8Array): RpcMessage | undefined => {
|
export const unmarshalHidRpcMessage = (data: Uint8Array): RpcMessage | undefined => {
|
||||||
if (data.length < 1) {
|
if (data.length < 1) {
|
||||||
throw new Error(`Invalid HID RPC message length: ${data.length}`);
|
throw new Error(`Invalid HID RPC message length: ${data.length}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const payload = data.slice(1);
|
const payload = data.slice(1);
|
||||||
|
|
||||||
const messageType = data[0];
|
const messageType = data[0];
|
||||||
if (!(messageType in messageRegistry)) {
|
if (!(messageType in messageRegistry)) {
|
||||||
throw new Error(`Unknown HID RPC message type: ${messageType}`);
|
throw new Error(`Unknown HID RPC message type: ${messageType}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
return messageRegistry[messageType].unmarshal(payload);
|
return messageRegistry[messageType].unmarshal(payload);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,7 @@
|
||||||
import { create } from "zustand";
|
import { create } from "zustand";
|
||||||
import { createJSONStorage, persist } from "zustand/middleware";
|
import { createJSONStorage, persist } from "zustand/middleware";
|
||||||
|
|
||||||
import {
|
import { MAX_STEPS_PER_MACRO, MAX_TOTAL_MACROS, MAX_KEYS_PER_STEP } from "@/constants/macros";
|
||||||
MAX_STEPS_PER_MACRO,
|
|
||||||
MAX_TOTAL_MACROS,
|
|
||||||
MAX_KEYS_PER_STEP,
|
|
||||||
} from "@/constants/macros";
|
|
||||||
|
|
||||||
// Define the JsonRpc types for better type checking
|
// Define the JsonRpc types for better type checking
|
||||||
interface JsonRpcResponse {
|
interface JsonRpcResponse {
|
||||||
|
|
@ -92,8 +88,7 @@ export const useUiStore = create<UIState>(set => ({
|
||||||
setDisableVideoFocusTrap: (enabled: boolean) => set({ disableVideoFocusTrap: enabled }),
|
setDisableVideoFocusTrap: (enabled: boolean) => set({ disableVideoFocusTrap: enabled }),
|
||||||
|
|
||||||
isWakeOnLanModalVisible: false,
|
isWakeOnLanModalVisible: false,
|
||||||
setWakeOnLanModalVisibility: (enabled: boolean) =>
|
setWakeOnLanModalVisibility: (enabled: boolean) => set({ isWakeOnLanModalVisible: enabled }),
|
||||||
set({ isWakeOnLanModalVisible: enabled }),
|
|
||||||
|
|
||||||
toggleSidebarView: view =>
|
toggleSidebarView: view =>
|
||||||
set(state => {
|
set(state => {
|
||||||
|
|
@ -275,12 +270,7 @@ export const useMouseStore = create<MouseState>(set => ({
|
||||||
setMousePosition: (x, y) => set({ mouseX: x, mouseY: y }),
|
setMousePosition: (x, y) => set({ mouseX: x, mouseY: y }),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
export type HdmiStates =
|
export type HdmiStates = "ready" | "no_signal" | "no_lock" | "out_of_range" | "connecting";
|
||||||
| "ready"
|
|
||||||
| "no_signal"
|
|
||||||
| "no_lock"
|
|
||||||
| "out_of_range"
|
|
||||||
| "connecting";
|
|
||||||
export type HdmiErrorStates = Extract<
|
export type HdmiErrorStates = Extract<
|
||||||
VideoState["hdmiState"],
|
VideoState["hdmiState"],
|
||||||
"no_signal" | "no_lock" | "out_of_range"
|
"no_signal" | "no_lock" | "out_of_range"
|
||||||
|
|
@ -310,8 +300,7 @@ export const useVideoStore = create<VideoState>(set => ({
|
||||||
clientHeight: 0,
|
clientHeight: 0,
|
||||||
|
|
||||||
// The video element's client size
|
// The video element's client size
|
||||||
setClientSize: (clientWidth: number, clientHeight: number) =>
|
setClientSize: (clientWidth: number, clientHeight: number) => set({ clientWidth, clientHeight }),
|
||||||
set({ clientWidth, clientHeight }),
|
|
||||||
|
|
||||||
// Resolution
|
// Resolution
|
||||||
setSize: (width: number, height: number) => set({ width, height }),
|
setSize: (width: number, height: number) => set({ width, height }),
|
||||||
|
|
@ -401,8 +390,7 @@ export const useSettingsStore = create(
|
||||||
dim_after: 10000,
|
dim_after: 10000,
|
||||||
off_after: 50000,
|
off_after: 50000,
|
||||||
},
|
},
|
||||||
setBacklightSettings: (settings: BacklightSettings) =>
|
setBacklightSettings: (settings: BacklightSettings) => set({ backlightSettings: settings }),
|
||||||
set({ backlightSettings: settings }),
|
|
||||||
|
|
||||||
keyboardLayout: "en-US",
|
keyboardLayout: "en-US",
|
||||||
setKeyboardLayout: (layout: string) => set({ keyboardLayout: layout }),
|
setKeyboardLayout: (layout: string) => set({ keyboardLayout: layout }),
|
||||||
|
|
@ -493,12 +481,7 @@ export interface KeysDownState {
|
||||||
keys: number[];
|
keys: number[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export type USBStates =
|
export type USBStates = "configured" | "attached" | "not attached" | "suspended" | "addressed";
|
||||||
| "configured"
|
|
||||||
| "attached"
|
|
||||||
| "not attached"
|
|
||||||
| "suspended"
|
|
||||||
| "addressed";
|
|
||||||
|
|
||||||
export interface HidState {
|
export interface HidState {
|
||||||
keyboardLedState: KeyboardLedState;
|
keyboardLedState: KeyboardLedState;
|
||||||
|
|
@ -526,15 +509,13 @@ export const useHidStore = create<HidState>(set => ({
|
||||||
kana: false,
|
kana: false,
|
||||||
shift: false,
|
shift: false,
|
||||||
} as KeyboardLedState,
|
} as KeyboardLedState,
|
||||||
setKeyboardLedState: (ledState: KeyboardLedState): void =>
|
setKeyboardLedState: (ledState: KeyboardLedState): void => set({ keyboardLedState: ledState }),
|
||||||
set({ keyboardLedState: ledState }),
|
|
||||||
|
|
||||||
keysDownState: { modifier: 0, keys: [0, 0, 0, 0, 0, 0] } as KeysDownState,
|
keysDownState: { modifier: 0, keys: [0, 0, 0, 0, 0, 0] } as KeysDownState,
|
||||||
setKeysDownState: (state: KeysDownState): void => set({ keysDownState: state }),
|
setKeysDownState: (state: KeysDownState): void => set({ keysDownState: state }),
|
||||||
|
|
||||||
isVirtualKeyboardEnabled: false,
|
isVirtualKeyboardEnabled: false,
|
||||||
setVirtualKeyboardEnabled: (enabled: boolean): void =>
|
setVirtualKeyboardEnabled: (enabled: boolean): void => set({ isVirtualKeyboardEnabled: enabled }),
|
||||||
set({ isVirtualKeyboardEnabled: enabled }),
|
|
||||||
|
|
||||||
isPasteInProgress: false,
|
isPasteInProgress: false,
|
||||||
setPasteModeEnabled: (enabled: boolean): void => set({ isPasteInProgress: enabled }),
|
setPasteModeEnabled: (enabled: boolean): void => set({ isPasteInProgress: enabled }),
|
||||||
|
|
@ -641,8 +622,7 @@ export const useUpdateStore = create<UpdateState>(set => ({
|
||||||
setModalView: (view: UpdateModalViews) => set({ modalView: view }),
|
setModalView: (view: UpdateModalViews) => set({ modalView: view }),
|
||||||
|
|
||||||
updateErrorMessage: null,
|
updateErrorMessage: null,
|
||||||
setUpdateErrorMessage: (errorMessage: string) =>
|
setUpdateErrorMessage: (errorMessage: string) => set({ updateErrorMessage: errorMessage }),
|
||||||
set({ updateErrorMessage: errorMessage }),
|
|
||||||
|
|
||||||
shouldReload: false,
|
shouldReload: false,
|
||||||
setShouldReload: (reloadRequired: boolean) => set({ shouldReload: reloadRequired }),
|
setShouldReload: (reloadRequired: boolean) => set({ shouldReload: reloadRequired }),
|
||||||
|
|
@ -791,12 +771,7 @@ export type IPv6Mode =
|
||||||
export type IPv4Mode = "disabled" | "static" | "dhcp" | "unknown";
|
export type IPv4Mode = "disabled" | "static" | "dhcp" | "unknown";
|
||||||
export type LLDPMode = "disabled" | "basic" | "all" | "unknown";
|
export type LLDPMode = "disabled" | "basic" | "all" | "unknown";
|
||||||
export type mDNSMode = "disabled" | "auto" | "ipv4_only" | "ipv6_only" | "unknown";
|
export type mDNSMode = "disabled" | "auto" | "ipv4_only" | "ipv6_only" | "unknown";
|
||||||
export type TimeSyncMode =
|
export type TimeSyncMode = "ntp_only" | "ntp_and_http" | "http_only" | "custom" | "unknown";
|
||||||
| "ntp_only"
|
|
||||||
| "ntp_and_http"
|
|
||||||
| "http_only"
|
|
||||||
| "custom"
|
|
||||||
| "unknown";
|
|
||||||
|
|
||||||
export interface IPv4StaticConfig {
|
export interface IPv4StaticConfig {
|
||||||
address: string;
|
address: string;
|
||||||
|
|
@ -861,12 +836,12 @@ export interface MacrosState {
|
||||||
loadMacros: () => Promise<void>;
|
loadMacros: () => Promise<void>;
|
||||||
saveMacros: (macros: KeySequence[]) => Promise<void>;
|
saveMacros: (macros: KeySequence[]) => Promise<void>;
|
||||||
sendFn:
|
sendFn:
|
||||||
| ((
|
| ((
|
||||||
method: string,
|
method: string,
|
||||||
params: unknown,
|
params: unknown,
|
||||||
callback?: ((resp: JsonRpcResponse) => void) | undefined,
|
callback?: ((resp: JsonRpcResponse) => void) | undefined,
|
||||||
) => void)
|
) => void)
|
||||||
| null;
|
| null;
|
||||||
setSendFn: (
|
setSendFn: (
|
||||||
sendFn: (
|
sendFn: (
|
||||||
method: string,
|
method: string,
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,8 @@ export function useHidRpc(onHidRpcMessage?: (payload: RpcMessage) => void) {
|
||||||
rpcHidUnreliableChannel,
|
rpcHidUnreliableChannel,
|
||||||
rpcHidUnreliableNonOrderedChannel,
|
rpcHidUnreliableNonOrderedChannel,
|
||||||
setRpcHidProtocolVersion,
|
setRpcHidProtocolVersion,
|
||||||
rpcHidProtocolVersion, hidRpcDisabled,
|
rpcHidProtocolVersion,
|
||||||
|
hidRpcDisabled,
|
||||||
} = useRTCStore();
|
} = useRTCStore();
|
||||||
|
|
||||||
const rpcHidReady = useMemo(() => {
|
const rpcHidReady = useMemo(() => {
|
||||||
|
|
@ -40,15 +41,12 @@ export function useHidRpc(onHidRpcMessage?: (payload: RpcMessage) => void) {
|
||||||
}, [rpcHidChannel, rpcHidProtocolVersion, hidRpcDisabled]);
|
}, [rpcHidChannel, rpcHidProtocolVersion, hidRpcDisabled]);
|
||||||
|
|
||||||
const rpcHidUnreliableReady = useMemo(() => {
|
const rpcHidUnreliableReady = useMemo(() => {
|
||||||
return (
|
return rpcHidUnreliableChannel?.readyState === "open" && rpcHidProtocolVersion !== null;
|
||||||
rpcHidUnreliableChannel?.readyState === "open" && rpcHidProtocolVersion !== null
|
|
||||||
);
|
|
||||||
}, [rpcHidProtocolVersion, rpcHidUnreliableChannel?.readyState]);
|
}, [rpcHidProtocolVersion, rpcHidUnreliableChannel?.readyState]);
|
||||||
|
|
||||||
const rpcHidUnreliableNonOrderedReady = useMemo(() => {
|
const rpcHidUnreliableNonOrderedReady = useMemo(() => {
|
||||||
return (
|
return (
|
||||||
rpcHidUnreliableNonOrderedChannel?.readyState === "open" &&
|
rpcHidUnreliableNonOrderedChannel?.readyState === "open" && rpcHidProtocolVersion !== null
|
||||||
rpcHidProtocolVersion !== null
|
|
||||||
);
|
);
|
||||||
}, [rpcHidProtocolVersion, rpcHidUnreliableNonOrderedChannel?.readyState]);
|
}, [rpcHidProtocolVersion, rpcHidUnreliableNonOrderedChannel?.readyState]);
|
||||||
|
|
||||||
|
|
@ -64,11 +62,7 @@ export function useHidRpc(onHidRpcMessage?: (payload: RpcMessage) => void) {
|
||||||
const sendMessage = useCallback(
|
const sendMessage = useCallback(
|
||||||
(
|
(
|
||||||
message: RpcMessage,
|
message: RpcMessage,
|
||||||
{
|
{ ignoreHandshakeState, useUnreliableChannel, requireOrdered = true }: sendMessageParams = {},
|
||||||
ignoreHandshakeState,
|
|
||||||
useUnreliableChannel,
|
|
||||||
requireOrdered = true,
|
|
||||||
}: sendMessageParams = {},
|
|
||||||
) => {
|
) => {
|
||||||
if (hidRpcDisabled) return;
|
if (hidRpcDisabled) return;
|
||||||
if (rpcHidChannel?.readyState !== "open") return;
|
if (rpcHidChannel?.readyState !== "open") return;
|
||||||
|
|
@ -96,7 +90,8 @@ export function useHidRpc(onHidRpcMessage?: (payload: RpcMessage) => void) {
|
||||||
[
|
[
|
||||||
rpcHidChannel,
|
rpcHidChannel,
|
||||||
rpcHidUnreliableChannel,
|
rpcHidUnreliableChannel,
|
||||||
hidRpcDisabled, rpcHidUnreliableNonOrderedChannel,
|
hidRpcDisabled,
|
||||||
|
rpcHidUnreliableNonOrderedChannel,
|
||||||
rpcHidReady,
|
rpcHidReady,
|
||||||
rpcHidUnreliableReady,
|
rpcHidUnreliableReady,
|
||||||
rpcHidUnreliableNonOrderedReady,
|
rpcHidUnreliableNonOrderedReady,
|
||||||
|
|
@ -140,12 +135,9 @@ export function useHidRpc(onHidRpcMessage?: (payload: RpcMessage) => void) {
|
||||||
[sendMessage],
|
[sendMessage],
|
||||||
);
|
);
|
||||||
|
|
||||||
const cancelOngoingKeyboardMacro = useCallback(
|
const cancelOngoingKeyboardMacro = useCallback(() => {
|
||||||
() => {
|
sendMessage(new CancelKeyboardMacroReportMessage());
|
||||||
sendMessage(new CancelKeyboardMacroReportMessage());
|
}, [sendMessage]);
|
||||||
},
|
|
||||||
[sendMessage],
|
|
||||||
);
|
|
||||||
|
|
||||||
const reportKeypressKeepAlive = useCallback(() => {
|
const reportKeypressKeepAlive = useCallback(() => {
|
||||||
sendMessage(KEEPALIVE_MESSAGE);
|
sendMessage(KEEPALIVE_MESSAGE);
|
||||||
|
|
@ -224,7 +216,7 @@ export function useHidRpc(onHidRpcMessage?: (payload: RpcMessage) => void) {
|
||||||
};
|
};
|
||||||
|
|
||||||
const errorHandler = (e: Event) => {
|
const errorHandler = (e: Event) => {
|
||||||
console.error(`Error on rpcHidChannel '${rpcHidChannel.label}': ${e}`)
|
console.error(`Error on rpcHidChannel '${rpcHidChannel.label}': ${e}`);
|
||||||
};
|
};
|
||||||
|
|
||||||
rpcHidChannel.addEventListener("message", messageHandler);
|
rpcHidChannel.addEventListener("message", messageHandler);
|
||||||
|
|
|
||||||
|
|
@ -37,14 +37,14 @@ let requestCounter = 0;
|
||||||
// Map of blocked RPC methods by failsafe reason
|
// Map of blocked RPC methods by failsafe reason
|
||||||
const blockedMethodsByReason: Record<string, string[]> = {
|
const blockedMethodsByReason: Record<string, string[]> = {
|
||||||
video: [
|
video: [
|
||||||
'setStreamQualityFactor',
|
"setStreamQualityFactor",
|
||||||
'getEDID',
|
"getEDID",
|
||||||
'setEDID',
|
"setEDID",
|
||||||
'getVideoLogStatus',
|
"getVideoLogStatus",
|
||||||
'setDisplayRotation',
|
"setDisplayRotation",
|
||||||
'getVideoSleepMode',
|
"getVideoSleepMode",
|
||||||
'setVideoSleepMode',
|
"setVideoSleepMode",
|
||||||
'getVideoState',
|
"getVideoState",
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -86,7 +86,7 @@ export function useJsonRpc(onRequest?: (payload: JsonRpcRequest) => void) {
|
||||||
|
|
||||||
rpcDataChannel.send(JSON.stringify(payload));
|
rpcDataChannel.send(JSON.stringify(payload));
|
||||||
},
|
},
|
||||||
[rpcDataChannel, isFailsafeMode, reason]
|
[rpcDataChannel, isFailsafeMode, reason],
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
@ -117,8 +117,7 @@ export function useJsonRpc(onRequest?: (payload: JsonRpcRequest) => void) {
|
||||||
return () => {
|
return () => {
|
||||||
rpcDataChannel.removeEventListener("message", messageHandler);
|
rpcDataChannel.removeEventListener("message", messageHandler);
|
||||||
};
|
};
|
||||||
},
|
}, [rpcDataChannel, onRequest]);
|
||||||
[rpcDataChannel, onRequest]);
|
|
||||||
|
|
||||||
return { send };
|
return { send };
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -197,9 +197,7 @@ export default function useKeyboard() {
|
||||||
// If we reach here it means we didn't find an empty slot or the key in the buffer
|
// If we reach here it means we didn't find an empty slot or the key in the buffer
|
||||||
if (overrun) {
|
if (overrun) {
|
||||||
if (press) {
|
if (press) {
|
||||||
console.warn(
|
console.warn(`keyboard buffer overflow current keys ${keys}, key: ${key} not added`);
|
||||||
`keyboard buffer overflow current keys ${keys}, key: ${key} not added`,
|
|
||||||
);
|
|
||||||
// Fill all key slots with ErrorRollOver (0x01) to indicate overflow
|
// Fill all key slots with ErrorRollOver (0x01) to indicate overflow
|
||||||
keys.length = hidKeyBufferSize;
|
keys.length = hidKeyBufferSize;
|
||||||
keys.fill(hidErrorRollOver);
|
keys.fill(hidErrorRollOver);
|
||||||
|
|
@ -248,11 +246,7 @@ export default function useKeyboard() {
|
||||||
// Older backends don't support the hidRpc API, so we need:
|
// Older backends don't support the hidRpc API, so we need:
|
||||||
// 1. Calculate the state
|
// 1. Calculate the state
|
||||||
// 2. Send the newly calculated state to the device
|
// 2. Send the newly calculated state to the device
|
||||||
const downState = simulateDeviceSideKeyHandlingForLegacyDevices(
|
const downState = simulateDeviceSideKeyHandlingForLegacyDevices(keysDownState, key, press);
|
||||||
keysDownState,
|
|
||||||
key,
|
|
||||||
press,
|
|
||||||
);
|
|
||||||
|
|
||||||
handleLegacyKeyboardReport(downState.keys, downState.modifier);
|
handleLegacyKeyboardReport(downState.keys, downState.modifier);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,17 +7,17 @@ export default function useKeyboardLayout() {
|
||||||
const { keyboardLayout } = useSettingsStore();
|
const { keyboardLayout } = useSettingsStore();
|
||||||
|
|
||||||
const keyboardOptions = useMemo(() => {
|
const keyboardOptions = useMemo(() => {
|
||||||
return keyboards.map((keyboard) => {
|
return keyboards.map(keyboard => {
|
||||||
return { label: keyboard.name, value: keyboard.isoCode }
|
return { label: keyboard.name, value: keyboard.isoCode };
|
||||||
});
|
});
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const isoCode = useMemo(() => {
|
const isoCode = useMemo(() => {
|
||||||
// If we don't have a specific layout, default to "en-US" because that was the original layout
|
// If we don't have a specific layout, default to "en-US" because that was the original layout
|
||||||
// developed so it is a good fallback. Additionally, we replace "en_US" with "en-US" because
|
// developed so it is a good fallback. Additionally, we replace "en_US" with "en-US" because
|
||||||
// the original server-side code used "en_US" as the default value, but that's not the correct
|
// the original server-side code used "en_US" as the default value, but that's not the correct
|
||||||
// ISO code for English/United State. To ensure we remain backward compatible with devices that
|
// ISO code for English/United State. To ensure we remain backward compatible with devices that
|
||||||
// have not had their Keyboard Layout selected by the user, we want to treat "en_US" as if it was
|
// have not had their Keyboard Layout selected by the user, we want to treat "en_US" as if it was
|
||||||
// "en-US" to match the ISO standard codes now used in the keyboardLayouts.
|
// "en-US" to match the ISO standard codes now used in the keyboardLayouts.
|
||||||
console.debug("Current keyboard layout from store:", keyboardLayout);
|
console.debug("Current keyboard layout from store:", keyboardLayout);
|
||||||
if (keyboardLayout && keyboardLayout.length > 0)
|
if (keyboardLayout && keyboardLayout.length > 0)
|
||||||
|
|
@ -27,9 +27,11 @@ export default function useKeyboardLayout() {
|
||||||
|
|
||||||
const selectedKeyboard = useMemo(() => {
|
const selectedKeyboard = useMemo(() => {
|
||||||
// fallback to original behaviour of en-US if no isoCode given or matching layout not found
|
// fallback to original behaviour of en-US if no isoCode given or matching layout not found
|
||||||
return keyboards.find(keyboard => keyboard.isoCode === isoCode)
|
return (
|
||||||
?? keyboards.find(keyboard => keyboard.isoCode === "en-US")!;
|
keyboards.find(keyboard => keyboard.isoCode === isoCode) ??
|
||||||
|
keyboards.find(keyboard => keyboard.isoCode === "en-US")!
|
||||||
|
);
|
||||||
}, [isoCode]);
|
}, [isoCode]);
|
||||||
|
|
||||||
return { keyboardOptions, isoCode, selectedKeyboard };
|
return { keyboardOptions, isoCode, selectedKeyboard };
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -40,13 +40,7 @@ export default function useMouse() {
|
||||||
}
|
}
|
||||||
setMouseMove({ x, y, buttons });
|
setMouseMove({ x, y, buttons });
|
||||||
},
|
},
|
||||||
[
|
[send, reportRelMouseEvent, setMouseMove, mouseMode, rpcHidReady],
|
||||||
send,
|
|
||||||
reportRelMouseEvent,
|
|
||||||
setMouseMove,
|
|
||||||
mouseMode,
|
|
||||||
rpcHidReady,
|
|
||||||
],
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const getRelMouseMoveHandler = useCallback(
|
const getRelMouseMoveHandler = useCallback(
|
||||||
|
|
@ -72,56 +66,52 @@ export default function useMouse() {
|
||||||
// We set that for the debug info bar
|
// We set that for the debug info bar
|
||||||
setMousePosition(x, y);
|
setMousePosition(x, y);
|
||||||
},
|
},
|
||||||
[
|
[send, reportAbsMouseEvent, setMousePosition, mouseMode, rpcHidReady],
|
||||||
send,
|
|
||||||
reportAbsMouseEvent,
|
|
||||||
setMousePosition,
|
|
||||||
mouseMode,
|
|
||||||
rpcHidReady,
|
|
||||||
],
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const getAbsMouseMoveHandler = useCallback(
|
const getAbsMouseMoveHandler = useCallback(
|
||||||
({ videoClientWidth, videoClientHeight, videoWidth, videoHeight }: AbsMouseMoveHandlerProps) => (e: MouseEvent) => {
|
({ videoClientWidth, videoClientHeight, videoWidth, videoHeight }: AbsMouseMoveHandlerProps) =>
|
||||||
if (!videoClientWidth || !videoClientHeight) return;
|
(e: MouseEvent) => {
|
||||||
if (mouseMode !== "absolute") return;
|
if (!videoClientWidth || !videoClientHeight) return;
|
||||||
|
if (mouseMode !== "absolute") return;
|
||||||
|
|
||||||
// Get the aspect ratios of the video element and the video stream
|
// Get the aspect ratios of the video element and the video stream
|
||||||
const videoElementAspectRatio = videoClientWidth / videoClientHeight;
|
const videoElementAspectRatio = videoClientWidth / videoClientHeight;
|
||||||
const videoStreamAspectRatio = videoWidth / videoHeight;
|
const videoStreamAspectRatio = videoWidth / videoHeight;
|
||||||
|
|
||||||
// Calculate the effective video display area
|
// Calculate the effective video display area
|
||||||
let effectiveWidth = videoClientWidth;
|
let effectiveWidth = videoClientWidth;
|
||||||
let effectiveHeight = videoClientHeight;
|
let effectiveHeight = videoClientHeight;
|
||||||
let offsetX = 0;
|
let offsetX = 0;
|
||||||
let offsetY = 0;
|
let offsetY = 0;
|
||||||
|
|
||||||
if (videoElementAspectRatio > videoStreamAspectRatio) {
|
if (videoElementAspectRatio > videoStreamAspectRatio) {
|
||||||
// Pillarboxing: black bars on the left and right
|
// Pillarboxing: black bars on the left and right
|
||||||
effectiveWidth = videoClientHeight * videoStreamAspectRatio;
|
effectiveWidth = videoClientHeight * videoStreamAspectRatio;
|
||||||
offsetX = (videoClientWidth - effectiveWidth) / 2;
|
offsetX = (videoClientWidth - effectiveWidth) / 2;
|
||||||
} else if (videoElementAspectRatio < videoStreamAspectRatio) {
|
} else if (videoElementAspectRatio < videoStreamAspectRatio) {
|
||||||
// Letterboxing: black bars on the top and bottom
|
// Letterboxing: black bars on the top and bottom
|
||||||
effectiveHeight = videoClientWidth / videoStreamAspectRatio;
|
effectiveHeight = videoClientWidth / videoStreamAspectRatio;
|
||||||
offsetY = (videoClientHeight - effectiveHeight) / 2;
|
offsetY = (videoClientHeight - effectiveHeight) / 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clamp mouse position within the effective video boundaries
|
// Clamp mouse position within the effective video boundaries
|
||||||
const clampedX = Math.min(Math.max(offsetX, e.offsetX), offsetX + effectiveWidth);
|
const clampedX = Math.min(Math.max(offsetX, e.offsetX), offsetX + effectiveWidth);
|
||||||
const clampedY = Math.min(Math.max(offsetY, e.offsetY), offsetY + effectiveHeight);
|
const clampedY = Math.min(Math.max(offsetY, e.offsetY), offsetY + effectiveHeight);
|
||||||
|
|
||||||
// Map clamped mouse position to the video stream's coordinate system
|
// Map clamped mouse position to the video stream's coordinate system
|
||||||
const relativeX = (clampedX - offsetX) / effectiveWidth;
|
const relativeX = (clampedX - offsetX) / effectiveWidth;
|
||||||
const relativeY = (clampedY - offsetY) / effectiveHeight;
|
const relativeY = (clampedY - offsetY) / effectiveHeight;
|
||||||
|
|
||||||
// Convert to HID absolute coordinate system (0-32767 range)
|
// Convert to HID absolute coordinate system (0-32767 range)
|
||||||
const x = Math.round(relativeX * 32767);
|
const x = Math.round(relativeX * 32767);
|
||||||
const y = Math.round(relativeY * 32767);
|
const y = Math.round(relativeY * 32767);
|
||||||
|
|
||||||
// Send mouse movement
|
// Send mouse movement
|
||||||
const { buttons } = e;
|
const { buttons } = e;
|
||||||
sendAbsMouseMovement(x, y, buttons);
|
sendAbsMouseMovement(x, y, buttons);
|
||||||
}, [mouseMode, sendAbsMouseMovement],
|
},
|
||||||
|
[mouseMode, sendAbsMouseMovement],
|
||||||
);
|
);
|
||||||
|
|
||||||
const getMouseWheelHandler = useCallback(
|
const getMouseWheelHandler = useCallback(
|
||||||
|
|
@ -169,4 +159,4 @@ export default function useMouse() {
|
||||||
getMouseWheelHandler,
|
getMouseWheelHandler,
|
||||||
resetMousePosition,
|
resetMousePosition,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,12 +20,7 @@ export interface SystemVersionInfo {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useVersion() {
|
export function useVersion() {
|
||||||
const {
|
const { appVersion, systemVersion, setAppVersion, setSystemVersion } = useDeviceStore();
|
||||||
appVersion,
|
|
||||||
systemVersion,
|
|
||||||
setAppVersion,
|
|
||||||
setSystemVersion,
|
|
||||||
} = useDeviceStore();
|
|
||||||
|
|
||||||
const getVersionInfo = useCallback(async () => {
|
const getVersionInfo = useCallback(async () => {
|
||||||
try {
|
try {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,16 @@
|
||||||
export interface KeyStroke { modifier: number; keys: number[]; }
|
export interface KeyStroke {
|
||||||
export interface KeyInfo { key: string | number; shift?: boolean, altRight?: boolean }
|
modifier: number;
|
||||||
export interface KeyCombo extends KeyInfo { deadKey?: boolean, accentKey?: KeyInfo }
|
keys: number[];
|
||||||
|
}
|
||||||
|
export interface KeyInfo {
|
||||||
|
key: string | number;
|
||||||
|
shift?: boolean;
|
||||||
|
altRight?: boolean;
|
||||||
|
}
|
||||||
|
export interface KeyCombo extends KeyInfo {
|
||||||
|
deadKey?: boolean;
|
||||||
|
accentKey?: KeyInfo;
|
||||||
|
}
|
||||||
export interface KeyboardLayout {
|
export interface KeyboardLayout {
|
||||||
isoCode: string;
|
isoCode: string;
|
||||||
name: string;
|
name: string;
|
||||||
|
|
@ -8,25 +18,39 @@ export interface KeyboardLayout {
|
||||||
modifierDisplayMap: Record<string, string>;
|
modifierDisplayMap: Record<string, string>;
|
||||||
keyDisplayMap: Record<string, string>;
|
keyDisplayMap: Record<string, string>;
|
||||||
virtualKeyboard: {
|
virtualKeyboard: {
|
||||||
main: { default: string[], shift: string[] },
|
main: { default: string[]; shift: string[] };
|
||||||
control?: { default: string[], shift?: string[] },
|
control?: { default: string[]; shift?: string[] };
|
||||||
arrows?: { default: string[] }
|
arrows?: { default: string[] };
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// To add a new layout, create a file like the above and add it to the list
|
// To add a new layout, create a file like the above and add it to the list
|
||||||
import { cs_CZ } from "@/keyboardLayouts/cs_CZ"
|
import { cs_CZ } from "@/keyboardLayouts/cs_CZ";
|
||||||
import { de_CH } from "@/keyboardLayouts/de_CH"
|
import { de_CH } from "@/keyboardLayouts/de_CH";
|
||||||
import { de_DE } from "@/keyboardLayouts/de_DE"
|
import { de_DE } from "@/keyboardLayouts/de_DE";
|
||||||
import { en_US } from "@/keyboardLayouts/en_US"
|
import { en_US } from "@/keyboardLayouts/en_US";
|
||||||
import { en_UK } from "@/keyboardLayouts/en_UK"
|
import { en_UK } from "@/keyboardLayouts/en_UK";
|
||||||
import { es_ES } from "@/keyboardLayouts/es_ES"
|
import { es_ES } from "@/keyboardLayouts/es_ES";
|
||||||
import { fr_BE } from "@/keyboardLayouts/fr_BE"
|
import { fr_BE } from "@/keyboardLayouts/fr_BE";
|
||||||
import { fr_CH } from "@/keyboardLayouts/fr_CH"
|
import { fr_CH } from "@/keyboardLayouts/fr_CH";
|
||||||
import { fr_FR } from "@/keyboardLayouts/fr_FR"
|
import { fr_FR } from "@/keyboardLayouts/fr_FR";
|
||||||
import { it_IT } from "@/keyboardLayouts/it_IT"
|
import { it_IT } from "@/keyboardLayouts/it_IT";
|
||||||
import { nb_NO } from "@/keyboardLayouts/nb_NO"
|
import { nb_NO } from "@/keyboardLayouts/nb_NO";
|
||||||
import { sv_SE } from "@/keyboardLayouts/sv_SE"
|
import { sv_SE } from "@/keyboardLayouts/sv_SE";
|
||||||
import { da_DK } from "@/keyboardLayouts/da_DK"
|
import { da_DK } from "@/keyboardLayouts/da_DK";
|
||||||
|
|
||||||
export const keyboards: KeyboardLayout[] = [cs_CZ, de_CH, de_DE, en_UK, en_US, es_ES, fr_BE, fr_CH, fr_FR, it_IT, nb_NO, sv_SE, da_DK];
|
export const keyboards: KeyboardLayout[] = [
|
||||||
|
cs_CZ,
|
||||||
|
de_CH,
|
||||||
|
de_DE,
|
||||||
|
en_UK,
|
||||||
|
en_US,
|
||||||
|
es_ES,
|
||||||
|
fr_BE,
|
||||||
|
fr_CH,
|
||||||
|
fr_FR,
|
||||||
|
it_IT,
|
||||||
|
nb_NO,
|
||||||
|
sv_SE,
|
||||||
|
da_DK,
|
||||||
|
];
|
||||||
|
|
|
||||||
|
|
@ -1,231 +1,231 @@
|
||||||
import { KeyboardLayout, KeyCombo } from "../keyboardLayouts"
|
import { KeyboardLayout, KeyCombo } from "../keyboardLayouts";
|
||||||
|
|
||||||
import { en_US } from "./en_US" // for fallback of keyDisplayMap, modifierDisplayMap, and virtualKeyboard
|
import { en_US } from "./en_US"; // for fallback of keyDisplayMap, modifierDisplayMap, and virtualKeyboard
|
||||||
|
|
||||||
const name = "Čeština";
|
const name = "Čeština";
|
||||||
const isoCode = "cs-CZ";
|
const isoCode = "cs-CZ";
|
||||||
|
|
||||||
const keyTrema: KeyCombo = { key: "Backslash" } // tréma (umlaut), two dots placed above a vowel
|
const keyTrema: KeyCombo = { key: "Backslash" }; // tréma (umlaut), two dots placed above a vowel
|
||||||
const keyAcute: KeyCombo = { key: "Equal" } // accent aigu (acute accent), mark ´ placed above the letter
|
const keyAcute: KeyCombo = { key: "Equal" }; // accent aigu (acute accent), mark ´ placed above the letter
|
||||||
const keyHat: KeyCombo = { key: "Digit3", shift: true, altRight: true } // accent circonflexe (accent hat), mark ^ placed above the letter
|
const keyHat: KeyCombo = { key: "Digit3", shift: true, altRight: true }; // accent circonflexe (accent hat), mark ^ placed above the letter
|
||||||
const keyCaron: KeyCombo = { key: "Equal", shift: true } // caron or haček (inverted hat), mark ˇ placed above the letter
|
const keyCaron: KeyCombo = { key: "Equal", shift: true }; // caron or haček (inverted hat), mark ˇ placed above the letter
|
||||||
const keyGrave: KeyCombo = { key: "Digit7", shift: true, altRight: true } // accent grave, mark ` placed above the letter
|
const keyGrave: KeyCombo = { key: "Digit7", shift: true, altRight: true }; // accent grave, mark ` placed above the letter
|
||||||
const keyTilde: KeyCombo = { key: "Digit1", shift: true, altRight: true } // tilde, mark ~ placed above the letter
|
const keyTilde: KeyCombo = { key: "Digit1", shift: true, altRight: true }; // tilde, mark ~ placed above the letter
|
||||||
const keyRing: KeyCombo = { key: "Backquote", shift: true } // kroužek (little ring), mark ° placed above the letter
|
const keyRing: KeyCombo = { key: "Backquote", shift: true }; // kroužek (little ring), mark ° placed above the letter
|
||||||
const keyOverdot: KeyCombo = { key: "Digit8", shift: true, altRight: true } // overdot (dot above), mark ˙ placed above the letter
|
const keyOverdot: KeyCombo = { key: "Digit8", shift: true, altRight: true }; // overdot (dot above), mark ˙ placed above the letter
|
||||||
const keyHook: KeyCombo = { key: "Digit6", shift: true, altRight: true } // ogonoek (little hook), mark ˛ placed beneath a letter
|
const keyHook: KeyCombo = { key: "Digit6", shift: true, altRight: true }; // ogonoek (little hook), mark ˛ placed beneath a letter
|
||||||
const keyCedille: KeyCombo = { key: "Equal", shift: true, altRight: true } // accent cedille (cedilla), mark ¸ placed beneath a letter
|
const keyCedille: KeyCombo = { key: "Equal", shift: true, altRight: true }; // accent cedille (cedilla), mark ¸ placed beneath a letter
|
||||||
|
|
||||||
const chars = {
|
const chars = {
|
||||||
A: { key: "KeyA", shift: true },
|
A: { key: "KeyA", shift: true },
|
||||||
"Ä": { key: "KeyA", shift: true, accentKey: keyTrema },
|
Ä: { key: "KeyA", shift: true, accentKey: keyTrema },
|
||||||
"Á": { key: "KeyA", shift: true, accentKey: keyAcute },
|
Á: { key: "KeyA", shift: true, accentKey: keyAcute },
|
||||||
"Â": { key: "KeyA", shift: true, accentKey: keyHat },
|
Â: { key: "KeyA", shift: true, accentKey: keyHat },
|
||||||
"À": { key: "KeyA", shift: true, accentKey: keyGrave },
|
À: { key: "KeyA", shift: true, accentKey: keyGrave },
|
||||||
"Ã": { key: "KeyA", shift: true, accentKey: keyTilde },
|
Ã: { key: "KeyA", shift: true, accentKey: keyTilde },
|
||||||
"Ȧ": { key: "KeyA", shift: true, accentKey: keyOverdot },
|
Ȧ: { key: "KeyA", shift: true, accentKey: keyOverdot },
|
||||||
"Ą": { key: "KeyA", shift: true, accentKey: keyHook },
|
Ą: { key: "KeyA", shift: true, accentKey: keyHook },
|
||||||
B: { key: "KeyB", shift: true },
|
B: { key: "KeyB", shift: true },
|
||||||
"Ḃ": { key: "KeyB", shift: true, accentKEy: keyOverdot },
|
Ḃ: { key: "KeyB", shift: true, accentKEy: keyOverdot },
|
||||||
C: { key: "KeyC", shift: true },
|
C: { key: "KeyC", shift: true },
|
||||||
"Č": { key: "KeyC", shift: true, accentKey: keyCaron },
|
Č: { key: "KeyC", shift: true, accentKey: keyCaron },
|
||||||
"Ċ": { key: "KeyC", shift: true, accentKey: keyOverdot },
|
Ċ: { key: "KeyC", shift: true, accentKey: keyOverdot },
|
||||||
"Ç": { key: "KeyC", shift: true, accentKey: keyCedille },
|
Ç: { key: "KeyC", shift: true, accentKey: keyCedille },
|
||||||
D: { key: "KeyD", shift: true },
|
D: { key: "KeyD", shift: true },
|
||||||
"Ď": { key: "KeyD", shift: true, accentKey: keyCaron },
|
Ď: { key: "KeyD", shift: true, accentKey: keyCaron },
|
||||||
"Ḋ": { key: "KeyD", shift: true, accentKey: keyOverdot },
|
Ḋ: { key: "KeyD", shift: true, accentKey: keyOverdot },
|
||||||
E: { key: "KeyE", shift: true },
|
E: { key: "KeyE", shift: true },
|
||||||
"Ë": { key: "KeyE", shift: true, accentKey: keyTrema },
|
Ë: { key: "KeyE", shift: true, accentKey: keyTrema },
|
||||||
"É": { key: "KeyE", shift: true, accentKey: keyAcute },
|
É: { key: "KeyE", shift: true, accentKey: keyAcute },
|
||||||
"Ê": { key: "KeyE", shift: true, accentKey: keyHat },
|
Ê: { key: "KeyE", shift: true, accentKey: keyHat },
|
||||||
"Ě": { key: "KeyE", shift: true, accentKey: keyCaron },
|
Ě: { key: "KeyE", shift: true, accentKey: keyCaron },
|
||||||
"È": { key: "KeyE", shift: true, accentKey: keyGrave },
|
È: { key: "KeyE", shift: true, accentKey: keyGrave },
|
||||||
"Ẽ": { key: "KeyE", shift: true, accentKey: keyTilde },
|
Ẽ: { key: "KeyE", shift: true, accentKey: keyTilde },
|
||||||
"Ė": { key: "KeyE", shift: true, accentKEy: keyOverdot },
|
Ė: { key: "KeyE", shift: true, accentKEy: keyOverdot },
|
||||||
"Ę": { key: "KeyE", shift: true, accentKey: keyHook },
|
Ę: { key: "KeyE", shift: true, accentKey: keyHook },
|
||||||
F: { key: "KeyF", shift: true },
|
F: { key: "KeyF", shift: true },
|
||||||
"Ḟ": { key: "KeyF", shift: true, accentKey: keyOverdot },
|
Ḟ: { key: "KeyF", shift: true, accentKey: keyOverdot },
|
||||||
G: { key: "KeyG", shift: true },
|
G: { key: "KeyG", shift: true },
|
||||||
"Ġ": { key: "KeyG", shift: true, accentKey: keyOverdot },
|
Ġ: { key: "KeyG", shift: true, accentKey: keyOverdot },
|
||||||
H: { key: "KeyH", shift: true },
|
H: { key: "KeyH", shift: true },
|
||||||
"Ḣ": { key: "KeyH", shift: true, accentKey: keyOverdot },
|
Ḣ: { key: "KeyH", shift: true, accentKey: keyOverdot },
|
||||||
I: { key: "KeyI", shift: true },
|
I: { key: "KeyI", shift: true },
|
||||||
"Ï": { key: "KeyI", shift: true, accentKey: keyTrema },
|
Ï: { key: "KeyI", shift: true, accentKey: keyTrema },
|
||||||
"Í": { key: "KeyI", shift: true, accentKey: keyAcute },
|
Í: { key: "KeyI", shift: true, accentKey: keyAcute },
|
||||||
"Î": { key: "KeyI", shift: true, accentKey: keyHat },
|
Î: { key: "KeyI", shift: true, accentKey: keyHat },
|
||||||
"Ì": { key: "KeyI", shift: true, accentKey: keyGrave },
|
Ì: { key: "KeyI", shift: true, accentKey: keyGrave },
|
||||||
"Ĩ": { key: "KeyI", shift: true, accentKey: keyTilde },
|
Ĩ: { key: "KeyI", shift: true, accentKey: keyTilde },
|
||||||
"İ": { key: "KeyI", shift: true, accentKey: keyOverdot },
|
İ: { key: "KeyI", shift: true, accentKey: keyOverdot },
|
||||||
"Į": { key: "KeyI", shift: true, accentKey: keyHook },
|
Į: { key: "KeyI", shift: true, accentKey: keyHook },
|
||||||
J: { key: "KeyJ", shift: true },
|
J: { key: "KeyJ", shift: true },
|
||||||
K: { key: "KeyK", shift: true },
|
K: { key: "KeyK", shift: true },
|
||||||
L: { key: "KeyL", shift: true },
|
L: { key: "KeyL", shift: true },
|
||||||
"Ŀ": { key: "KeyL", shift: true },
|
Ŀ: { key: "KeyL", shift: true },
|
||||||
M: { key: "KeyM", shift: true },
|
M: { key: "KeyM", shift: true },
|
||||||
"Ṁ": { key: "KeyM", shift: true },
|
Ṁ: { key: "KeyM", shift: true },
|
||||||
N: { key: "KeyN", shift: true },
|
N: { key: "KeyN", shift: true },
|
||||||
"Ň": { key: "KeyN", shift: true, accentKey: keyCaron },
|
Ň: { key: "KeyN", shift: true, accentKey: keyCaron },
|
||||||
"Ñ": { key: "KeyN", shift: true, accentKey: keyTilde },
|
Ñ: { key: "KeyN", shift: true, accentKey: keyTilde },
|
||||||
"Ṅ": { key: "KeyN", shift: true, accentKEy: keyOverdot },
|
Ṅ: { key: "KeyN", shift: true, accentKEy: keyOverdot },
|
||||||
O: { key: "KeyO", shift: true },
|
O: { key: "KeyO", shift: true },
|
||||||
"Ö": { key: "KeyO", shift: true, accentKey: keyTrema },
|
Ö: { key: "KeyO", shift: true, accentKey: keyTrema },
|
||||||
"Ó": { key: "KeyO", shift: true, accentKey: keyAcute },
|
Ó: { key: "KeyO", shift: true, accentKey: keyAcute },
|
||||||
"Ô": { key: "KeyO", shift: true, accentKey: keyHat },
|
Ô: { key: "KeyO", shift: true, accentKey: keyHat },
|
||||||
"Ò": { key: "KeyO", shift: true, accentKey: keyGrave },
|
Ò: { key: "KeyO", shift: true, accentKey: keyGrave },
|
||||||
"Õ": { key: "KeyO", shift: true, accentKey: keyTilde },
|
Õ: { key: "KeyO", shift: true, accentKey: keyTilde },
|
||||||
"Ȯ": { key: "KeyO", shift: true, accentKey: keyOverdot },
|
Ȯ: { key: "KeyO", shift: true, accentKey: keyOverdot },
|
||||||
"Ǫ": { key: "KeyO", shift: true, accentKey: keyHook },
|
Ǫ: { key: "KeyO", shift: true, accentKey: keyHook },
|
||||||
P: { key: "KeyP", shift: true },
|
P: { key: "KeyP", shift: true },
|
||||||
"Ṗ": { key: "KeyP", shift: true, accentKey: keyOverdot },
|
Ṗ: { key: "KeyP", shift: true, accentKey: keyOverdot },
|
||||||
Q: { key: "KeyQ", shift: true },
|
Q: { key: "KeyQ", shift: true },
|
||||||
R: { key: "KeyR", shift: true },
|
R: { key: "KeyR", shift: true },
|
||||||
"Ř": { key: "KeyR", shift: true, accentKey: keyCaron },
|
Ř: { key: "KeyR", shift: true, accentKey: keyCaron },
|
||||||
"Ṙ": { key: "KeyR", shift: true, accentKey: keyOverdot },
|
Ṙ: { key: "KeyR", shift: true, accentKey: keyOverdot },
|
||||||
S: { key: "KeyS", shift: true },
|
S: { key: "KeyS", shift: true },
|
||||||
"Š": { key: "KeyS", shift: true, accentKey: keyCaron },
|
Š: { key: "KeyS", shift: true, accentKey: keyCaron },
|
||||||
"Ṡ": { key: "KeyS", shift: true, accentKey: keyOverdot },
|
Ṡ: { key: "KeyS", shift: true, accentKey: keyOverdot },
|
||||||
T: { key: "KeyT", shift: true },
|
T: { key: "KeyT", shift: true },
|
||||||
"Ť": { key: "KeyT", shift: true, accentKey: keyCaron },
|
Ť: { key: "KeyT", shift: true, accentKey: keyCaron },
|
||||||
"Ṫ": { key: "KeyT", shift: true, accentKey: keyOverdot },
|
Ṫ: { key: "KeyT", shift: true, accentKey: keyOverdot },
|
||||||
U: { key: "KeyU", shift: true },
|
U: { key: "KeyU", shift: true },
|
||||||
"Ü": { key: "KeyU", shift: true, accentKey: keyTrema },
|
Ü: { key: "KeyU", shift: true, accentKey: keyTrema },
|
||||||
"Ú": { key: "KeyU", shift: true, accentKey: keyAcute },
|
Ú: { key: "KeyU", shift: true, accentKey: keyAcute },
|
||||||
"Û": { key: "KeyU", shift: true, accentKey: keyHat },
|
Û: { key: "KeyU", shift: true, accentKey: keyHat },
|
||||||
"Ù": { key: "KeyU", shift: true, accentKey: keyGrave },
|
Ù: { key: "KeyU", shift: true, accentKey: keyGrave },
|
||||||
"Ũ": { key: "KeyU", shift: true, accentKey: keyTilde },
|
Ũ: { key: "KeyU", shift: true, accentKey: keyTilde },
|
||||||
"Ů": { key: "KeyU", shift: true, accentKey: keyRing },
|
Ů: { key: "KeyU", shift: true, accentKey: keyRing },
|
||||||
"Ų": { key: "KeyU", shift: true, accentKey: keyHook },
|
Ų: { key: "KeyU", shift: true, accentKey: keyHook },
|
||||||
V: { key: "KeyV", shift: true },
|
V: { key: "KeyV", shift: true },
|
||||||
W: { key: "KeyW", shift: true },
|
W: { key: "KeyW", shift: true },
|
||||||
"Ẇ": { key: "KeyW", shift: true, accentKey: keyOverdot },
|
Ẇ: { key: "KeyW", shift: true, accentKey: keyOverdot },
|
||||||
X: { key: "KeyX", shift: true },
|
X: { key: "KeyX", shift: true },
|
||||||
"Ẋ": { key: "KeyX", shift: true, accentKey: keyOverdot },
|
Ẋ: { key: "KeyX", shift: true, accentKey: keyOverdot },
|
||||||
Y: { key: "KeyY", shift: true },
|
Y: { key: "KeyY", shift: true },
|
||||||
"Ý": { key: "KeyY", shift: true, accentKey: keyAcute },
|
Ý: { key: "KeyY", shift: true, accentKey: keyAcute },
|
||||||
"Ẏ": { key: "KeyY", shift: true, accentKey: keyOverdot },
|
Ẏ: { key: "KeyY", shift: true, accentKey: keyOverdot },
|
||||||
Z: { key: "KeyZ", shift: true },
|
Z: { key: "KeyZ", shift: true },
|
||||||
"Ż": { key: "KeyZ", shift: true, accentKey: keyOverdot },
|
Ż: { key: "KeyZ", shift: true, accentKey: keyOverdot },
|
||||||
a: { key: "KeyA" },
|
a: { key: "KeyA" },
|
||||||
"ä": { key: "KeyA", accentKey: keyTrema },
|
ä: { key: "KeyA", accentKey: keyTrema },
|
||||||
"â": { key: "KeyA", accentKey: keyHat },
|
â: { key: "KeyA", accentKey: keyHat },
|
||||||
"à": { key: "KeyA", accentKey: keyGrave },
|
à: { key: "KeyA", accentKey: keyGrave },
|
||||||
"ã": { key: "KeyA", accentKey: keyTilde },
|
ã: { key: "KeyA", accentKey: keyTilde },
|
||||||
"ȧ": { key: "KeyA", accentKey: keyOverdot },
|
ȧ: { key: "KeyA", accentKey: keyOverdot },
|
||||||
"ą": { key: "KeyA", accentKey: keyHook },
|
ą: { key: "KeyA", accentKey: keyHook },
|
||||||
b: { key: "KeyB" },
|
b: { key: "KeyB" },
|
||||||
"{": { key: "KeyB", altRight: true },
|
"{": { key: "KeyB", altRight: true },
|
||||||
"ḃ": { key: "KeyB", accentKey: keyOverdot },
|
ḃ: { key: "KeyB", accentKey: keyOverdot },
|
||||||
c: { key: "KeyC" },
|
c: { key: "KeyC" },
|
||||||
"&": { key: "KeyC", altRight: true },
|
"&": { key: "KeyC", altRight: true },
|
||||||
"ç": { key: "KeyC", accentKey: keyCedille },
|
ç: { key: "KeyC", accentKey: keyCedille },
|
||||||
"ċ": { key: "KeyC", accentKey: keyOverdot },
|
ċ: { key: "KeyC", accentKey: keyOverdot },
|
||||||
d: { key: "KeyD" },
|
d: { key: "KeyD" },
|
||||||
"ď": { key: "KeyD", accentKey: keyCaron },
|
ď: { key: "KeyD", accentKey: keyCaron },
|
||||||
"ḋ": { key: "KeyD", accentKey: keyOverdot },
|
ḋ: { key: "KeyD", accentKey: keyOverdot },
|
||||||
"Đ": { key: "KeyD", altRight: true },
|
Đ: { key: "KeyD", altRight: true },
|
||||||
e: { key: "KeyE" },
|
e: { key: "KeyE" },
|
||||||
"ë": { key: "KeyE", accentKey: keyTrema },
|
ë: { key: "KeyE", accentKey: keyTrema },
|
||||||
"ê": { key: "KeyE", accentKey: keyHat },
|
ê: { key: "KeyE", accentKey: keyHat },
|
||||||
"ẽ": { key: "KeyE", accentKey: keyTilde },
|
ẽ: { key: "KeyE", accentKey: keyTilde },
|
||||||
"è": { key: "KeyE", accentKey: keyGrave },
|
è: { key: "KeyE", accentKey: keyGrave },
|
||||||
"ė": { key: "KeyE", accentKey: keyOverdot },
|
ė: { key: "KeyE", accentKey: keyOverdot },
|
||||||
"ę": { key: "KeyE", accentKey: keyHook },
|
ę: { key: "KeyE", accentKey: keyHook },
|
||||||
"€": { key: "KeyE", altRight: true },
|
"€": { key: "KeyE", altRight: true },
|
||||||
f: { key: "KeyF" },
|
f: { key: "KeyF" },
|
||||||
"ḟ": { key: "KeyF", accentKey: keyOverdot },
|
ḟ: { key: "KeyF", accentKey: keyOverdot },
|
||||||
"[": { key: "KeyF", altRight: true },
|
"[": { key: "KeyF", altRight: true },
|
||||||
g: { key: "KeyG" },
|
g: { key: "KeyG" },
|
||||||
"ġ": { key: "KeyG", accentKey: keyOverdot },
|
ġ: { key: "KeyG", accentKey: keyOverdot },
|
||||||
"]": { key: "KeyF", altRight: true },
|
"]": { key: "KeyF", altRight: true },
|
||||||
h: { key: "KeyH" },
|
h: { key: "KeyH" },
|
||||||
"ḣ": { key: "KeyH", accentKey: keyOverdot },
|
ḣ: { key: "KeyH", accentKey: keyOverdot },
|
||||||
i: { key: "KeyI" },
|
i: { key: "KeyI" },
|
||||||
"ï": { key: "KeyI", accentKey: keyTrema },
|
ï: { key: "KeyI", accentKey: keyTrema },
|
||||||
"î": { key: "KeyI", accentKey: keyHat },
|
î: { key: "KeyI", accentKey: keyHat },
|
||||||
"ì": { key: "KeyI", accentKey: keyGrave },
|
ì: { key: "KeyI", accentKey: keyGrave },
|
||||||
"ĩ": { key: "KeyI", accentKey: keyTilde },
|
ĩ: { key: "KeyI", accentKey: keyTilde },
|
||||||
"ı": { key: "KeyI", accentKey: keyOverdot },
|
ı: { key: "KeyI", accentKey: keyOverdot },
|
||||||
"į": { key: "KeyI", accentKey: keyHook },
|
į: { key: "KeyI", accentKey: keyHook },
|
||||||
j: { key: "KeyJ" },
|
j: { key: "KeyJ" },
|
||||||
"ȷ": { key: "KeyJ", accentKey: keyOverdot },
|
ȷ: { key: "KeyJ", accentKey: keyOverdot },
|
||||||
k: { key: "KeyK" },
|
k: { key: "KeyK" },
|
||||||
"ł": { key: "KeyK", altRight: true },
|
ł: { key: "KeyK", altRight: true },
|
||||||
l: { key: "KeyL" },
|
l: { key: "KeyL" },
|
||||||
"ŀ": { key: "KeyL", accentKey: keyOverdot },
|
ŀ: { key: "KeyL", accentKey: keyOverdot },
|
||||||
"Ł": { key: "KeyL", altRight: true },
|
Ł: { key: "KeyL", altRight: true },
|
||||||
m: { key: "KeyM" },
|
m: { key: "KeyM" },
|
||||||
"ṁ": { key: "KeyM", accentKey: keyOverdot },
|
ṁ: { key: "KeyM", accentKey: keyOverdot },
|
||||||
n: { key: "KeyN" },
|
n: { key: "KeyN" },
|
||||||
"}": { key: "KeyN", altRight: true },
|
"}": { key: "KeyN", altRight: true },
|
||||||
"ň": { key: "KeyN", accentKey: keyCaron },
|
ň: { key: "KeyN", accentKey: keyCaron },
|
||||||
"ñ": { key: "KeyN", accentKey: keyTilde },
|
ñ: { key: "KeyN", accentKey: keyTilde },
|
||||||
"ṅ": { key: "KeyN", accentKey: keyOverdot },
|
ṅ: { key: "KeyN", accentKey: keyOverdot },
|
||||||
o: { key: "KeyO" },
|
o: { key: "KeyO" },
|
||||||
"ö": { key: "Key0", accentKey: keyTrema },
|
ö: { key: "Key0", accentKey: keyTrema },
|
||||||
"ó": { key: "KeyO", accentKey: keyAcute },
|
ó: { key: "KeyO", accentKey: keyAcute },
|
||||||
"ô": { key: "KeyO", accentKey: keyHat },
|
ô: { key: "KeyO", accentKey: keyHat },
|
||||||
"ò": { key: "KeyO", accentKey: keyGrave },
|
ò: { key: "KeyO", accentKey: keyGrave },
|
||||||
"õ": { key: "KeyO", accentKey: keyTilde },
|
õ: { key: "KeyO", accentKey: keyTilde },
|
||||||
"ȯ": { key: "KeyO", accentKey: keyOverdot },
|
ȯ: { key: "KeyO", accentKey: keyOverdot },
|
||||||
"ǫ": { key: "KeyO", accentKey: keyHook },
|
ǫ: { key: "KeyO", accentKey: keyHook },
|
||||||
p: { key: "KeyP" },
|
p: { key: "KeyP" },
|
||||||
"ṗ": { key: "KeyP", accentKey: keyOverdot },
|
ṗ: { key: "KeyP", accentKey: keyOverdot },
|
||||||
q: { key: "KeyQ" },
|
q: { key: "KeyQ" },
|
||||||
r: { key: "KeyR" },
|
r: { key: "KeyR" },
|
||||||
"ṙ": { key: "KeyR", accentKey: keyOverdot },
|
ṙ: { key: "KeyR", accentKey: keyOverdot },
|
||||||
s: { key: "KeyS" },
|
s: { key: "KeyS" },
|
||||||
"ṡ": { key: "KeyS", accentKey: keyOverdot },
|
ṡ: { key: "KeyS", accentKey: keyOverdot },
|
||||||
"đ": { key: "KeyS", altRight: true },
|
đ: { key: "KeyS", altRight: true },
|
||||||
t: { key: "KeyT" },
|
t: { key: "KeyT" },
|
||||||
"ť": { key: "KeyT", accentKey: keyCaron },
|
ť: { key: "KeyT", accentKey: keyCaron },
|
||||||
"ṫ": { key: "KeyT", accentKey: keyOverdot },
|
ṫ: { key: "KeyT", accentKey: keyOverdot },
|
||||||
u: { key: "KeyU" },
|
u: { key: "KeyU" },
|
||||||
"ü": { key: "KeyU", accentKey: keyTrema },
|
ü: { key: "KeyU", accentKey: keyTrema },
|
||||||
"û": { key: "KeyU", accentKey: keyHat },
|
û: { key: "KeyU", accentKey: keyHat },
|
||||||
"ù": { key: "KeyU", accentKey: keyGrave },
|
ù: { key: "KeyU", accentKey: keyGrave },
|
||||||
"ũ": { key: "KeyU", accentKey: keyTilde },
|
ũ: { key: "KeyU", accentKey: keyTilde },
|
||||||
"ų": { key: "KeyU", accentKey: keyHook },
|
ų: { key: "KeyU", accentKey: keyHook },
|
||||||
v: { key: "KeyV" },
|
v: { key: "KeyV" },
|
||||||
"@": { key: "KeyV", altRight: true },
|
"@": { key: "KeyV", altRight: true },
|
||||||
w: { key: "KeyW" },
|
w: { key: "KeyW" },
|
||||||
"ẇ": { key: "KeyW", accentKey: keyOverdot },
|
ẇ: { key: "KeyW", accentKey: keyOverdot },
|
||||||
x: { key: "KeyX" },
|
x: { key: "KeyX" },
|
||||||
"#": { key: "KeyX", altRight: true },
|
"#": { key: "KeyX", altRight: true },
|
||||||
"ẋ": { key: "KeyX", accentKey: keyOverdot },
|
ẋ: { key: "KeyX", accentKey: keyOverdot },
|
||||||
y: { key: "KeyY" },
|
y: { key: "KeyY" },
|
||||||
"ẏ": { key: "KeyY", accentKey: keyOverdot },
|
ẏ: { key: "KeyY", accentKey: keyOverdot },
|
||||||
z: { key: "KeyZ" },
|
z: { key: "KeyZ" },
|
||||||
"ż": { key: "KeyZ", accentKey: keyOverdot },
|
ż: { key: "KeyZ", accentKey: keyOverdot },
|
||||||
";": { key: "Backquote" },
|
";": { key: "Backquote" },
|
||||||
"°": { key: "Backquote", shift: true, deadKey: true },
|
"°": { key: "Backquote", shift: true, deadKey: true },
|
||||||
"+": { key: "Digit1" },
|
"+": { key: "Digit1" },
|
||||||
1: { key: "Digit1", shift: true },
|
1: { key: "Digit1", shift: true },
|
||||||
"ě": { key: "Digit2" },
|
ě: { key: "Digit2" },
|
||||||
2: { key: "Digit2", shift: true },
|
2: { key: "Digit2", shift: true },
|
||||||
"š": { key: "Digit3" },
|
š: { key: "Digit3" },
|
||||||
3: { key: "Digit3", shift: true },
|
3: { key: "Digit3", shift: true },
|
||||||
"č": { key: "Digit4" },
|
č: { key: "Digit4" },
|
||||||
4: { key: "Digit4", shift: true },
|
4: { key: "Digit4", shift: true },
|
||||||
"ř": { key: "Digit5" },
|
ř: { key: "Digit5" },
|
||||||
5: { key: "Digit5", shift: true },
|
5: { key: "Digit5", shift: true },
|
||||||
"ž": { key: "Digit6" },
|
ž: { key: "Digit6" },
|
||||||
6: { key: "Digit6", shift: true },
|
6: { key: "Digit6", shift: true },
|
||||||
"ý": { key: "Digit7" },
|
ý: { key: "Digit7" },
|
||||||
7: { key: "Digit7", shift: true },
|
7: { key: "Digit7", shift: true },
|
||||||
"á": { key: "Digit8" },
|
á: { key: "Digit8" },
|
||||||
8: { key: "Digit8", shift: true },
|
8: { key: "Digit8", shift: true },
|
||||||
"í": { key: "Digit9" },
|
í: { key: "Digit9" },
|
||||||
9: { key: "Digit9", shift: true },
|
9: { key: "Digit9", shift: true },
|
||||||
"é": { key: "Digit0" },
|
é: { key: "Digit0" },
|
||||||
0: { key: "Digit0", shift: true },
|
0: { key: "Digit0", shift: true },
|
||||||
"=": { key: "Minus" },
|
"=": { key: "Minus" },
|
||||||
"%": { key: "Minus", shift: true },
|
"%": { key: "Minus", shift: true },
|
||||||
"ú": { key: "BracketLeft" },
|
ú: { key: "BracketLeft" },
|
||||||
"/": { key: "BracketLeft", shift: true },
|
"/": { key: "BracketLeft", shift: true },
|
||||||
")": { key: "BracketRight" },
|
")": { key: "BracketRight" },
|
||||||
"(": { key: "BracketRight", shift: true },
|
"(": { key: "BracketRight", shift: true },
|
||||||
"ů": { key: "Semicolon" },
|
ů: { key: "Semicolon" },
|
||||||
"\"": { key: "Semicolon", shift: true },
|
'"': { key: "Semicolon", shift: true },
|
||||||
"§": { key: "Quote" },
|
"§": { key: "Quote" },
|
||||||
"!": { key: "Quote", shift: true },
|
"!": { key: "Quote", shift: true },
|
||||||
"'": { key: "Backslash", shift: true },
|
"'": { key: "Backslash", shift: true },
|
||||||
|
|
@ -236,7 +236,7 @@ const chars = {
|
||||||
":": { key: "Period", shift: true },
|
":": { key: "Period", shift: true },
|
||||||
">": { key: "Period", altRight: true },
|
">": { key: "Period", altRight: true },
|
||||||
"-": { key: "Slash" },
|
"-": { key: "Slash" },
|
||||||
"_": { key: "Slash", shift: true },
|
_: { key: "Slash", shift: true },
|
||||||
"*": { key: "Slash", altRight: true },
|
"*": { key: "Slash", altRight: true },
|
||||||
"\\": { key: "IntlBackslash" },
|
"\\": { key: "IntlBackslash" },
|
||||||
"|": { key: "IntlBackslash", shift: true },
|
"|": { key: "IntlBackslash", shift: true },
|
||||||
|
|
@ -253,5 +253,5 @@ export const cs_CZ: KeyboardLayout = {
|
||||||
// TODO need to localize these maps and layouts
|
// TODO need to localize these maps and layouts
|
||||||
keyDisplayMap: en_US.keyDisplayMap,
|
keyDisplayMap: en_US.keyDisplayMap,
|
||||||
modifierDisplayMap: en_US.modifierDisplayMap,
|
modifierDisplayMap: en_US.modifierDisplayMap,
|
||||||
virtualKeyboard: en_US.virtualKeyboard
|
virtualKeyboard: en_US.virtualKeyboard,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,133 +1,133 @@
|
||||||
import { KeyboardLayout, KeyCombo } from "../keyboardLayouts"
|
import { KeyboardLayout, KeyCombo } from "../keyboardLayouts";
|
||||||
|
|
||||||
import { en_US } from "./en_US" // for fallback of keyDisplayMap, modifierDisplayMap, and virtualKeyboard
|
import { en_US } from "./en_US"; // for fallback of keyDisplayMap, modifierDisplayMap, and virtualKeyboard
|
||||||
|
|
||||||
export const name = "Dansk";
|
export const name = "Dansk";
|
||||||
const isoCode = "da-DK";
|
const isoCode = "da-DK";
|
||||||
|
|
||||||
const keyTrema = { key: "BracketRight" }
|
const keyTrema = { key: "BracketRight" };
|
||||||
const keyAcute = { key: "Equal", altRight: true }
|
const keyAcute = { key: "Equal", altRight: true };
|
||||||
const keyHat = { key: "BracketRight", shift: true }
|
const keyHat = { key: "BracketRight", shift: true };
|
||||||
const keyGrave = { key: "Equal", shift: true }
|
const keyGrave = { key: "Equal", shift: true };
|
||||||
const keyTilde = { key: "BracketRight", altRight: true }
|
const keyTilde = { key: "BracketRight", altRight: true };
|
||||||
|
|
||||||
export const chars = {
|
export const chars = {
|
||||||
A: { key: "KeyA", shift: true },
|
A: { key: "KeyA", shift: true },
|
||||||
"Ä": { key: "KeyA", shift: true, accentKey: keyTrema },
|
Ä: { key: "KeyA", shift: true, accentKey: keyTrema },
|
||||||
"Á": { key: "KeyA", shift: true, accentKey: keyAcute },
|
Á: { key: "KeyA", shift: true, accentKey: keyAcute },
|
||||||
"Â": { key: "KeyA", shift: true, accentKey: keyHat },
|
Â: { key: "KeyA", shift: true, accentKey: keyHat },
|
||||||
"À": { key: "KeyA", shift: true, accentKey: keyGrave },
|
À: { key: "KeyA", shift: true, accentKey: keyGrave },
|
||||||
"Ã": { key: "KeyA", shift: true, accentKey: keyTilde },
|
Ã: { key: "KeyA", shift: true, accentKey: keyTilde },
|
||||||
B: { key: "KeyB", shift: true },
|
B: { key: "KeyB", shift: true },
|
||||||
C: { key: "KeyC", shift: true },
|
C: { key: "KeyC", shift: true },
|
||||||
D: { key: "KeyD", shift: true },
|
D: { key: "KeyD", shift: true },
|
||||||
E: { key: "KeyE", shift: true },
|
E: { key: "KeyE", shift: true },
|
||||||
"Ë": { key: "KeyE", shift: true, accentKey: keyTrema },
|
Ë: { key: "KeyE", shift: true, accentKey: keyTrema },
|
||||||
"É": { key: "KeyE", shift: true, accentKey: keyAcute },
|
É: { key: "KeyE", shift: true, accentKey: keyAcute },
|
||||||
"Ê": { key: "KeyE", shift: true, accentKey: keyHat },
|
Ê: { key: "KeyE", shift: true, accentKey: keyHat },
|
||||||
"È": { key: "KeyE", shift: true, accentKey: keyGrave },
|
È: { key: "KeyE", shift: true, accentKey: keyGrave },
|
||||||
"Ẽ": { key: "KeyE", shift: true, accentKey: keyTilde },
|
Ẽ: { key: "KeyE", shift: true, accentKey: keyTilde },
|
||||||
F: { key: "KeyF", shift: true },
|
F: { key: "KeyF", shift: true },
|
||||||
G: { key: "KeyG", shift: true },
|
G: { key: "KeyG", shift: true },
|
||||||
H: { key: "KeyH", shift: true },
|
H: { key: "KeyH", shift: true },
|
||||||
I: { key: "KeyI", shift: true },
|
I: { key: "KeyI", shift: true },
|
||||||
"Ï": { key: "KeyI", shift: true, accentKey: keyTrema },
|
Ï: { key: "KeyI", shift: true, accentKey: keyTrema },
|
||||||
"Í": { key: "KeyI", shift: true, accentKey: keyAcute },
|
Í: { key: "KeyI", shift: true, accentKey: keyAcute },
|
||||||
"Î": { key: "KeyI", shift: true, accentKey: keyHat },
|
Î: { key: "KeyI", shift: true, accentKey: keyHat },
|
||||||
"Ì": { key: "KeyI", shift: true, accentKey: keyGrave },
|
Ì: { key: "KeyI", shift: true, accentKey: keyGrave },
|
||||||
"Ĩ": { key: "KeyI", shift: true, accentKey: keyTilde },
|
Ĩ: { key: "KeyI", shift: true, accentKey: keyTilde },
|
||||||
J: { key: "KeyJ", shift: true },
|
J: { key: "KeyJ", shift: true },
|
||||||
K: { key: "KeyK", shift: true },
|
K: { key: "KeyK", shift: true },
|
||||||
L: { key: "KeyL", shift: true },
|
L: { key: "KeyL", shift: true },
|
||||||
M: { key: "KeyM", shift: true },
|
M: { key: "KeyM", shift: true },
|
||||||
N: { key: "KeyN", shift: true },
|
N: { key: "KeyN", shift: true },
|
||||||
O: { key: "KeyO", shift: true },
|
O: { key: "KeyO", shift: true },
|
||||||
"Ö": { key: "KeyO", shift: true, accentKey: keyTrema },
|
Ö: { key: "KeyO", shift: true, accentKey: keyTrema },
|
||||||
"Ó": { key: "KeyO", shift: true, accentKey: keyAcute },
|
Ó: { key: "KeyO", shift: true, accentKey: keyAcute },
|
||||||
"Ô": { key: "KeyO", shift: true, accentKey: keyHat },
|
Ô: { key: "KeyO", shift: true, accentKey: keyHat },
|
||||||
"Ò": { key: "KeyO", shift: true, accentKey: keyGrave },
|
Ò: { key: "KeyO", shift: true, accentKey: keyGrave },
|
||||||
"Õ": { key: "KeyO", shift: true, accentKey: keyTilde },
|
Õ: { key: "KeyO", shift: true, accentKey: keyTilde },
|
||||||
P: { key: "KeyP", shift: true },
|
P: { key: "KeyP", shift: true },
|
||||||
Q: { key: "KeyQ", shift: true },
|
Q: { key: "KeyQ", shift: true },
|
||||||
R: { key: "KeyR", shift: true },
|
R: { key: "KeyR", shift: true },
|
||||||
S: { key: "KeyS", shift: true },
|
S: { key: "KeyS", shift: true },
|
||||||
T: { key: "KeyT", shift: true },
|
T: { key: "KeyT", shift: true },
|
||||||
U: { key: "KeyU", shift: true },
|
U: { key: "KeyU", shift: true },
|
||||||
"Ü": { key: "KeyU", shift: true, accentKey: keyTrema },
|
Ü: { key: "KeyU", shift: true, accentKey: keyTrema },
|
||||||
"Ú": { key: "KeyU", shift: true, accentKey: keyAcute },
|
Ú: { key: "KeyU", shift: true, accentKey: keyAcute },
|
||||||
"Û": { key: "KeyU", shift: true, accentKey: keyHat },
|
Û: { key: "KeyU", shift: true, accentKey: keyHat },
|
||||||
"Ù": { key: "KeyU", shift: true, accentKey: keyGrave },
|
Ù: { key: "KeyU", shift: true, accentKey: keyGrave },
|
||||||
"Ũ": { key: "KeyU", shift: true, accentKey: keyTilde },
|
Ũ: { key: "KeyU", shift: true, accentKey: keyTilde },
|
||||||
V: { key: "KeyV", shift: true },
|
V: { key: "KeyV", shift: true },
|
||||||
W: { key: "KeyW", shift: true },
|
W: { key: "KeyW", shift: true },
|
||||||
X: { key: "KeyX", shift: true },
|
X: { key: "KeyX", shift: true },
|
||||||
Y: { key: "KeyY", shift: true },
|
Y: { key: "KeyY", shift: true },
|
||||||
Z: { key: "KeyZ", shift: true },
|
Z: { key: "KeyZ", shift: true },
|
||||||
a: { key: "KeyA" },
|
a: { key: "KeyA" },
|
||||||
"ä": { key: "KeyA", accentKey: keyTrema },
|
ä: { key: "KeyA", accentKey: keyTrema },
|
||||||
"á": { key: "KeyA", accentKey: keyAcute },
|
á: { key: "KeyA", accentKey: keyAcute },
|
||||||
"â": { key: "KeyA", accentKey: keyHat },
|
â: { key: "KeyA", accentKey: keyHat },
|
||||||
"à": { key: "KeyA", accentKey: keyGrave },
|
à: { key: "KeyA", accentKey: keyGrave },
|
||||||
"ã": { key: "KeyA", accentKey: keyTilde },
|
ã: { key: "KeyA", accentKey: keyTilde },
|
||||||
b: { key: "KeyB" },
|
b: { key: "KeyB" },
|
||||||
c: { key: "KeyC" },
|
c: { key: "KeyC" },
|
||||||
d: { key: "KeyD" },
|
d: { key: "KeyD" },
|
||||||
e: { key: "KeyE" },
|
e: { key: "KeyE" },
|
||||||
"ë": { key: "KeyE", accentKey: keyTrema },
|
ë: { key: "KeyE", accentKey: keyTrema },
|
||||||
"é": { key: "KeyE", accentKey: keyAcute },
|
é: { key: "KeyE", accentKey: keyAcute },
|
||||||
"ê": { key: "KeyE", accentKey: keyHat },
|
ê: { key: "KeyE", accentKey: keyHat },
|
||||||
"è": { key: "KeyE", accentKey: keyGrave },
|
è: { key: "KeyE", accentKey: keyGrave },
|
||||||
"ẽ": { key: "KeyE", accentKey: keyTilde },
|
ẽ: { key: "KeyE", accentKey: keyTilde },
|
||||||
"€": { key: "KeyE", altRight: true },
|
"€": { key: "KeyE", altRight: true },
|
||||||
f: { key: "KeyF" },
|
f: { key: "KeyF" },
|
||||||
g: { key: "KeyG" },
|
g: { key: "KeyG" },
|
||||||
h: { key: "KeyH" },
|
h: { key: "KeyH" },
|
||||||
i: { key: "KeyI" },
|
i: { key: "KeyI" },
|
||||||
"ï": { key: "KeyI", accentKey: keyTrema },
|
ï: { key: "KeyI", accentKey: keyTrema },
|
||||||
"í": { key: "KeyI", accentKey: keyAcute },
|
í: { key: "KeyI", accentKey: keyAcute },
|
||||||
"î": { key: "KeyI", accentKey: keyHat },
|
î: { key: "KeyI", accentKey: keyHat },
|
||||||
"ì": { key: "KeyI", accentKey: keyGrave },
|
ì: { key: "KeyI", accentKey: keyGrave },
|
||||||
"ĩ": { key: "KeyI", accentKey: keyTilde },
|
ĩ: { key: "KeyI", accentKey: keyTilde },
|
||||||
j: { key: "KeyJ" },
|
j: { key: "KeyJ" },
|
||||||
k: { key: "KeyK" },
|
k: { key: "KeyK" },
|
||||||
l: { key: "KeyL" },
|
l: { key: "KeyL" },
|
||||||
m: { key: "KeyM" },
|
m: { key: "KeyM" },
|
||||||
n: { key: "KeyN" },
|
n: { key: "KeyN" },
|
||||||
o: { key: "KeyO" },
|
o: { key: "KeyO" },
|
||||||
"ö": { key: "KeyO", accentKey: keyTrema },
|
ö: { key: "KeyO", accentKey: keyTrema },
|
||||||
"ó": { key: "KeyO", accentKey: keyAcute },
|
ó: { key: "KeyO", accentKey: keyAcute },
|
||||||
"ô": { key: "KeyO", accentKey: keyHat },
|
ô: { key: "KeyO", accentKey: keyHat },
|
||||||
"ò": { key: "KeyO", accentKey: keyGrave },
|
ò: { key: "KeyO", accentKey: keyGrave },
|
||||||
"õ": { key: "KeyO", accentKey: keyTilde },
|
õ: { key: "KeyO", accentKey: keyTilde },
|
||||||
p: { key: "KeyP" },
|
p: { key: "KeyP" },
|
||||||
q: { key: "KeyQ" },
|
q: { key: "KeyQ" },
|
||||||
r: { key: "KeyR" },
|
r: { key: "KeyR" },
|
||||||
s: { key: "KeyS" },
|
s: { key: "KeyS" },
|
||||||
t: { key: "KeyT" },
|
t: { key: "KeyT" },
|
||||||
u: { key: "KeyU" },
|
u: { key: "KeyU" },
|
||||||
"ü": { key: "KeyU", accentKey: keyTrema },
|
ü: { key: "KeyU", accentKey: keyTrema },
|
||||||
"ú": { key: "KeyU", accentKey: keyAcute },
|
ú: { key: "KeyU", accentKey: keyAcute },
|
||||||
"û": { key: "KeyU", accentKey: keyHat },
|
û: { key: "KeyU", accentKey: keyHat },
|
||||||
"ù": { key: "KeyU", accentKey: keyGrave },
|
ù: { key: "KeyU", accentKey: keyGrave },
|
||||||
"ũ": { key: "KeyU", accentKey: keyTilde },
|
ũ: { key: "KeyU", accentKey: keyTilde },
|
||||||
v: { key: "KeyV" },
|
v: { key: "KeyV" },
|
||||||
w: { key: "KeyW" },
|
w: { key: "KeyW" },
|
||||||
x: { key: "KeyX" },
|
x: { key: "KeyX" },
|
||||||
y: { key: "KeyY" }, // <-- corrected
|
y: { key: "KeyY" }, // <-- corrected
|
||||||
z: { key: "KeyZ" }, // <-- corrected
|
z: { key: "KeyZ" }, // <-- corrected
|
||||||
"½": { key: "Backquote" },
|
"½": { key: "Backquote" },
|
||||||
"§": { key: "Backquote", shift: true },
|
"§": { key: "Backquote", shift: true },
|
||||||
1: { key: "Digit1" },
|
1: { key: "Digit1" },
|
||||||
"!": { key: "Digit1", shift: true },
|
"!": { key: "Digit1", shift: true },
|
||||||
2: { key: "Digit2" },
|
2: { key: "Digit2" },
|
||||||
"\"": { key: "Digit2", shift: true },
|
'"': { key: "Digit2", shift: true },
|
||||||
"@": { key: "Digit2", altRight: true },
|
"@": { key: "Digit2", altRight: true },
|
||||||
3: { key: "Digit3" },
|
3: { key: "Digit3" },
|
||||||
"#": { key: "Digit3", shift: true },
|
"#": { key: "Digit3", shift: true },
|
||||||
"£": { key: "Digit3", altRight: true },
|
"£": { key: "Digit3", altRight: true },
|
||||||
4: { key: "Digit4" },
|
4: { key: "Digit4" },
|
||||||
"¤": { key: "Digit4", shift: true },
|
"¤": { key: "Digit4", shift: true },
|
||||||
"$": { key: "Digit4", altRight: true },
|
$: { key: "Digit4", altRight: true },
|
||||||
5: { key: "Digit5" },
|
5: { key: "Digit5" },
|
||||||
"%": { key: "Digit5", shift: true },
|
"%": { key: "Digit5", shift: true },
|
||||||
6: { key: "Digit6" },
|
6: { key: "Digit6" },
|
||||||
|
|
@ -147,12 +147,12 @@ export const chars = {
|
||||||
"+": { key: "Minus" },
|
"+": { key: "Minus" },
|
||||||
"?": { key: "Minus", shift: true },
|
"?": { key: "Minus", shift: true },
|
||||||
"\\": { key: "Equal" },
|
"\\": { key: "Equal" },
|
||||||
"å": { key: "BracketLeft" },
|
å: { key: "BracketLeft" },
|
||||||
"Å": { key: "BracketLeft", shift: true },
|
Å: { key: "BracketLeft", shift: true },
|
||||||
"ø": { key: "Semicolon" },
|
ø: { key: "Semicolon" },
|
||||||
"Ø": { key: "Semicolon", shift: true },
|
Ø: { key: "Semicolon", shift: true },
|
||||||
"æ": { key: "Quote" },
|
æ: { key: "Quote" },
|
||||||
"Æ": { key: "Quote", shift: true },
|
Æ: { key: "Quote", shift: true },
|
||||||
"'": { key: "Backslash" },
|
"'": { key: "Backslash" },
|
||||||
"*": { key: "Backslash", shift: true },
|
"*": { key: "Backslash", shift: true },
|
||||||
",": { key: "Comma" },
|
",": { key: "Comma" },
|
||||||
|
|
@ -160,20 +160,19 @@ export const chars = {
|
||||||
".": { key: "Period" },
|
".": { key: "Period" },
|
||||||
":": { key: "Period", shift: true },
|
":": { key: "Period", shift: true },
|
||||||
"-": { key: "Slash" },
|
"-": { key: "Slash" },
|
||||||
"_": { key: "Slash", shift: true },
|
_: { key: "Slash", shift: true },
|
||||||
"<": { key: "IntlBackslash" },
|
"<": { key: "IntlBackslash" },
|
||||||
">": { key: "IntlBackslash", shift: true },
|
">": { key: "IntlBackslash", shift: true },
|
||||||
"~": { key: "BracketRight", deadKey: true, altRight: true },
|
"~": { key: "BracketRight", deadKey: true, altRight: true },
|
||||||
"^": { key: "BracketRight", deadKey: true, shift: true },
|
"^": { key: "BracketRight", deadKey: true, shift: true },
|
||||||
"¨": { key: "BracketRight", deadKey: true, },
|
"¨": { key: "BracketRight", deadKey: true },
|
||||||
"|": { key: "Equal", deadKey: true, altRight: true },
|
"|": { key: "Equal", deadKey: true, altRight: true },
|
||||||
"`": { key: "Equal", deadKey: true, shift: true, },
|
"`": { key: "Equal", deadKey: true, shift: true },
|
||||||
"´": { key: "Equal", deadKey: true, },
|
"´": { key: "Equal", deadKey: true },
|
||||||
" ": { key: "Space" },
|
" ": { key: "Space" },
|
||||||
"\n": { key: "Enter" },
|
"\n": { key: "Enter" },
|
||||||
Enter: { key: "Enter" },
|
Enter: { key: "Enter" },
|
||||||
Tab: { key: "Tab" },
|
Tab: { key: "Tab" },
|
||||||
|
|
||||||
} as Record<string, KeyCombo>;
|
} as Record<string, KeyCombo>;
|
||||||
|
|
||||||
export const da_DK: KeyboardLayout = {
|
export const da_DK: KeyboardLayout = {
|
||||||
|
|
@ -183,5 +182,5 @@ export const da_DK: KeyboardLayout = {
|
||||||
// TODO need to localize these maps and layouts
|
// TODO need to localize these maps and layouts
|
||||||
keyDisplayMap: en_US.keyDisplayMap,
|
keyDisplayMap: en_US.keyDisplayMap,
|
||||||
modifierDisplayMap: en_US.modifierDisplayMap,
|
modifierDisplayMap: en_US.modifierDisplayMap,
|
||||||
virtualKeyboard: en_US.virtualKeyboard
|
virtualKeyboard: en_US.virtualKeyboard,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,109 +1,109 @@
|
||||||
import { KeyboardLayout, KeyCombo } from "../keyboardLayouts"
|
import { KeyboardLayout, KeyCombo } from "../keyboardLayouts";
|
||||||
|
|
||||||
import { en_US } from "./en_US" // for fallback of keyDisplayMap, modifierDisplayMap, and virtualKeyboard
|
import { en_US } from "./en_US"; // for fallback of keyDisplayMap, modifierDisplayMap, and virtualKeyboard
|
||||||
|
|
||||||
const name = "Schwiizerdütsch";
|
const name = "Schwiizerdütsch";
|
||||||
const isoCode = "de-CH";
|
const isoCode = "de-CH";
|
||||||
|
|
||||||
const keyTrema: KeyCombo = { key: "BracketRight" } // tréma (umlaut), two dots placed above a vowel
|
const keyTrema: KeyCombo = { key: "BracketRight" }; // tréma (umlaut), two dots placed above a vowel
|
||||||
const keyAcute: KeyCombo = { key: "Minus", altRight: true } // accent aigu (acute accent), mark ´ placed above the letter
|
const keyAcute: KeyCombo = { key: "Minus", altRight: true }; // accent aigu (acute accent), mark ´ placed above the letter
|
||||||
const keyHat: KeyCombo = { key: "Equal" } // accent circonflexe (accent hat), mark ^ placed above the letter
|
const keyHat: KeyCombo = { key: "Equal" }; // accent circonflexe (accent hat), mark ^ placed above the letter
|
||||||
const keyGrave: KeyCombo = { key: "Equal", shift: true } // accent grave, mark ` placed above the letter
|
const keyGrave: KeyCombo = { key: "Equal", shift: true }; // accent grave, mark ` placed above the letter
|
||||||
const keyTilde: KeyCombo = { key: "Equal", altRight: true } // tilde, mark ~ placed above the letter
|
const keyTilde: KeyCombo = { key: "Equal", altRight: true }; // tilde, mark ~ placed above the letter
|
||||||
|
|
||||||
const chars = {
|
const chars = {
|
||||||
A: { key: "KeyA", shift: true },
|
A: { key: "KeyA", shift: true },
|
||||||
"Ä": { key: "KeyA", shift: true, accentKey: keyTrema },
|
Ä: { key: "KeyA", shift: true, accentKey: keyTrema },
|
||||||
"Á": { key: "KeyA", shift: true, accentKey: keyAcute },
|
Á: { key: "KeyA", shift: true, accentKey: keyAcute },
|
||||||
"Â": { key: "KeyA", shift: true, accentKey: keyHat },
|
Â: { key: "KeyA", shift: true, accentKey: keyHat },
|
||||||
"À": { key: "KeyA", shift: true, accentKey: keyGrave },
|
À: { key: "KeyA", shift: true, accentKey: keyGrave },
|
||||||
"Ã": { key: "KeyA", shift: true, accentKey: keyTilde },
|
Ã: { key: "KeyA", shift: true, accentKey: keyTilde },
|
||||||
B: { key: "KeyB", shift: true },
|
B: { key: "KeyB", shift: true },
|
||||||
C: { key: "KeyC", shift: true },
|
C: { key: "KeyC", shift: true },
|
||||||
D: { key: "KeyD", shift: true },
|
D: { key: "KeyD", shift: true },
|
||||||
E: { key: "KeyE", shift: true },
|
E: { key: "KeyE", shift: true },
|
||||||
"Ë": { key: "KeyE", shift: true, accentKey: keyTrema },
|
Ë: { key: "KeyE", shift: true, accentKey: keyTrema },
|
||||||
"É": { key: "KeyE", shift: true, accentKey: keyAcute },
|
É: { key: "KeyE", shift: true, accentKey: keyAcute },
|
||||||
"Ê": { key: "KeyE", shift: true, accentKey: keyHat },
|
Ê: { key: "KeyE", shift: true, accentKey: keyHat },
|
||||||
"È": { key: "KeyE", shift: true, accentKey: keyGrave },
|
È: { key: "KeyE", shift: true, accentKey: keyGrave },
|
||||||
"Ẽ": { key: "KeyE", shift: true, accentKey: keyTilde },
|
Ẽ: { key: "KeyE", shift: true, accentKey: keyTilde },
|
||||||
F: { key: "KeyF", shift: true },
|
F: { key: "KeyF", shift: true },
|
||||||
G: { key: "KeyG", shift: true },
|
G: { key: "KeyG", shift: true },
|
||||||
H: { key: "KeyH", shift: true },
|
H: { key: "KeyH", shift: true },
|
||||||
I: { key: "KeyI", shift: true },
|
I: { key: "KeyI", shift: true },
|
||||||
"Ï": { key: "KeyI", shift: true, accentKey: keyTrema },
|
Ï: { key: "KeyI", shift: true, accentKey: keyTrema },
|
||||||
"Í": { key: "KeyI", shift: true, accentKey: keyAcute },
|
Í: { key: "KeyI", shift: true, accentKey: keyAcute },
|
||||||
"Î": { key: "KeyI", shift: true, accentKey: keyHat },
|
Î: { key: "KeyI", shift: true, accentKey: keyHat },
|
||||||
"Ì": { key: "KeyI", shift: true, accentKey: keyGrave },
|
Ì: { key: "KeyI", shift: true, accentKey: keyGrave },
|
||||||
"Ĩ": { key: "KeyI", shift: true, accentKey: keyTilde },
|
Ĩ: { key: "KeyI", shift: true, accentKey: keyTilde },
|
||||||
J: { key: "KeyJ", shift: true },
|
J: { key: "KeyJ", shift: true },
|
||||||
K: { key: "KeyK", shift: true },
|
K: { key: "KeyK", shift: true },
|
||||||
L: { key: "KeyL", shift: true },
|
L: { key: "KeyL", shift: true },
|
||||||
M: { key: "KeyM", shift: true },
|
M: { key: "KeyM", shift: true },
|
||||||
N: { key: "KeyN", shift: true },
|
N: { key: "KeyN", shift: true },
|
||||||
O: { key: "KeyO", shift: true },
|
O: { key: "KeyO", shift: true },
|
||||||
"Ö": { key: "KeyO", shift: true, accentKey: keyTrema },
|
Ö: { key: "KeyO", shift: true, accentKey: keyTrema },
|
||||||
"Ó": { key: "KeyO", shift: true, accentKey: keyAcute },
|
Ó: { key: "KeyO", shift: true, accentKey: keyAcute },
|
||||||
"Ô": { key: "KeyO", shift: true, accentKey: keyHat },
|
Ô: { key: "KeyO", shift: true, accentKey: keyHat },
|
||||||
"Ò": { key: "KeyO", shift: true, accentKey: keyGrave },
|
Ò: { key: "KeyO", shift: true, accentKey: keyGrave },
|
||||||
"Õ": { key: "KeyO", shift: true, accentKey: keyTilde },
|
Õ: { key: "KeyO", shift: true, accentKey: keyTilde },
|
||||||
P: { key: "KeyP", shift: true },
|
P: { key: "KeyP", shift: true },
|
||||||
Q: { key: "KeyQ", shift: true },
|
Q: { key: "KeyQ", shift: true },
|
||||||
R: { key: "KeyR", shift: true },
|
R: { key: "KeyR", shift: true },
|
||||||
S: { key: "KeyS", shift: true },
|
S: { key: "KeyS", shift: true },
|
||||||
T: { key: "KeyT", shift: true },
|
T: { key: "KeyT", shift: true },
|
||||||
U: { key: "KeyU", shift: true },
|
U: { key: "KeyU", shift: true },
|
||||||
"Ü": { key: "KeyU", shift: true, accentKey: keyTrema },
|
Ü: { key: "KeyU", shift: true, accentKey: keyTrema },
|
||||||
"Ú": { key: "KeyU", shift: true, accentKey: keyAcute },
|
Ú: { key: "KeyU", shift: true, accentKey: keyAcute },
|
||||||
"Û": { key: "KeyU", shift: true, accentKey: keyHat },
|
Û: { key: "KeyU", shift: true, accentKey: keyHat },
|
||||||
"Ù": { key: "KeyU", shift: true, accentKey: keyGrave },
|
Ù: { key: "KeyU", shift: true, accentKey: keyGrave },
|
||||||
"Ũ": { key: "KeyU", shift: true, accentKey: keyTilde },
|
Ũ: { key: "KeyU", shift: true, accentKey: keyTilde },
|
||||||
V: { key: "KeyV", shift: true },
|
V: { key: "KeyV", shift: true },
|
||||||
W: { key: "KeyW", shift: true },
|
W: { key: "KeyW", shift: true },
|
||||||
X: { key: "KeyX", shift: true },
|
X: { key: "KeyX", shift: true },
|
||||||
Y: { key: "KeyZ", shift: true },
|
Y: { key: "KeyZ", shift: true },
|
||||||
Z: { key: "KeyY", shift: true },
|
Z: { key: "KeyY", shift: true },
|
||||||
a: { key: "KeyA" },
|
a: { key: "KeyA" },
|
||||||
"á": { key: "KeyA", accentKey: keyAcute },
|
á: { key: "KeyA", accentKey: keyAcute },
|
||||||
"â": { key: "KeyA", accentKey: keyHat },
|
â: { key: "KeyA", accentKey: keyHat },
|
||||||
"ã": { key: "KeyA", accentKey: keyTilde },
|
ã: { key: "KeyA", accentKey: keyTilde },
|
||||||
b: { key: "KeyB" },
|
b: { key: "KeyB" },
|
||||||
c: { key: "KeyC" },
|
c: { key: "KeyC" },
|
||||||
d: { key: "KeyD" },
|
d: { key: "KeyD" },
|
||||||
e: { key: "KeyE" },
|
e: { key: "KeyE" },
|
||||||
"ë": { key: "KeyE", accentKey: keyTrema },
|
ë: { key: "KeyE", accentKey: keyTrema },
|
||||||
"ê": { key: "KeyE", accentKey: keyHat },
|
ê: { key: "KeyE", accentKey: keyHat },
|
||||||
"ẽ": { key: "KeyE", accentKey: keyTilde },
|
ẽ: { key: "KeyE", accentKey: keyTilde },
|
||||||
"€": { key: "KeyE", altRight: true },
|
"€": { key: "KeyE", altRight: true },
|
||||||
f: { key: "KeyF" },
|
f: { key: "KeyF" },
|
||||||
g: { key: "KeyG" },
|
g: { key: "KeyG" },
|
||||||
h: { key: "KeyH" },
|
h: { key: "KeyH" },
|
||||||
i: { key: "KeyI" },
|
i: { key: "KeyI" },
|
||||||
"ï": { key: "KeyI", accentKey: keyTrema },
|
ï: { key: "KeyI", accentKey: keyTrema },
|
||||||
"í": { key: "KeyI", accentKey: keyAcute },
|
í: { key: "KeyI", accentKey: keyAcute },
|
||||||
"î": { key: "KeyI", accentKey: keyHat },
|
î: { key: "KeyI", accentKey: keyHat },
|
||||||
"ì": { key: "KeyI", accentKey: keyGrave },
|
ì: { key: "KeyI", accentKey: keyGrave },
|
||||||
"ĩ": { key: "KeyI", accentKey: keyTilde },
|
ĩ: { key: "KeyI", accentKey: keyTilde },
|
||||||
j: { key: "KeyJ" },
|
j: { key: "KeyJ" },
|
||||||
k: { key: "KeyK" },
|
k: { key: "KeyK" },
|
||||||
l: { key: "KeyL" },
|
l: { key: "KeyL" },
|
||||||
m: { key: "KeyM" },
|
m: { key: "KeyM" },
|
||||||
n: { key: "KeyN" },
|
n: { key: "KeyN" },
|
||||||
o: { key: "KeyO" },
|
o: { key: "KeyO" },
|
||||||
"ó": { key: "KeyO", accentKey: keyAcute },
|
ó: { key: "KeyO", accentKey: keyAcute },
|
||||||
"ô": { key: "KeyO", accentKey: keyHat },
|
ô: { key: "KeyO", accentKey: keyHat },
|
||||||
"ò": { key: "KeyO", accentKey: keyGrave },
|
ò: { key: "KeyO", accentKey: keyGrave },
|
||||||
"õ": { key: "KeyO", accentKey: keyTilde },
|
õ: { key: "KeyO", accentKey: keyTilde },
|
||||||
p: { key: "KeyP" },
|
p: { key: "KeyP" },
|
||||||
q: { key: "KeyQ" },
|
q: { key: "KeyQ" },
|
||||||
r: { key: "KeyR" },
|
r: { key: "KeyR" },
|
||||||
s: { key: "KeyS" },
|
s: { key: "KeyS" },
|
||||||
t: { key: "KeyT" },
|
t: { key: "KeyT" },
|
||||||
u: { key: "KeyU" },
|
u: { key: "KeyU" },
|
||||||
"ú": { key: "KeyU", accentKey: keyAcute },
|
ú: { key: "KeyU", accentKey: keyAcute },
|
||||||
"û": { key: "KeyU", accentKey: keyHat },
|
û: { key: "KeyU", accentKey: keyHat },
|
||||||
"ù": { key: "KeyU", accentKey: keyGrave },
|
ù: { key: "KeyU", accentKey: keyGrave },
|
||||||
"ũ": { key: "KeyU", accentKey: keyTilde },
|
ũ: { key: "KeyU", accentKey: keyTilde },
|
||||||
v: { key: "KeyV" },
|
v: { key: "KeyV" },
|
||||||
w: { key: "KeyW" },
|
w: { key: "KeyW" },
|
||||||
x: { key: "KeyX" },
|
x: { key: "KeyX" },
|
||||||
|
|
@ -115,13 +115,13 @@ const chars = {
|
||||||
"+": { key: "Digit1", shift: true },
|
"+": { key: "Digit1", shift: true },
|
||||||
"|": { key: "Digit1", altRight: true },
|
"|": { key: "Digit1", altRight: true },
|
||||||
2: { key: "Digit2" },
|
2: { key: "Digit2" },
|
||||||
"\"": { key: "Digit2", shift: true },
|
'"': { key: "Digit2", shift: true },
|
||||||
"@": { key: "Digit2", altRight: true },
|
"@": { key: "Digit2", altRight: true },
|
||||||
3: { key: "Digit3" },
|
3: { key: "Digit3" },
|
||||||
"*": { key: "Digit3", shift: true },
|
"*": { key: "Digit3", shift: true },
|
||||||
"#": { key: "Digit3", altRight: true },
|
"#": { key: "Digit3", altRight: true },
|
||||||
4: { key: "Digit4" },
|
4: { key: "Digit4" },
|
||||||
"ç": { key: "Digit4", shift: true },
|
ç: { key: "Digit4", shift: true },
|
||||||
5: { key: "Digit5" },
|
5: { key: "Digit5" },
|
||||||
"%": { key: "Digit5", shift: true },
|
"%": { key: "Digit5", shift: true },
|
||||||
6: { key: "Digit6" },
|
6: { key: "Digit6" },
|
||||||
|
|
@ -139,17 +139,17 @@ const chars = {
|
||||||
"^": { key: "Equal", deadKey: true },
|
"^": { key: "Equal", deadKey: true },
|
||||||
"`": { key: "Equal", shift: true },
|
"`": { key: "Equal", shift: true },
|
||||||
"~": { key: "Equal", altRight: true, deadKey: true },
|
"~": { key: "Equal", altRight: true, deadKey: true },
|
||||||
"ü": { key: "BracketLeft" },
|
ü: { key: "BracketLeft" },
|
||||||
"è": { key: "BracketLeft", shift: true },
|
è: { key: "BracketLeft", shift: true },
|
||||||
"[": { key: "BracketLeft", altRight: true },
|
"[": { key: "BracketLeft", altRight: true },
|
||||||
"!": { key: "BracketRight", shift: true },
|
"!": { key: "BracketRight", shift: true },
|
||||||
"]": { key: "BracketRight", altRight: true },
|
"]": { key: "BracketRight", altRight: true },
|
||||||
"ö": { key: "Semicolon" },
|
ö: { key: "Semicolon" },
|
||||||
"é": { key: "Semicolon", shift: true },
|
é: { key: "Semicolon", shift: true },
|
||||||
"ä": { key: "Quote" },
|
ä: { key: "Quote" },
|
||||||
"à": { key: "Quote", shift: true },
|
à: { key: "Quote", shift: true },
|
||||||
"{": { key: "Quote", altRight: true },
|
"{": { key: "Quote", altRight: true },
|
||||||
"$": { key: "Backslash" },
|
$: { key: "Backslash" },
|
||||||
"£": { key: "Backslash", shift: true },
|
"£": { key: "Backslash", shift: true },
|
||||||
"}": { key: "Backslash", altRight: true },
|
"}": { key: "Backslash", altRight: true },
|
||||||
",": { key: "Comma" },
|
",": { key: "Comma" },
|
||||||
|
|
@ -157,7 +157,7 @@ const chars = {
|
||||||
".": { key: "Period" },
|
".": { key: "Period" },
|
||||||
":": { key: "Period", shift: true },
|
":": { key: "Period", shift: true },
|
||||||
"-": { key: "Slash" },
|
"-": { key: "Slash" },
|
||||||
"_": { key: "Slash", shift: true },
|
_: { key: "Slash", shift: true },
|
||||||
"<": { key: "IntlBackslash" },
|
"<": { key: "IntlBackslash" },
|
||||||
">": { key: "IntlBackslash", shift: true },
|
">": { key: "IntlBackslash", shift: true },
|
||||||
"\\": { key: "IntlBackslash", altRight: true },
|
"\\": { key: "IntlBackslash", altRight: true },
|
||||||
|
|
@ -184,5 +184,5 @@ export const de_CH: KeyboardLayout = {
|
||||||
keyDisplayMap: keyDisplayMap,
|
keyDisplayMap: keyDisplayMap,
|
||||||
// TODO need to localize these maps and layouts
|
// TODO need to localize these maps and layouts
|
||||||
modifierDisplayMap: en_US.modifierDisplayMap,
|
modifierDisplayMap: en_US.modifierDisplayMap,
|
||||||
virtualKeyboard: en_US.virtualKeyboard
|
virtualKeyboard: en_US.virtualKeyboard,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,23 +1,23 @@
|
||||||
import { KeyboardLayout, KeyCombo } from "../keyboardLayouts"
|
import { KeyboardLayout, KeyCombo } from "../keyboardLayouts";
|
||||||
|
|
||||||
import { en_US } from "./en_US" // for fallback of keyDisplayMap, modifierDisplayMap, and virtualKeyboard
|
import { en_US } from "./en_US"; // for fallback of keyDisplayMap, modifierDisplayMap, and virtualKeyboard
|
||||||
|
|
||||||
const name = "Deutsch";
|
const name = "Deutsch";
|
||||||
const isoCode = "de-DE";
|
const isoCode = "de-DE";
|
||||||
|
|
||||||
const keyAcute: KeyCombo = { key: "Equal" } // accent aigu (acute accent), mark ´ placed above the letter
|
const keyAcute: KeyCombo = { key: "Equal" }; // accent aigu (acute accent), mark ´ placed above the letter
|
||||||
const keyHat: KeyCombo = { key: "Backquote" } // accent circonflexe (accent hat), mark ^ placed above the letter
|
const keyHat: KeyCombo = { key: "Backquote" }; // accent circonflexe (accent hat), mark ^ placed above the letter
|
||||||
const keyGrave: KeyCombo = { key: "Equal", shift: true } // accent grave, mark ` placed above the letter
|
const keyGrave: KeyCombo = { key: "Equal", shift: true }; // accent grave, mark ` placed above the letter
|
||||||
|
|
||||||
const chars = {
|
const chars = {
|
||||||
a: { key: "KeyA" },
|
a: { key: "KeyA" },
|
||||||
"á": { key: "KeyA", accentKey: keyAcute },
|
á: { key: "KeyA", accentKey: keyAcute },
|
||||||
"â": { key: "KeyA", accentKey: keyHat },
|
â: { key: "KeyA", accentKey: keyHat },
|
||||||
"à": { key: "KeyA", accentKey: keyGrave },
|
à: { key: "KeyA", accentKey: keyGrave },
|
||||||
A: { key: "KeyA", shift: true },
|
A: { key: "KeyA", shift: true },
|
||||||
"Á": { key: "KeyA", shift: true, accentKey: keyAcute },
|
Á: { key: "KeyA", shift: true, accentKey: keyAcute },
|
||||||
"Â": { key: "KeyA", shift: true, accentKey: keyHat },
|
Â: { key: "KeyA", shift: true, accentKey: keyHat },
|
||||||
"À": { key: "KeyA", shift: true, accentKey: keyGrave },
|
À: { key: "KeyA", shift: true, accentKey: keyGrave },
|
||||||
"☺": { key: "KeyA", altRight: true }, // white smiling face ☺
|
"☺": { key: "KeyA", altRight: true }, // white smiling face ☺
|
||||||
b: { key: "KeyB" },
|
b: { key: "KeyB" },
|
||||||
B: { key: "KeyB", shift: true },
|
B: { key: "KeyB", shift: true },
|
||||||
|
|
@ -29,31 +29,31 @@ const chars = {
|
||||||
D: { key: "KeyD", shift: true },
|
D: { key: "KeyD", shift: true },
|
||||||
"′": { key: "KeyD", altRight: true }, // prime, mark ′ placed above the letter
|
"′": { key: "KeyD", altRight: true }, // prime, mark ′ placed above the letter
|
||||||
e: { key: "KeyE" },
|
e: { key: "KeyE" },
|
||||||
"é": { key: "KeyE", accentKey: keyAcute },
|
é: { key: "KeyE", accentKey: keyAcute },
|
||||||
"ê": { key: "KeyE", accentKey: keyHat },
|
ê: { key: "KeyE", accentKey: keyHat },
|
||||||
"è": { key: "KeyE", accentKey: keyGrave },
|
è: { key: "KeyE", accentKey: keyGrave },
|
||||||
"€": { key: "KeyE", altRight: true },
|
"€": { key: "KeyE", altRight: true },
|
||||||
E: { key: "KeyE", shift: true },
|
E: { key: "KeyE", shift: true },
|
||||||
"É": { key: "KeyE", shift: true, accentKey: keyAcute },
|
É: { key: "KeyE", shift: true, accentKey: keyAcute },
|
||||||
"Ê": { key: "KeyE", shift: true, accentKey: keyHat },
|
Ê: { key: "KeyE", shift: true, accentKey: keyHat },
|
||||||
"È": { key: "KeyE", shift: true, accentKey: keyGrave },
|
È: { key: "KeyE", shift: true, accentKey: keyGrave },
|
||||||
f: { key: "KeyF" },
|
f: { key: "KeyF" },
|
||||||
F: { key: "KeyF", shift: true },
|
F: { key: "KeyF", shift: true },
|
||||||
"˟": { key: "KeyF", deadKey: true, altRight: true }, // modifier letter cross accent, ˟
|
"˟": { key: "KeyF", deadKey: true, altRight: true }, // modifier letter cross accent, ˟
|
||||||
G: { key: "KeyG", shift: true },
|
G: { key: "KeyG", shift: true },
|
||||||
g: { key: "KeyG" },
|
g: { key: "KeyG" },
|
||||||
"ẞ": { key: "KeyG", altRight: true }, // capital sharp S, ẞ
|
ẞ: { key: "KeyG", altRight: true }, // capital sharp S, ẞ
|
||||||
h: { key: "KeyH" },
|
h: { key: "KeyH" },
|
||||||
H: { key: "KeyH", shift: true },
|
H: { key: "KeyH", shift: true },
|
||||||
"ˍ": { key: "KeyH", deadKey: true, altRight: true }, // modifier letter low macron, ˍ
|
ˍ: { key: "KeyH", deadKey: true, altRight: true }, // modifier letter low macron, ˍ
|
||||||
i: { key: "KeyI" },
|
i: { key: "KeyI" },
|
||||||
"í": { key: "KeyI", accentKey: keyAcute },
|
í: { key: "KeyI", accentKey: keyAcute },
|
||||||
"î": { key: "KeyI", accentKey: keyHat },
|
î: { key: "KeyI", accentKey: keyHat },
|
||||||
"ì": { key: "KeyI", accentKey: keyGrave },
|
ì: { key: "KeyI", accentKey: keyGrave },
|
||||||
I: { key: "KeyI", shift: true },
|
I: { key: "KeyI", shift: true },
|
||||||
"Í": { key: "KeyI", shift: true, accentKey: keyAcute },
|
Í: { key: "KeyI", shift: true, accentKey: keyAcute },
|
||||||
"Î": { key: "KeyI", shift: true, accentKey: keyHat },
|
Î: { key: "KeyI", shift: true, accentKey: keyHat },
|
||||||
"Ì": { key: "KeyI", shift: true, accentKey: keyGrave },
|
Ì: { key: "KeyI", shift: true, accentKey: keyGrave },
|
||||||
"˜": { key: "KeyI", deadKey: true, altRight: true }, // tilde accent, mark ˜ placed above the letter
|
"˜": { key: "KeyI", deadKey: true, altRight: true }, // tilde accent, mark ˜ placed above the letter
|
||||||
j: { key: "KeyJ" },
|
j: { key: "KeyJ" },
|
||||||
J: { key: "KeyJ", shift: true },
|
J: { key: "KeyJ", shift: true },
|
||||||
|
|
@ -62,25 +62,25 @@ const chars = {
|
||||||
K: { key: "KeyK", shift: true },
|
K: { key: "KeyK", shift: true },
|
||||||
l: { key: "KeyL" },
|
l: { key: "KeyL" },
|
||||||
L: { key: "KeyL", shift: true },
|
L: { key: "KeyL", shift: true },
|
||||||
"ˏ": { key: "KeyL", deadKey: true, altRight: true }, // modifier letter reversed comma, ˏ
|
ˏ: { key: "KeyL", deadKey: true, altRight: true }, // modifier letter reversed comma, ˏ
|
||||||
m: { key: "KeyM" },
|
m: { key: "KeyM" },
|
||||||
M: { key: "KeyM", shift: true },
|
M: { key: "KeyM", shift: true },
|
||||||
"µ": { key: "KeyM", altRight: true },
|
µ: { key: "KeyM", altRight: true },
|
||||||
n: { key: "KeyN" },
|
n: { key: "KeyN" },
|
||||||
N: { key: "KeyN", shift: true },
|
N: { key: "KeyN", shift: true },
|
||||||
"–": { key: "KeyN", altRight: true }, // en dash, –
|
"–": { key: "KeyN", altRight: true }, // en dash, –
|
||||||
o: { key: "KeyO" },
|
o: { key: "KeyO" },
|
||||||
"ó": { key: "KeyO", accentKey: keyAcute },
|
ó: { key: "KeyO", accentKey: keyAcute },
|
||||||
"ô": { key: "KeyO", accentKey: keyHat },
|
ô: { key: "KeyO", accentKey: keyHat },
|
||||||
"ò": { key: "KeyO", accentKey: keyGrave },
|
ò: { key: "KeyO", accentKey: keyGrave },
|
||||||
O: { key: "KeyO", shift: true },
|
O: { key: "KeyO", shift: true },
|
||||||
"Ó": { key: "KeyO", shift: true, accentKey: keyAcute },
|
Ó: { key: "KeyO", shift: true, accentKey: keyAcute },
|
||||||
"Ô": { key: "KeyO", shift: true, accentKey: keyHat },
|
Ô: { key: "KeyO", shift: true, accentKey: keyHat },
|
||||||
"Ò": { key: "KeyO", shift: true, accentKey: keyGrave },
|
Ò: { key: "KeyO", shift: true, accentKey: keyGrave },
|
||||||
"˚": { key: "KeyO", deadKey: true, altRight: true }, // ring above, ˚
|
"˚": { key: "KeyO", deadKey: true, altRight: true }, // ring above, ˚
|
||||||
p: { key: "KeyP" },
|
p: { key: "KeyP" },
|
||||||
P: { key: "KeyP", shift: true },
|
P: { key: "KeyP", shift: true },
|
||||||
"ˀ": { key: "KeyP", deadKey: true, altRight: true }, // modifier letter apostrophe, ʾ
|
ˀ: { key: "KeyP", deadKey: true, altRight: true }, // modifier letter apostrophe, ʾ
|
||||||
q: { key: "KeyQ" },
|
q: { key: "KeyQ" },
|
||||||
Q: { key: "KeyQ", shift: true },
|
Q: { key: "KeyQ", shift: true },
|
||||||
"@": { key: "KeyQ", altRight: true },
|
"@": { key: "KeyQ", altRight: true },
|
||||||
|
|
@ -92,15 +92,15 @@ const chars = {
|
||||||
"″": { key: "KeyS", altRight: true }, // double prime, mark ″ placed above the letter
|
"″": { key: "KeyS", altRight: true }, // double prime, mark ″ placed above the letter
|
||||||
T: { key: "KeyT", shift: true },
|
T: { key: "KeyT", shift: true },
|
||||||
t: { key: "KeyT" },
|
t: { key: "KeyT" },
|
||||||
"ˇ": { key: "KeyT", deadKey: true, altRight: true }, // caron/hacek accent, mark ˇ placed above the letter
|
ˇ: { key: "KeyT", deadKey: true, altRight: true }, // caron/hacek accent, mark ˇ placed above the letter
|
||||||
u: { key: "KeyU" },
|
u: { key: "KeyU" },
|
||||||
"ú": { key: "KeyU", accentKey: keyAcute },
|
ú: { key: "KeyU", accentKey: keyAcute },
|
||||||
"û": { key: "KeyU", accentKey: keyHat },
|
û: { key: "KeyU", accentKey: keyHat },
|
||||||
"ù": { key: "KeyU", accentKey: keyGrave },
|
ù: { key: "KeyU", accentKey: keyGrave },
|
||||||
U: { key: "KeyU", shift: true },
|
U: { key: "KeyU", shift: true },
|
||||||
"Ú": { key: "KeyU", shift: true, accentKey: keyAcute },
|
Ú: { key: "KeyU", shift: true, accentKey: keyAcute },
|
||||||
"Û": { key: "KeyU", shift: true, accentKey: keyHat },
|
Û: { key: "KeyU", shift: true, accentKey: keyHat },
|
||||||
"Ù": { key: "KeyU", shift: true, accentKey: keyGrave },
|
Ù: { key: "KeyU", shift: true, accentKey: keyGrave },
|
||||||
"˘": { key: "KeyU", deadKey: true, altRight: true }, // breve accent, ˘ placed above the letter
|
"˘": { key: "KeyU", deadKey: true, altRight: true }, // breve accent, ˘ placed above the letter
|
||||||
v: { key: "KeyV" },
|
v: { key: "KeyV" },
|
||||||
V: { key: "KeyV", shift: true },
|
V: { key: "KeyV", shift: true },
|
||||||
|
|
@ -125,7 +125,7 @@ const chars = {
|
||||||
"!": { key: "Digit1", shift: true },
|
"!": { key: "Digit1", shift: true },
|
||||||
"’": { key: "Digit1", altRight: true }, // single quote, mark ’ placed above the letter
|
"’": { key: "Digit1", altRight: true }, // single quote, mark ’ placed above the letter
|
||||||
2: { key: "Digit2" },
|
2: { key: "Digit2" },
|
||||||
"\"": { key: "Digit2", shift: true },
|
'"': { key: "Digit2", shift: true },
|
||||||
"²": { key: "Digit2", altRight: true },
|
"²": { key: "Digit2", altRight: true },
|
||||||
"<": { key: "Digit2", altRight: true }, // non-US < and >
|
"<": { key: "Digit2", altRight: true }, // non-US < and >
|
||||||
3: { key: "Digit3" },
|
3: { key: "Digit3" },
|
||||||
|
|
@ -133,8 +133,8 @@ const chars = {
|
||||||
"³": { key: "Digit3", altRight: true },
|
"³": { key: "Digit3", altRight: true },
|
||||||
">": { key: "Digit3", altRight: true }, // non-US < and >
|
">": { key: "Digit3", altRight: true }, // non-US < and >
|
||||||
4: { key: "Digit4" },
|
4: { key: "Digit4" },
|
||||||
"$": { key: "Digit4", shift: true },
|
$: { key: "Digit4", shift: true },
|
||||||
"—": { key: "Digit4", altRight: true }, // em dash, —
|
"—": { key: "Digit4", altRight: true }, // em dash, —
|
||||||
5: { key: "Digit5" },
|
5: { key: "Digit5" },
|
||||||
"%": { key: "Digit5", shift: true },
|
"%": { key: "Digit5", shift: true },
|
||||||
"¡": { key: "Digit5", altRight: true }, // inverted exclamation mark, ¡
|
"¡": { key: "Digit5", altRight: true }, // inverted exclamation mark, ¡
|
||||||
|
|
@ -153,25 +153,25 @@ const chars = {
|
||||||
0: { key: "Digit0" },
|
0: { key: "Digit0" },
|
||||||
"=": { key: "Digit0", shift: true },
|
"=": { key: "Digit0", shift: true },
|
||||||
"}": { key: "Digit0", altRight: true },
|
"}": { key: "Digit0", altRight: true },
|
||||||
"ß": { key: "Minus" },
|
ß: { key: "Minus" },
|
||||||
"?": { key: "Minus", shift: true },
|
"?": { key: "Minus", shift: true },
|
||||||
"\\": { key: "Minus", altRight: true },
|
"\\": { key: "Minus", altRight: true },
|
||||||
"´": { key: "Equal", deadKey: true }, // accent acute, mark ´ placed above the letter
|
"´": { key: "Equal", deadKey: true }, // accent acute, mark ´ placed above the letter
|
||||||
"`": { key: "Equal", shift: true, deadKey: true }, // accent grave, mark ` placed above the letter
|
"`": { key: "Equal", shift: true, deadKey: true }, // accent grave, mark ` placed above the letter
|
||||||
"˙": { key: "Equal", control: true, altRight: true, deadKey: true }, // acute accent, mark ˙ placed above the letter
|
"˙": { key: "Equal", control: true, altRight: true, deadKey: true }, // acute accent, mark ˙ placed above the letter
|
||||||
"ü": { key: "BracketLeft" },
|
ü: { key: "BracketLeft" },
|
||||||
"Ü": { key: "BracketLeft", shift: true },
|
Ü: { key: "BracketLeft", shift: true },
|
||||||
Escape: { key: "BracketLeft", control: true },
|
Escape: { key: "BracketLeft", control: true },
|
||||||
"ʼ": { key: "BracketLeft", altRight: true }, // modifier letter apostrophe, ʼ
|
ʼ: { key: "BracketLeft", altRight: true }, // modifier letter apostrophe, ʼ
|
||||||
"+": { key: "BracketRight" },
|
"+": { key: "BracketRight" },
|
||||||
"*": { key: "BracketRight", shift: true },
|
"*": { key: "BracketRight", shift: true },
|
||||||
Control: { key: "BracketRight", control: true },
|
Control: { key: "BracketRight", control: true },
|
||||||
"~": { key: "BracketRight", altRight: true },
|
"~": { key: "BracketRight", altRight: true },
|
||||||
"ö": { key: "Semicolon" },
|
ö: { key: "Semicolon" },
|
||||||
"Ö": { key: "Semicolon", shift: true },
|
Ö: { key: "Semicolon", shift: true },
|
||||||
"ˌ": { key: "Semicolon", deadkey: true, altRight: true }, // modifier letter low vertical line, ˌ
|
ˌ: { key: "Semicolon", deadkey: true, altRight: true }, // modifier letter low vertical line, ˌ
|
||||||
"ä": { key: "Quote" },
|
ä: { key: "Quote" },
|
||||||
"Ä": { key: "Quote", shift: true },
|
Ä: { key: "Quote", shift: true },
|
||||||
"˗": { key: "Quote", deadKey: true, altRight: true }, // modifier letter minus sign, ˗
|
"˗": { key: "Quote", deadKey: true, altRight: true }, // modifier letter minus sign, ˗
|
||||||
"#": { key: "Backslash" },
|
"#": { key: "Backslash" },
|
||||||
"'": { key: "Backslash", shift: true },
|
"'": { key: "Backslash", shift: true },
|
||||||
|
|
@ -183,7 +183,7 @@ const chars = {
|
||||||
":": { key: "Period", shift: true },
|
":": { key: "Period", shift: true },
|
||||||
"·": { key: "Period", altRight: true }, // middle dot, ·
|
"·": { key: "Period", altRight: true }, // middle dot, ·
|
||||||
"-": { key: "Slash" },
|
"-": { key: "Slash" },
|
||||||
"_": { key: "Slash", shift: true },
|
_: { key: "Slash", shift: true },
|
||||||
"\u00ad": { key: "Slash", altRight: true }, // soft hyphen,
|
"\u00ad": { key: "Slash", altRight: true }, // soft hyphen,
|
||||||
" ": { key: "Space" },
|
" ": { key: "Space" },
|
||||||
"\n": { key: "Enter" },
|
"\n": { key: "Enter" },
|
||||||
|
|
@ -237,7 +237,7 @@ export const keyDisplayMap: Record<string, string> = {
|
||||||
"(Backslash)": "'",
|
"(Backslash)": "'",
|
||||||
|
|
||||||
// Shifted Numbers
|
// Shifted Numbers
|
||||||
"(Digit2)": "\"",
|
"(Digit2)": '"',
|
||||||
"(Digit3)": "§",
|
"(Digit3)": "§",
|
||||||
"(Digit6)": "&",
|
"(Digit6)": "&",
|
||||||
"(Digit7)": "/",
|
"(Digit7)": "/",
|
||||||
|
|
@ -265,7 +265,7 @@ export const keyDisplayMap: Record<string, string> = {
|
||||||
PrintScreen: "Druck",
|
PrintScreen: "Druck",
|
||||||
ScrollLock: "Rollen",
|
ScrollLock: "Rollen",
|
||||||
"(Pause)": "Unterbr",
|
"(Pause)": "Unterbr",
|
||||||
}
|
};
|
||||||
|
|
||||||
export const modifierDisplayMap: Record<string, string> = {
|
export const modifierDisplayMap: Record<string, string> = {
|
||||||
ShiftLeft: "Umschalt (links)",
|
ShiftLeft: "Umschalt (links)",
|
||||||
|
|
@ -298,25 +298,15 @@ export const virtualKeyboard = {
|
||||||
"CapsLock (KeyA) (KeyS) (KeyD) (KeyF) (KeyG) (KeyH) (KeyJ) (KeyK) (KeyL) (Semicolon) (Quote) Enter",
|
"CapsLock (KeyA) (KeyS) (KeyD) (KeyF) (KeyG) (KeyH) (KeyJ) (KeyK) (KeyL) (Semicolon) (Quote) Enter",
|
||||||
"ShiftLeft (KeyZ) (KeyX) (KeyC) (KeyV) (KeyB) (KeyN) (KeyM) (Comma) (Period) (Slash) ShiftRight",
|
"ShiftLeft (KeyZ) (KeyX) (KeyC) (KeyV) (KeyB) (KeyN) (KeyM) (Comma) (Period) (Slash) ShiftRight",
|
||||||
"ControlLeft MetaLeft AltLeft Space AltGr MetaRight Menu ControlRight",
|
"ControlLeft MetaLeft AltLeft Space AltGr MetaRight Menu ControlRight",
|
||||||
]
|
],
|
||||||
},
|
},
|
||||||
control: {
|
control: {
|
||||||
default: [
|
default: ["PrintScreen ScrollLock Pause", "Insert Home PageUp", "Delete End PageDown"],
|
||||||
"PrintScreen ScrollLock Pause",
|
shift: ["(PrintScreen) ScrollLock (Pause)", "Insert Home PageUp", "Delete End PageDown"],
|
||||||
"Insert Home PageUp",
|
|
||||||
"Delete End PageDown"
|
|
||||||
],
|
|
||||||
shift: [
|
|
||||||
"(PrintScreen) ScrollLock (Pause)",
|
|
||||||
"Insert Home PageUp",
|
|
||||||
"Delete End PageDown"
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
|
|
||||||
arrows: {
|
arrows: {
|
||||||
default: [
|
default: [" ArrowUp ", "ArrowLeft ArrowDown ArrowRight"],
|
||||||
" ArrowUp ",
|
|
||||||
"ArrowLeft ArrowDown ArrowRight"],
|
|
||||||
},
|
},
|
||||||
|
|
||||||
numpad: {
|
numpad: {
|
||||||
|
|
@ -334,8 +324,8 @@ export const virtualKeyboard = {
|
||||||
"End ArrowDown PageDown NumpadEnter",
|
"End ArrowDown PageDown NumpadEnter",
|
||||||
"NumpadInsert NumpadDelete",
|
"NumpadInsert NumpadDelete",
|
||||||
],
|
],
|
||||||
}
|
},
|
||||||
}
|
};
|
||||||
|
|
||||||
export const de_DE: KeyboardLayout = {
|
export const de_DE: KeyboardLayout = {
|
||||||
isoCode: isoCode,
|
isoCode: isoCode,
|
||||||
|
|
@ -343,5 +333,5 @@ export const de_DE: KeyboardLayout = {
|
||||||
chars: chars,
|
chars: chars,
|
||||||
keyDisplayMap: keyDisplayMap,
|
keyDisplayMap: keyDisplayMap,
|
||||||
modifierDisplayMap: modifierDisplayMap,
|
modifierDisplayMap: modifierDisplayMap,
|
||||||
virtualKeyboard: virtualKeyboard
|
virtualKeyboard: virtualKeyboard,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { KeyboardLayout, KeyCombo } from "../keyboardLayouts"
|
import { KeyboardLayout, KeyCombo } from "../keyboardLayouts";
|
||||||
|
|
||||||
import { en_US } from "./en_US" // for fallback of keyDisplayMap, modifierDisplayMap, and virtualKeyboard
|
import { en_US } from "./en_US"; // for fallback of keyDisplayMap, modifierDisplayMap, and virtualKeyboard
|
||||||
|
|
||||||
const name = "English (UK)";
|
const name = "English (UK)";
|
||||||
const isoCode = "en-UK";
|
const isoCode = "en-UK";
|
||||||
|
|
@ -61,7 +61,7 @@ const chars = {
|
||||||
1: { key: "Digit1" },
|
1: { key: "Digit1" },
|
||||||
"!": { key: "Digit1", shift: true },
|
"!": { key: "Digit1", shift: true },
|
||||||
2: { key: "Digit2" },
|
2: { key: "Digit2" },
|
||||||
"\"": { key: "Digit2", shift: true },
|
'"': { key: "Digit2", shift: true },
|
||||||
3: { key: "Digit3" },
|
3: { key: "Digit3" },
|
||||||
"£": { key: "Digit3", shift: true },
|
"£": { key: "Digit3", shift: true },
|
||||||
4: { key: "Digit4" },
|
4: { key: "Digit4" },
|
||||||
|
|
@ -84,7 +84,7 @@ const chars = {
|
||||||
"=": { key: "Equal" },
|
"=": { key: "Equal" },
|
||||||
"+": { key: "Equal", shift: true },
|
"+": { key: "Equal", shift: true },
|
||||||
"'": { key: "Quote" },
|
"'": { key: "Quote" },
|
||||||
'@': { key: "Quote", shift: true },
|
"@": { key: "Quote", shift: true },
|
||||||
",": { key: "Comma" },
|
",": { key: "Comma" },
|
||||||
"<": { key: "Comma", shift: true },
|
"<": { key: "Comma", shift: true },
|
||||||
"/": { key: "Slash" },
|
"/": { key: "Slash" },
|
||||||
|
|
@ -107,7 +107,7 @@ const chars = {
|
||||||
"\n": { key: "Enter" },
|
"\n": { key: "Enter" },
|
||||||
Enter: { key: "Enter" },
|
Enter: { key: "Enter" },
|
||||||
Tab: { key: "Tab" },
|
Tab: { key: "Tab" },
|
||||||
} as Record<string, KeyCombo>
|
} as Record<string, KeyCombo>;
|
||||||
|
|
||||||
export const en_UK: KeyboardLayout = {
|
export const en_UK: KeyboardLayout = {
|
||||||
isoCode: isoCode,
|
isoCode: isoCode,
|
||||||
|
|
@ -116,5 +116,5 @@ export const en_UK: KeyboardLayout = {
|
||||||
// TODO need to localize these maps and layouts
|
// TODO need to localize these maps and layouts
|
||||||
keyDisplayMap: en_US.keyDisplayMap,
|
keyDisplayMap: en_US.keyDisplayMap,
|
||||||
modifierDisplayMap: en_US.modifierDisplayMap,
|
modifierDisplayMap: en_US.modifierDisplayMap,
|
||||||
virtualKeyboard: en_US.virtualKeyboard
|
virtualKeyboard: en_US.virtualKeyboard,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { KeyboardLayout, KeyCombo } from "../keyboardLayouts"
|
import { KeyboardLayout, KeyCombo } from "../keyboardLayouts";
|
||||||
|
|
||||||
const name = "English (US)";
|
const name = "English (US)";
|
||||||
const isoCode = "en-US";
|
const isoCode = "en-US";
|
||||||
|
|
@ -99,13 +99,13 @@ export const chars = {
|
||||||
">": { key: "Period", shift: true },
|
">": { key: "Period", shift: true },
|
||||||
";": { key: "Semicolon" },
|
";": { key: "Semicolon" },
|
||||||
":": { key: "Semicolon", shift: true },
|
":": { key: "Semicolon", shift: true },
|
||||||
"¶": { key: "Semicolon", altRight: true }, // pilcrow sign
|
"¶": { key: "Semicolon", altRight: true }, // pilcrow sign
|
||||||
"[": { key: "BracketLeft" },
|
"[": { key: "BracketLeft" },
|
||||||
"{": { key: "BracketLeft", shift: true },
|
"{": { key: "BracketLeft", shift: true },
|
||||||
"«": { key: "BracketLeft", altRight: true }, // double left quote sign
|
"«": { key: "BracketLeft", altRight: true }, // double left quote sign
|
||||||
"]": { key: "BracketRight" },
|
"]": { key: "BracketRight" },
|
||||||
"}": { key: "BracketRight", shift: true },
|
"}": { key: "BracketRight", shift: true },
|
||||||
"»": { key: "BracketRight", altRight: true }, // double right quote sign
|
"»": { key: "BracketRight", altRight: true }, // double right quote sign
|
||||||
"\\": { key: "Backslash" },
|
"\\": { key: "Backslash" },
|
||||||
"|": { key: "Backslash", shift: true },
|
"|": { key: "Backslash", shift: true },
|
||||||
"¬": { key: "Backslash", altRight: true }, // not sign
|
"¬": { key: "Backslash", altRight: true }, // not sign
|
||||||
|
|
@ -125,7 +125,7 @@ export const chars = {
|
||||||
Break: { key: "Pause", shift: true },
|
Break: { key: "Pause", shift: true },
|
||||||
Insert: { key: "Insert" },
|
Insert: { key: "Insert" },
|
||||||
Delete: { key: "Delete" },
|
Delete: { key: "Delete" },
|
||||||
} as Record<string, KeyCombo>
|
} as Record<string, KeyCombo>;
|
||||||
|
|
||||||
export const modifierDisplayMap: Record<string, string> = {
|
export const modifierDisplayMap: Record<string, string> = {
|
||||||
ControlLeft: "Left Ctrl",
|
ControlLeft: "Left Ctrl",
|
||||||
|
|
@ -173,28 +173,84 @@ export const keyDisplayMap: Record<string, string> = {
|
||||||
Tab: "Tab ⇥",
|
Tab: "Tab ⇥",
|
||||||
|
|
||||||
// Letters
|
// Letters
|
||||||
KeyA: "a", KeyB: "b", KeyC: "c", KeyD: "d", KeyE: "e",
|
KeyA: "a",
|
||||||
KeyF: "f", KeyG: "g", KeyH: "h", KeyI: "i", KeyJ: "j",
|
KeyB: "b",
|
||||||
KeyK: "k", KeyL: "l", KeyM: "m", KeyN: "n", KeyO: "o",
|
KeyC: "c",
|
||||||
KeyP: "p", KeyQ: "q", KeyR: "r", KeyS: "s", KeyT: "t",
|
KeyD: "d",
|
||||||
KeyU: "u", KeyV: "v", KeyW: "w", KeyX: "x", KeyY: "y",
|
KeyE: "e",
|
||||||
|
KeyF: "f",
|
||||||
|
KeyG: "g",
|
||||||
|
KeyH: "h",
|
||||||
|
KeyI: "i",
|
||||||
|
KeyJ: "j",
|
||||||
|
KeyK: "k",
|
||||||
|
KeyL: "l",
|
||||||
|
KeyM: "m",
|
||||||
|
KeyN: "n",
|
||||||
|
KeyO: "o",
|
||||||
|
KeyP: "p",
|
||||||
|
KeyQ: "q",
|
||||||
|
KeyR: "r",
|
||||||
|
KeyS: "s",
|
||||||
|
KeyT: "t",
|
||||||
|
KeyU: "u",
|
||||||
|
KeyV: "v",
|
||||||
|
KeyW: "w",
|
||||||
|
KeyX: "x",
|
||||||
|
KeyY: "y",
|
||||||
KeyZ: "z",
|
KeyZ: "z",
|
||||||
|
|
||||||
// Capital letters
|
// Capital letters
|
||||||
"(KeyA)": "A", "(KeyB)": "B", "(KeyC)": "C", "(KeyD)": "D", "(KeyE)": "E",
|
"(KeyA)": "A",
|
||||||
"(KeyF)": "F", "(KeyG)": "G", "(KeyH)": "H", "(KeyI)": "I", "(KeyJ)": "J",
|
"(KeyB)": "B",
|
||||||
"(KeyK)": "K", "(KeyL)": "L", "(KeyM)": "M", "(KeyN)": "N", "(KeyO)": "O",
|
"(KeyC)": "C",
|
||||||
"(KeyP)": "P", "(KeyQ)": "Q", "(KeyR)": "R", "(KeyS)": "S", "(KeyT)": "T",
|
"(KeyD)": "D",
|
||||||
"(KeyU)": "U", "(KeyV)": "V", "(KeyW)": "W", "(KeyX)": "X", "(KeyY)": "Y",
|
"(KeyE)": "E",
|
||||||
|
"(KeyF)": "F",
|
||||||
|
"(KeyG)": "G",
|
||||||
|
"(KeyH)": "H",
|
||||||
|
"(KeyI)": "I",
|
||||||
|
"(KeyJ)": "J",
|
||||||
|
"(KeyK)": "K",
|
||||||
|
"(KeyL)": "L",
|
||||||
|
"(KeyM)": "M",
|
||||||
|
"(KeyN)": "N",
|
||||||
|
"(KeyO)": "O",
|
||||||
|
"(KeyP)": "P",
|
||||||
|
"(KeyQ)": "Q",
|
||||||
|
"(KeyR)": "R",
|
||||||
|
"(KeyS)": "S",
|
||||||
|
"(KeyT)": "T",
|
||||||
|
"(KeyU)": "U",
|
||||||
|
"(KeyV)": "V",
|
||||||
|
"(KeyW)": "W",
|
||||||
|
"(KeyX)": "X",
|
||||||
|
"(KeyY)": "Y",
|
||||||
"(KeyZ)": "Z",
|
"(KeyZ)": "Z",
|
||||||
|
|
||||||
// Numbers
|
// Numbers
|
||||||
Digit1: "1", Digit2: "2", Digit3: "3", Digit4: "4", Digit5: "5",
|
Digit1: "1",
|
||||||
Digit6: "6", Digit7: "7", Digit8: "8", Digit9: "9", Digit0: "0",
|
Digit2: "2",
|
||||||
|
Digit3: "3",
|
||||||
|
Digit4: "4",
|
||||||
|
Digit5: "5",
|
||||||
|
Digit6: "6",
|
||||||
|
Digit7: "7",
|
||||||
|
Digit8: "8",
|
||||||
|
Digit9: "9",
|
||||||
|
Digit0: "0",
|
||||||
|
|
||||||
// Shifted Numbers
|
// Shifted Numbers
|
||||||
"(Digit1)": "!", "(Digit2)": "@", "(Digit3)": "#", "(Digit4)": "$", "(Digit5)": "%",
|
"(Digit1)": "!",
|
||||||
"(Digit6)": "^", "(Digit7)": "&", "(Digit8)": "*", "(Digit9)": "(", "(Digit0)": ")",
|
"(Digit2)": "@",
|
||||||
|
"(Digit3)": "#",
|
||||||
|
"(Digit4)": "$",
|
||||||
|
"(Digit5)": "%",
|
||||||
|
"(Digit6)": "^",
|
||||||
|
"(Digit7)": "&",
|
||||||
|
"(Digit8)": "*",
|
||||||
|
"(Digit9)": "(",
|
||||||
|
"(Digit0)": ")",
|
||||||
|
|
||||||
// Symbols
|
// Symbols
|
||||||
Minus: "-",
|
Minus: "-",
|
||||||
|
|
@ -210,7 +266,7 @@ export const keyDisplayMap: Record<string, string> = {
|
||||||
Semicolon: ";",
|
Semicolon: ";",
|
||||||
"(Semicolon)": ":",
|
"(Semicolon)": ":",
|
||||||
Quote: "'",
|
Quote: "'",
|
||||||
"(Quote)": "\"",
|
"(Quote)": '"',
|
||||||
Comma: ",",
|
Comma: ",",
|
||||||
"(Comma)": "<",
|
"(Comma)": "<",
|
||||||
Period: ".",
|
Period: ".",
|
||||||
|
|
@ -222,23 +278,49 @@ export const keyDisplayMap: Record<string, string> = {
|
||||||
IntlBackslash: "\\",
|
IntlBackslash: "\\",
|
||||||
|
|
||||||
// Function keys
|
// Function keys
|
||||||
F1: "F1", F2: "F2", F3: "F3", F4: "F4",
|
F1: "F1",
|
||||||
F5: "F5", F6: "F6", F7: "F7", F8: "F8",
|
F2: "F2",
|
||||||
F9: "F9", F10: "F10", F11: "F11", F12: "F12",
|
F3: "F3",
|
||||||
|
F4: "F4",
|
||||||
|
F5: "F5",
|
||||||
|
F6: "F6",
|
||||||
|
F7: "F7",
|
||||||
|
F8: "F8",
|
||||||
|
F9: "F9",
|
||||||
|
F10: "F10",
|
||||||
|
F11: "F11",
|
||||||
|
F12: "F12",
|
||||||
|
|
||||||
// Numpad
|
// Numpad
|
||||||
Numpad0: "Num 0", Numpad1: "Num 1", Numpad2: "Num 2",
|
Numpad0: "Num 0",
|
||||||
Numpad3: "Num 3", Numpad4: "Num 4", Numpad5: "Num 5",
|
Numpad1: "Num 1",
|
||||||
Numpad6: "Num 6", Numpad7: "Num 7", Numpad8: "Num 8",
|
Numpad2: "Num 2",
|
||||||
Numpad9: "Num 9", NumpadAdd: "Num +", NumpadSubtract: "Num -",
|
Numpad3: "Num 3",
|
||||||
NumpadMultiply: "Num *", NumpadDivide: "Num /", NumpadDecimal: "Num .",
|
Numpad4: "Num 4",
|
||||||
NumpadEqual: "Num =", NumpadEnter: "Num Enter", NumpadInsert: "Ins",
|
Numpad5: "Num 5",
|
||||||
NumpadDelete: "Del", NumLock: "Num Lock",
|
Numpad6: "Num 6",
|
||||||
|
Numpad7: "Num 7",
|
||||||
|
Numpad8: "Num 8",
|
||||||
|
Numpad9: "Num 9",
|
||||||
|
NumpadAdd: "Num +",
|
||||||
|
NumpadSubtract: "Num -",
|
||||||
|
NumpadMultiply: "Num *",
|
||||||
|
NumpadDivide: "Num /",
|
||||||
|
NumpadDecimal: "Num .",
|
||||||
|
NumpadEqual: "Num =",
|
||||||
|
NumpadEnter: "Num Enter",
|
||||||
|
NumpadInsert: "Ins",
|
||||||
|
NumpadDelete: "Del",
|
||||||
|
NumLock: "Num Lock",
|
||||||
|
|
||||||
// Modals
|
// Modals
|
||||||
PrintScreen: "Prt Sc", ScrollLock: "Scr Lk", Pause: "Pause",
|
PrintScreen: "Prt Sc",
|
||||||
"(PrintScreen)": "Sys Rq", "(Pause)": "Break",
|
ScrollLock: "Scr Lk",
|
||||||
SystemRequest: "Sys Rq", Break: "Break"
|
Pause: "Pause",
|
||||||
|
"(PrintScreen)": "Sys Rq",
|
||||||
|
"(Pause)": "Break",
|
||||||
|
SystemRequest: "Sys Rq",
|
||||||
|
Break: "Break",
|
||||||
};
|
};
|
||||||
|
|
||||||
export const virtualKeyboard = {
|
export const virtualKeyboard = {
|
||||||
|
|
@ -260,25 +342,15 @@ export const virtualKeyboard = {
|
||||||
"CapsLock (KeyA) (KeyS) (KeyD) (KeyF) (KeyG) (KeyH) (KeyJ) (KeyK) (KeyL) (Semicolon) (Quote) Enter",
|
"CapsLock (KeyA) (KeyS) (KeyD) (KeyF) (KeyG) (KeyH) (KeyJ) (KeyK) (KeyL) (Semicolon) (Quote) Enter",
|
||||||
"ShiftLeft (KeyZ) (KeyX) (KeyC) (KeyV) (KeyB) (KeyN) (KeyM) (Comma) (Period) (Slash) ShiftRight",
|
"ShiftLeft (KeyZ) (KeyX) (KeyC) (KeyV) (KeyB) (KeyN) (KeyM) (Comma) (Period) (Slash) ShiftRight",
|
||||||
"ControlLeft MetaLeft AltLeft Space AltGr MetaRight Menu ControlRight",
|
"ControlLeft MetaLeft AltLeft Space AltGr MetaRight Menu ControlRight",
|
||||||
]
|
],
|
||||||
},
|
},
|
||||||
control: {
|
control: {
|
||||||
default: [
|
default: ["PrintScreen ScrollLock Pause", "Insert Home PageUp", "Delete End PageDown"],
|
||||||
"PrintScreen ScrollLock Pause",
|
shift: ["(PrintScreen) ScrollLock (Pause)", "Insert Home PageUp", "Delete End PageDown"],
|
||||||
"Insert Home PageUp",
|
|
||||||
"Delete End PageDown"
|
|
||||||
],
|
|
||||||
shift: [
|
|
||||||
"(PrintScreen) ScrollLock (Pause)",
|
|
||||||
"Insert Home PageUp",
|
|
||||||
"Delete End PageDown"
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
|
|
||||||
arrows: {
|
arrows: {
|
||||||
default: [
|
default: ["ArrowUp", "ArrowLeft ArrowDown ArrowRight"],
|
||||||
"ArrowUp",
|
|
||||||
"ArrowLeft ArrowDown ArrowRight"],
|
|
||||||
},
|
},
|
||||||
|
|
||||||
numpad: {
|
numpad: {
|
||||||
|
|
@ -296,8 +368,8 @@ export const virtualKeyboard = {
|
||||||
"End ArrowDown PageDown NumpadEnter",
|
"End ArrowDown PageDown NumpadEnter",
|
||||||
"NumpadInsert NumpadDelete",
|
"NumpadInsert NumpadDelete",
|
||||||
],
|
],
|
||||||
}
|
},
|
||||||
}
|
};
|
||||||
|
|
||||||
export const en_US: KeyboardLayout = {
|
export const en_US: KeyboardLayout = {
|
||||||
isoCode,
|
isoCode,
|
||||||
|
|
@ -305,7 +377,5 @@ export const en_US: KeyboardLayout = {
|
||||||
chars,
|
chars,
|
||||||
keyDisplayMap,
|
keyDisplayMap,
|
||||||
modifierDisplayMap,
|
modifierDisplayMap,
|
||||||
virtualKeyboard
|
virtualKeyboard,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,134 +1,134 @@
|
||||||
import { KeyboardLayout, KeyCombo } from "../keyboardLayouts"
|
import { KeyboardLayout, KeyCombo } from "../keyboardLayouts";
|
||||||
|
|
||||||
import { en_US } from "./en_US" // for fallback of keyDisplayMap, modifierDisplayMap, and virtualKeyboard
|
import { en_US } from "./en_US"; // for fallback of keyDisplayMap, modifierDisplayMap, and virtualKeyboard
|
||||||
|
|
||||||
const name = "Español";
|
const name = "Español";
|
||||||
const isoCode = "es-ES";
|
const isoCode = "es-ES";
|
||||||
|
|
||||||
const keyTrema: KeyCombo = { key: "Quote", shift: true } // tréma (umlaut), two dots placed above a vowel
|
const keyTrema: KeyCombo = { key: "Quote", shift: true }; // tréma (umlaut), two dots placed above a vowel
|
||||||
const keyAcute: KeyCombo = { key: "Quote" } // accent aigu (acute accent), mark ´ placed above the letter
|
const keyAcute: KeyCombo = { key: "Quote" }; // accent aigu (acute accent), mark ´ placed above the letter
|
||||||
const keyHat: KeyCombo = { key: "BracketRight", shift: true } // accent circonflexe (accent hat), mark ^ placed above the letter
|
const keyHat: KeyCombo = { key: "BracketRight", shift: true }; // accent circonflexe (accent hat), mark ^ placed above the letter
|
||||||
const keyGrave: KeyCombo = { key: "BracketRight" } // accent grave, mark ` placed above the letter
|
const keyGrave: KeyCombo = { key: "BracketRight" }; // accent grave, mark ` placed above the letter
|
||||||
const keyTilde: KeyCombo = { key: "Key4", altRight: true } // tilde, mark ~ placed above the letter
|
const keyTilde: KeyCombo = { key: "Key4", altRight: true }; // tilde, mark ~ placed above the letter
|
||||||
|
|
||||||
const chars = {
|
const chars = {
|
||||||
A: { key: "KeyA", shift: true },
|
A: { key: "KeyA", shift: true },
|
||||||
"Ä": { key: "KeyA", shift: true, accentKey: keyTrema },
|
Ä: { key: "KeyA", shift: true, accentKey: keyTrema },
|
||||||
"Á": { key: "KeyA", shift: true, accentKey: keyAcute },
|
Á: { key: "KeyA", shift: true, accentKey: keyAcute },
|
||||||
"Â": { key: "KeyA", shift: true, accentKey: keyHat },
|
Â: { key: "KeyA", shift: true, accentKey: keyHat },
|
||||||
"À": { key: "KeyA", shift: true, accentKey: keyGrave },
|
À: { key: "KeyA", shift: true, accentKey: keyGrave },
|
||||||
"Ã": { key: "KeyA", shift: true, accentKey: keyTilde },
|
Ã: { key: "KeyA", shift: true, accentKey: keyTilde },
|
||||||
B: { key: "KeyB", shift: true },
|
B: { key: "KeyB", shift: true },
|
||||||
C: { key: "KeyC", shift: true },
|
C: { key: "KeyC", shift: true },
|
||||||
D: { key: "KeyD", shift: true },
|
D: { key: "KeyD", shift: true },
|
||||||
E: { key: "KeyE", shift: true },
|
E: { key: "KeyE", shift: true },
|
||||||
"Ë": { key: "KeyE", shift: true, accentKey: keyTrema },
|
Ë: { key: "KeyE", shift: true, accentKey: keyTrema },
|
||||||
"É": { key: "KeyE", shift: true, accentKey: keyAcute },
|
É: { key: "KeyE", shift: true, accentKey: keyAcute },
|
||||||
"Ê": { key: "KeyE", shift: true, accentKey: keyHat },
|
Ê: { key: "KeyE", shift: true, accentKey: keyHat },
|
||||||
"È": { key: "KeyE", shift: true, accentKey: keyGrave },
|
È: { key: "KeyE", shift: true, accentKey: keyGrave },
|
||||||
"Ẽ": { key: "KeyE", shift: true, accentKey: keyTilde },
|
Ẽ: { key: "KeyE", shift: true, accentKey: keyTilde },
|
||||||
F: { key: "KeyF", shift: true },
|
F: { key: "KeyF", shift: true },
|
||||||
G: { key: "KeyG", shift: true },
|
G: { key: "KeyG", shift: true },
|
||||||
H: { key: "KeyH", shift: true },
|
H: { key: "KeyH", shift: true },
|
||||||
I: { key: "KeyI", shift: true },
|
I: { key: "KeyI", shift: true },
|
||||||
"Ï": { key: "KeyI", shift: true, accentKey: keyTrema },
|
Ï: { key: "KeyI", shift: true, accentKey: keyTrema },
|
||||||
"Í": { key: "KeyI", shift: true, accentKey: keyAcute },
|
Í: { key: "KeyI", shift: true, accentKey: keyAcute },
|
||||||
"Î": { key: "KeyI", shift: true, accentKey: keyHat },
|
Î: { key: "KeyI", shift: true, accentKey: keyHat },
|
||||||
"Ì": { key: "KeyI", shift: true, accentKey: keyGrave },
|
Ì: { key: "KeyI", shift: true, accentKey: keyGrave },
|
||||||
"Ĩ": { key: "KeyI", shift: true, accentKey: keyTilde },
|
Ĩ: { key: "KeyI", shift: true, accentKey: keyTilde },
|
||||||
J: { key: "KeyJ", shift: true },
|
J: { key: "KeyJ", shift: true },
|
||||||
K: { key: "KeyK", shift: true },
|
K: { key: "KeyK", shift: true },
|
||||||
L: { key: "KeyL", shift: true },
|
L: { key: "KeyL", shift: true },
|
||||||
M: { key: "KeyM", shift: true },
|
M: { key: "KeyM", shift: true },
|
||||||
N: { key: "KeyN", shift: true },
|
N: { key: "KeyN", shift: true },
|
||||||
O: { key: "KeyO", shift: true },
|
O: { key: "KeyO", shift: true },
|
||||||
"Ö": { key: "KeyO", shift: true, accentKey: keyTrema },
|
Ö: { key: "KeyO", shift: true, accentKey: keyTrema },
|
||||||
"Ó": { key: "KeyO", shift: true, accentKey: keyAcute },
|
Ó: { key: "KeyO", shift: true, accentKey: keyAcute },
|
||||||
"Ô": { key: "KeyO", shift: true, accentKey: keyHat },
|
Ô: { key: "KeyO", shift: true, accentKey: keyHat },
|
||||||
"Ò": { key: "KeyO", shift: true, accentKey: keyGrave },
|
Ò: { key: "KeyO", shift: true, accentKey: keyGrave },
|
||||||
"Õ": { key: "KeyO", shift: true, accentKey: keyTilde },
|
Õ: { key: "KeyO", shift: true, accentKey: keyTilde },
|
||||||
P: { key: "KeyP", shift: true },
|
P: { key: "KeyP", shift: true },
|
||||||
Q: { key: "KeyQ", shift: true },
|
Q: { key: "KeyQ", shift: true },
|
||||||
R: { key: "KeyR", shift: true },
|
R: { key: "KeyR", shift: true },
|
||||||
S: { key: "KeyS", shift: true },
|
S: { key: "KeyS", shift: true },
|
||||||
T: { key: "KeyT", shift: true },
|
T: { key: "KeyT", shift: true },
|
||||||
U: { key: "KeyU", shift: true },
|
U: { key: "KeyU", shift: true },
|
||||||
"Ü": { key: "KeyU", shift: true, accentKey: keyTrema },
|
Ü: { key: "KeyU", shift: true, accentKey: keyTrema },
|
||||||
"Ú": { key: "KeyU", shift: true, accentKey: keyAcute },
|
Ú: { key: "KeyU", shift: true, accentKey: keyAcute },
|
||||||
"Û": { key: "KeyU", shift: true, accentKey: keyHat },
|
Û: { key: "KeyU", shift: true, accentKey: keyHat },
|
||||||
"Ù": { key: "KeyU", shift: true, accentKey: keyGrave },
|
Ù: { key: "KeyU", shift: true, accentKey: keyGrave },
|
||||||
"Ũ": { key: "KeyU", shift: true, accentKey: keyTilde },
|
Ũ: { key: "KeyU", shift: true, accentKey: keyTilde },
|
||||||
V: { key: "KeyV", shift: true },
|
V: { key: "KeyV", shift: true },
|
||||||
W: { key: "KeyW", shift: true },
|
W: { key: "KeyW", shift: true },
|
||||||
X: { key: "KeyX", shift: true },
|
X: { key: "KeyX", shift: true },
|
||||||
Y: { key: "KeyY", shift: true },
|
Y: { key: "KeyY", shift: true },
|
||||||
Z: { key: "KeyZ", shift: true },
|
Z: { key: "KeyZ", shift: true },
|
||||||
a: { key: "KeyA" },
|
a: { key: "KeyA" },
|
||||||
"ä": { key: "KeyA", accentKey: keyTrema },
|
ä: { key: "KeyA", accentKey: keyTrema },
|
||||||
"á": { key: "KeyA", accentKey: keyAcute },
|
á: { key: "KeyA", accentKey: keyAcute },
|
||||||
"â": { key: "KeyA", accentKey: keyHat },
|
â: { key: "KeyA", accentKey: keyHat },
|
||||||
"à": { key: "KeyA", accentKey: keyGrave },
|
à: { key: "KeyA", accentKey: keyGrave },
|
||||||
"ã": { key: "KeyA", accentKey: keyTilde },
|
ã: { key: "KeyA", accentKey: keyTilde },
|
||||||
b: { key: "KeyB" },
|
b: { key: "KeyB" },
|
||||||
c: { key: "KeyC" },
|
c: { key: "KeyC" },
|
||||||
d: { key: "KeyD" },
|
d: { key: "KeyD" },
|
||||||
e: { key: "KeyE" },
|
e: { key: "KeyE" },
|
||||||
"ë": { key: "KeyE", accentKey: keyTrema },
|
ë: { key: "KeyE", accentKey: keyTrema },
|
||||||
"é": { key: "KeyE", accentKey: keyAcute },
|
é: { key: "KeyE", accentKey: keyAcute },
|
||||||
"ê": { key: "KeyE", accentKey: keyHat },
|
ê: { key: "KeyE", accentKey: keyHat },
|
||||||
"è": { key: "KeyE", accentKey: keyGrave },
|
è: { key: "KeyE", accentKey: keyGrave },
|
||||||
"ẽ": { key: "KeyE", accentKey: keyTilde },
|
ẽ: { key: "KeyE", accentKey: keyTilde },
|
||||||
"€": { key: "KeyE", altRight: true },
|
"€": { key: "KeyE", altRight: true },
|
||||||
f: { key: "KeyF" },
|
f: { key: "KeyF" },
|
||||||
g: { key: "KeyG" },
|
g: { key: "KeyG" },
|
||||||
h: { key: "KeyH" },
|
h: { key: "KeyH" },
|
||||||
i: { key: "KeyI" },
|
i: { key: "KeyI" },
|
||||||
"ï": { key: "KeyI", accentKey: keyTrema },
|
ï: { key: "KeyI", accentKey: keyTrema },
|
||||||
"í": { key: "KeyI", accentKey: keyAcute },
|
í: { key: "KeyI", accentKey: keyAcute },
|
||||||
"î": { key: "KeyI", accentKey: keyHat },
|
î: { key: "KeyI", accentKey: keyHat },
|
||||||
"ì": { key: "KeyI", accentKey: keyGrave },
|
ì: { key: "KeyI", accentKey: keyGrave },
|
||||||
"ĩ": { key: "KeyI", accentKey: keyTilde },
|
ĩ: { key: "KeyI", accentKey: keyTilde },
|
||||||
j: { key: "KeyJ" },
|
j: { key: "KeyJ" },
|
||||||
k: { key: "KeyK" },
|
k: { key: "KeyK" },
|
||||||
l: { key: "KeyL" },
|
l: { key: "KeyL" },
|
||||||
m: { key: "KeyM" },
|
m: { key: "KeyM" },
|
||||||
n: { key: "KeyN" },
|
n: { key: "KeyN" },
|
||||||
o: { key: "KeyO" },
|
o: { key: "KeyO" },
|
||||||
"ö": { key: "KeyO", accentKey: keyTrema },
|
ö: { key: "KeyO", accentKey: keyTrema },
|
||||||
"ó": { key: "KeyO", accentKey: keyAcute },
|
ó: { key: "KeyO", accentKey: keyAcute },
|
||||||
"ô": { key: "KeyO", accentKey: keyHat },
|
ô: { key: "KeyO", accentKey: keyHat },
|
||||||
"ò": { key: "KeyO", accentKey: keyGrave },
|
ò: { key: "KeyO", accentKey: keyGrave },
|
||||||
"õ": { key: "KeyO", accentKey: keyTilde },
|
õ: { key: "KeyO", accentKey: keyTilde },
|
||||||
p: { key: "KeyP" },
|
p: { key: "KeyP" },
|
||||||
q: { key: "KeyQ" },
|
q: { key: "KeyQ" },
|
||||||
r: { key: "KeyR" },
|
r: { key: "KeyR" },
|
||||||
s: { key: "KeyS" },
|
s: { key: "KeyS" },
|
||||||
t: { key: "KeyT" },
|
t: { key: "KeyT" },
|
||||||
u: { key: "KeyU" },
|
u: { key: "KeyU" },
|
||||||
"ü": { key: "KeyU", accentKey: keyTrema },
|
ü: { key: "KeyU", accentKey: keyTrema },
|
||||||
"ú": { key: "KeyU", accentKey: keyAcute },
|
ú: { key: "KeyU", accentKey: keyAcute },
|
||||||
"û": { key: "KeyU", accentKey: keyHat },
|
û: { key: "KeyU", accentKey: keyHat },
|
||||||
"ù": { key: "KeyU", accentKey: keyGrave },
|
ù: { key: "KeyU", accentKey: keyGrave },
|
||||||
"ũ": { key: "KeyU", accentKey: keyTilde },
|
ũ: { key: "KeyU", accentKey: keyTilde },
|
||||||
v: { key: "KeyV" },
|
v: { key: "KeyV" },
|
||||||
w: { key: "KeyW" },
|
w: { key: "KeyW" },
|
||||||
x: { key: "KeyX" },
|
x: { key: "KeyX" },
|
||||||
y: { key: "KeyY" },
|
y: { key: "KeyY" },
|
||||||
z: { key: "KeyZ" },
|
z: { key: "KeyZ" },
|
||||||
"º": { key: "Backquote" },
|
º: { key: "Backquote" },
|
||||||
"ª": { key: "Backquote", shift: true },
|
ª: { key: "Backquote", shift: true },
|
||||||
"\\": { key: "Backquote", altRight: true },
|
"\\": { key: "Backquote", altRight: true },
|
||||||
1: { key: "Digit1" },
|
1: { key: "Digit1" },
|
||||||
"!": { key: "Digit1", shift: true },
|
"!": { key: "Digit1", shift: true },
|
||||||
"|": { key: "Digit1", altRight: true },
|
"|": { key: "Digit1", altRight: true },
|
||||||
2: { key: "Digit2" },
|
2: { key: "Digit2" },
|
||||||
"\"": { key: "Digit2", shift: true },
|
'"': { key: "Digit2", shift: true },
|
||||||
"@": { key: "Digit2", altRight: true },
|
"@": { key: "Digit2", altRight: true },
|
||||||
3: { key: "Digit3" },
|
3: { key: "Digit3" },
|
||||||
"·": { key: "Digit3", shift: true },
|
"·": { key: "Digit3", shift: true },
|
||||||
"#": { key: "Digit3", altRight: true },
|
"#": { key: "Digit3", altRight: true },
|
||||||
4: { key: "Digit4" },
|
4: { key: "Digit4" },
|
||||||
"$": { key: "Digit4", shift: true },
|
$: { key: "Digit4", shift: true },
|
||||||
5: { key: "Digit5" },
|
5: { key: "Digit5" },
|
||||||
"%": { key: "Digit5", shift: true },
|
"%": { key: "Digit5", shift: true },
|
||||||
6: { key: "Digit6" },
|
6: { key: "Digit6" },
|
||||||
|
|
@ -150,18 +150,18 @@ const chars = {
|
||||||
"+": { key: "BracketRight" },
|
"+": { key: "BracketRight" },
|
||||||
"*": { key: "BracketRight", shift: true },
|
"*": { key: "BracketRight", shift: true },
|
||||||
"]": { key: "BracketRight", altRight: true },
|
"]": { key: "BracketRight", altRight: true },
|
||||||
"ñ": { key: "Semicolon" },
|
ñ: { key: "Semicolon" },
|
||||||
"Ñ": { key: "Semicolon", shift: true },
|
Ñ: { key: "Semicolon", shift: true },
|
||||||
"{": { key: "Quote", altRight: true },
|
"{": { key: "Quote", altRight: true },
|
||||||
"ç": { key: "Backslash" },
|
ç: { key: "Backslash" },
|
||||||
"Ç": { key: "Backslash", shift: true },
|
Ç: { key: "Backslash", shift: true },
|
||||||
"}": { key: "Backslash", altRight: true },
|
"}": { key: "Backslash", altRight: true },
|
||||||
",": { key: "Comma" },
|
",": { key: "Comma" },
|
||||||
";": { key: "Comma", shift: true },
|
";": { key: "Comma", shift: true },
|
||||||
".": { key: "Period" },
|
".": { key: "Period" },
|
||||||
":": { key: "Period", shift: true },
|
":": { key: "Period", shift: true },
|
||||||
"-": { key: "Slash" },
|
"-": { key: "Slash" },
|
||||||
"_": { key: "Slash", shift: true },
|
_: { key: "Slash", shift: true },
|
||||||
"<": { key: "IntlBackslash" },
|
"<": { key: "IntlBackslash" },
|
||||||
">": { key: "IntlBackslash", shift: true },
|
">": { key: "IntlBackslash", shift: true },
|
||||||
" ": { key: "Space" },
|
" ": { key: "Space" },
|
||||||
|
|
@ -177,5 +177,5 @@ export const es_ES: KeyboardLayout = {
|
||||||
// TODO need to localize these maps and layouts
|
// TODO need to localize these maps and layouts
|
||||||
keyDisplayMap: en_US.keyDisplayMap,
|
keyDisplayMap: en_US.keyDisplayMap,
|
||||||
modifierDisplayMap: en_US.modifierDisplayMap,
|
modifierDisplayMap: en_US.modifierDisplayMap,
|
||||||
virtualKeyboard: en_US.virtualKeyboard
|
virtualKeyboard: en_US.virtualKeyboard,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,111 +1,111 @@
|
||||||
import { KeyboardLayout, KeyCombo } from "../keyboardLayouts"
|
import { KeyboardLayout, KeyCombo } from "../keyboardLayouts";
|
||||||
|
|
||||||
import { en_US } from "./en_US" // for fallback of keyDisplayMap, modifierDisplayMap, and virtualKeyboard
|
import { en_US } from "./en_US"; // for fallback of keyDisplayMap, modifierDisplayMap, and virtualKeyboard
|
||||||
|
|
||||||
const name = "Belgisch Nederlands";
|
const name = "Belgisch Nederlands";
|
||||||
const isoCode = "nl-BE";
|
const isoCode = "nl-BE";
|
||||||
|
|
||||||
const keyTrema: KeyCombo = { key: "BracketLeft", shift: true } // tréma (umlaut), two dots placed above a vowel
|
const keyTrema: KeyCombo = { key: "BracketLeft", shift: true }; // tréma (umlaut), two dots placed above a vowel
|
||||||
const keyHat: KeyCombo = { key: "BracketLeft" } // accent circonflexe (accent hat), mark ^ placed above the letter
|
const keyHat: KeyCombo = { key: "BracketLeft" }; // accent circonflexe (accent hat), mark ^ placed above the letter
|
||||||
const keyAcute: KeyCombo = { key: "Semicolon", altRight: true } // accent aigu (acute accent), mark ´ placed above the letter
|
const keyAcute: KeyCombo = { key: "Semicolon", altRight: true }; // accent aigu (acute accent), mark ´ placed above the letter
|
||||||
const keyGrave: KeyCombo = { key: "Quote", shift: true } // accent grave, mark ` placed above the letter
|
const keyGrave: KeyCombo = { key: "Quote", shift: true }; // accent grave, mark ` placed above the letter
|
||||||
const keyTilde: KeyCombo = { key: "Slash", altRight: true } // tilde, mark ~ placed above the letter
|
const keyTilde: KeyCombo = { key: "Slash", altRight: true }; // tilde, mark ~ placed above the letter
|
||||||
|
|
||||||
const chars = {
|
const chars = {
|
||||||
A: { key: "KeyQ", shift: true },
|
A: { key: "KeyQ", shift: true },
|
||||||
"Ä": { key: "KeyQ", shift: true, accentKey: keyTrema },
|
Ä: { key: "KeyQ", shift: true, accentKey: keyTrema },
|
||||||
"Â": { key: "KeyQ", shift: true, accentKey: keyHat },
|
Â: { key: "KeyQ", shift: true, accentKey: keyHat },
|
||||||
"Á": { key: "KeyQ", shift: true, accentKey: keyAcute },
|
Á: { key: "KeyQ", shift: true, accentKey: keyAcute },
|
||||||
"À": { key: "KeyQ", shift: true, accentKey: keyGrave },
|
À: { key: "KeyQ", shift: true, accentKey: keyGrave },
|
||||||
"Ã": { key: "KeyQ", shift: true, accentKey: keyTilde },
|
Ã: { key: "KeyQ", shift: true, accentKey: keyTilde },
|
||||||
B: { key: "KeyB", shift: true },
|
B: { key: "KeyB", shift: true },
|
||||||
C: { key: "KeyC", shift: true },
|
C: { key: "KeyC", shift: true },
|
||||||
D: { key: "KeyD", shift: true },
|
D: { key: "KeyD", shift: true },
|
||||||
E: { key: "KeyE", shift: true },
|
E: { key: "KeyE", shift: true },
|
||||||
"Ë": { key: "KeyE", shift: true, accentKey: keyTrema },
|
Ë: { key: "KeyE", shift: true, accentKey: keyTrema },
|
||||||
"Ê": { key: "KeyE", shift: true, accentKey: keyHat },
|
Ê: { key: "KeyE", shift: true, accentKey: keyHat },
|
||||||
"É": { key: "KeyE", shift: true, accentKey: keyAcute },
|
É: { key: "KeyE", shift: true, accentKey: keyAcute },
|
||||||
"È": { key: "KeyE", shift: true, accentKey: keyGrave },
|
È: { key: "KeyE", shift: true, accentKey: keyGrave },
|
||||||
"Ẽ": { key: "KeyE", shift: true, accentKey: keyTilde },
|
Ẽ: { key: "KeyE", shift: true, accentKey: keyTilde },
|
||||||
F: { key: "KeyF", shift: true },
|
F: { key: "KeyF", shift: true },
|
||||||
G: { key: "KeyG", shift: true },
|
G: { key: "KeyG", shift: true },
|
||||||
H: { key: "KeyH", shift: true },
|
H: { key: "KeyH", shift: true },
|
||||||
I: { key: "KeyI", shift: true },
|
I: { key: "KeyI", shift: true },
|
||||||
"Ï": { key: "KeyI", shift: true, accentKey: keyTrema },
|
Ï: { key: "KeyI", shift: true, accentKey: keyTrema },
|
||||||
"Î": { key: "KeyI", shift: true, accentKey: keyHat },
|
Î: { key: "KeyI", shift: true, accentKey: keyHat },
|
||||||
"Í": { key: "KeyI", shift: true, accentKey: keyAcute },
|
Í: { key: "KeyI", shift: true, accentKey: keyAcute },
|
||||||
"Ì": { key: "KeyI", shift: true, accentKey: keyGrave },
|
Ì: { key: "KeyI", shift: true, accentKey: keyGrave },
|
||||||
"Ĩ": { key: "KeyI", shift: true, accentKey: keyTilde },
|
Ĩ: { key: "KeyI", shift: true, accentKey: keyTilde },
|
||||||
J: { key: "KeyJ", shift: true },
|
J: { key: "KeyJ", shift: true },
|
||||||
K: { key: "KeyK", shift: true },
|
K: { key: "KeyK", shift: true },
|
||||||
L: { key: "KeyL", shift: true },
|
L: { key: "KeyL", shift: true },
|
||||||
M: { key: "Semicolon", shift: true },
|
M: { key: "Semicolon", shift: true },
|
||||||
N: { key: "KeyN", shift: true },
|
N: { key: "KeyN", shift: true },
|
||||||
O: { key: "KeyO", shift: true },
|
O: { key: "KeyO", shift: true },
|
||||||
"Ö": { key: "KeyO", shift: true, accentKey: keyTrema },
|
Ö: { key: "KeyO", shift: true, accentKey: keyTrema },
|
||||||
"Ô": { key: "KeyO", shift: true, accentKey: keyHat },
|
Ô: { key: "KeyO", shift: true, accentKey: keyHat },
|
||||||
"Ó": { key: "KeyO", shift: true, accentKey: keyAcute },
|
Ó: { key: "KeyO", shift: true, accentKey: keyAcute },
|
||||||
"Ò": { key: "KeyO", shift: true, accentKey: keyGrave },
|
Ò: { key: "KeyO", shift: true, accentKey: keyGrave },
|
||||||
"Õ": { key: "KeyO", shift: true, accentKey: keyTilde },
|
Õ: { key: "KeyO", shift: true, accentKey: keyTilde },
|
||||||
P: { key: "KeyP", shift: true },
|
P: { key: "KeyP", shift: true },
|
||||||
Q: { key: "KeyA", shift: true },
|
Q: { key: "KeyA", shift: true },
|
||||||
R: { key: "KeyR", shift: true },
|
R: { key: "KeyR", shift: true },
|
||||||
S: { key: "KeyS", shift: true },
|
S: { key: "KeyS", shift: true },
|
||||||
T: { key: "KeyT", shift: true },
|
T: { key: "KeyT", shift: true },
|
||||||
U: { key: "KeyU", shift: true },
|
U: { key: "KeyU", shift: true },
|
||||||
"Ü": { key: "KeyU", shift: true, accentKey: keyTrema },
|
Ü: { key: "KeyU", shift: true, accentKey: keyTrema },
|
||||||
"Û": { key: "KeyU", shift: true, accentKey: keyHat },
|
Û: { key: "KeyU", shift: true, accentKey: keyHat },
|
||||||
"Ú": { key: "KeyU", shift: true, accentKey: keyAcute },
|
Ú: { key: "KeyU", shift: true, accentKey: keyAcute },
|
||||||
"Ù": { key: "KeyU", shift: true, accentKey: keyGrave },
|
Ù: { key: "KeyU", shift: true, accentKey: keyGrave },
|
||||||
"Ũ": { key: "KeyU", shift: true, accentKey: keyTilde },
|
Ũ: { key: "KeyU", shift: true, accentKey: keyTilde },
|
||||||
V: { key: "KeyV", shift: true },
|
V: { key: "KeyV", shift: true },
|
||||||
W: { key: "KeyW", shift: true },
|
W: { key: "KeyW", shift: true },
|
||||||
X: { key: "KeyX", shift: true },
|
X: { key: "KeyX", shift: true },
|
||||||
Y: { key: "KeyZ", shift: true },
|
Y: { key: "KeyZ", shift: true },
|
||||||
Z: { key: "KeyY", shift: true },
|
Z: { key: "KeyY", shift: true },
|
||||||
a: { key: "KeyQ" },
|
a: { key: "KeyQ" },
|
||||||
"ä": { key: "KeyQ", accentKey: keyTrema },
|
ä: { key: "KeyQ", accentKey: keyTrema },
|
||||||
"â": { key: "KeyQ", accentKey: keyHat },
|
â: { key: "KeyQ", accentKey: keyHat },
|
||||||
"á": { key: "KeyQ", accentKey: keyAcute },
|
á: { key: "KeyQ", accentKey: keyAcute },
|
||||||
"ã": { key: "KeyQ", accentKey: keyTilde },
|
ã: { key: "KeyQ", accentKey: keyTilde },
|
||||||
b: { key: "KeyB" },
|
b: { key: "KeyB" },
|
||||||
c: { key: "KeyC" },
|
c: { key: "KeyC" },
|
||||||
d: { key: "KeyD" },
|
d: { key: "KeyD" },
|
||||||
e: { key: "KeyE" },
|
e: { key: "KeyE" },
|
||||||
"ë": { key: "KeyE", accentKey: keyTrema },
|
ë: { key: "KeyE", accentKey: keyTrema },
|
||||||
"ê": { key: "KeyE", accentKey: keyHat },
|
ê: { key: "KeyE", accentKey: keyHat },
|
||||||
"ẽ": { key: "KeyE", accentKey: keyTilde },
|
ẽ: { key: "KeyE", accentKey: keyTilde },
|
||||||
"€": { key: "KeyE", altRight: true },
|
"€": { key: "KeyE", altRight: true },
|
||||||
f: { key: "KeyF" },
|
f: { key: "KeyF" },
|
||||||
g: { key: "KeyG" },
|
g: { key: "KeyG" },
|
||||||
h: { key: "KeyH" },
|
h: { key: "KeyH" },
|
||||||
i: { key: "KeyI" },
|
i: { key: "KeyI" },
|
||||||
"ï": { key: "KeyI", accentKey: keyTrema },
|
ï: { key: "KeyI", accentKey: keyTrema },
|
||||||
"î": { key: "KeyI", accentKey: keyHat },
|
î: { key: "KeyI", accentKey: keyHat },
|
||||||
"í": { key: "KeyI", accentKey: keyAcute },
|
í: { key: "KeyI", accentKey: keyAcute },
|
||||||
"ì": { key: "KeyI", accentKey: keyGrave },
|
ì: { key: "KeyI", accentKey: keyGrave },
|
||||||
"ĩ": { key: "KeyI", accentKey: keyTilde },
|
ĩ: { key: "KeyI", accentKey: keyTilde },
|
||||||
j: { key: "KeyJ" },
|
j: { key: "KeyJ" },
|
||||||
k: { key: "KeyK" },
|
k: { key: "KeyK" },
|
||||||
l: { key: "KeyL" },
|
l: { key: "KeyL" },
|
||||||
m: { key: "Semicolon" },
|
m: { key: "Semicolon" },
|
||||||
n: { key: "KeyN" },
|
n: { key: "KeyN" },
|
||||||
o: { key: "KeyO" },
|
o: { key: "KeyO" },
|
||||||
"ö": { key: "KeyO", accentKey: keyTrema },
|
ö: { key: "KeyO", accentKey: keyTrema },
|
||||||
"ó": { key: "KeyO", accentKey: keyAcute },
|
ó: { key: "KeyO", accentKey: keyAcute },
|
||||||
"ô": { key: "KeyO", accentKey: keyHat },
|
ô: { key: "KeyO", accentKey: keyHat },
|
||||||
"ò": { key: "KeyO", accentKey: keyGrave },
|
ò: { key: "KeyO", accentKey: keyGrave },
|
||||||
"õ": { key: "KeyO", accentKey: keyTilde },
|
õ: { key: "KeyO", accentKey: keyTilde },
|
||||||
p: { key: "KeyP" },
|
p: { key: "KeyP" },
|
||||||
q: { key: "KeyA" },
|
q: { key: "KeyA" },
|
||||||
r: { key: "KeyR" },
|
r: { key: "KeyR" },
|
||||||
s: { key: "KeyS" },
|
s: { key: "KeyS" },
|
||||||
t: { key: "KeyT" },
|
t: { key: "KeyT" },
|
||||||
u: { key: "KeyU" },
|
u: { key: "KeyU" },
|
||||||
"ü": { key: "KeyU", accentKey: keyTrema },
|
ü: { key: "KeyU", accentKey: keyTrema },
|
||||||
"û": { key: "KeyU", accentKey: keyHat },
|
û: { key: "KeyU", accentKey: keyHat },
|
||||||
"ú": { key: "KeyU", accentKey: keyAcute },
|
ú: { key: "KeyU", accentKey: keyAcute },
|
||||||
"ũ": { key: "KeyU", accentKey: keyTilde },
|
ũ: { key: "KeyU", accentKey: keyTilde },
|
||||||
v: { key: "KeyV" },
|
v: { key: "KeyV" },
|
||||||
w: { key: "KeyW" },
|
w: { key: "KeyW" },
|
||||||
x: { key: "KeyX" },
|
x: { key: "KeyX" },
|
||||||
|
|
@ -116,10 +116,10 @@ const chars = {
|
||||||
"&": { key: "Digit1" },
|
"&": { key: "Digit1" },
|
||||||
1: { key: "Digit1", shift: true },
|
1: { key: "Digit1", shift: true },
|
||||||
"|": { key: "Digit1", altRight: true },
|
"|": { key: "Digit1", altRight: true },
|
||||||
"é": { key: "Digit2" },
|
é: { key: "Digit2" },
|
||||||
2: { key: "Digit2", shift: true },
|
2: { key: "Digit2", shift: true },
|
||||||
"@": { key: "Digit2", altRight: true },
|
"@": { key: "Digit2", altRight: true },
|
||||||
"\"": { key: "Digit3" },
|
'"': { key: "Digit3" },
|
||||||
3: { key: "Digit3", shift: true },
|
3: { key: "Digit3", shift: true },
|
||||||
"#": { key: "Digit3", altRight: true },
|
"#": { key: "Digit3", altRight: true },
|
||||||
"'": { key: "Digit4" },
|
"'": { key: "Digit4" },
|
||||||
|
|
@ -129,27 +129,27 @@ const chars = {
|
||||||
"§": { key: "Digit6" },
|
"§": { key: "Digit6" },
|
||||||
6: { key: "Digit6", shift: true },
|
6: { key: "Digit6", shift: true },
|
||||||
"^": { key: "Digit6", altRight: true },
|
"^": { key: "Digit6", altRight: true },
|
||||||
"è": { key: "Digit7" },
|
è: { key: "Digit7" },
|
||||||
7: { key: "Digit7", shift: true },
|
7: { key: "Digit7", shift: true },
|
||||||
"!": { key: "Digit8" },
|
"!": { key: "Digit8" },
|
||||||
8: { key: "Digit8", shift: true },
|
8: { key: "Digit8", shift: true },
|
||||||
"ç": { key: "Digit9" },
|
ç: { key: "Digit9" },
|
||||||
9: { key: "Digit9", shift: true },
|
9: { key: "Digit9", shift: true },
|
||||||
"{": { key: "Digit9", altRight: true },
|
"{": { key: "Digit9", altRight: true },
|
||||||
"à": { key: "Digit0" },
|
à: { key: "Digit0" },
|
||||||
0: { key: "Digit0", shift: true },
|
0: { key: "Digit0", shift: true },
|
||||||
"}": { key: "Digit0", altRight: true },
|
"}": { key: "Digit0", altRight: true },
|
||||||
")": { key: "Minus" },
|
")": { key: "Minus" },
|
||||||
"°": { key: "Minus", shift: true },
|
"°": { key: "Minus", shift: true },
|
||||||
"-": { key: "Equal", deadKey: true },
|
"-": { key: "Equal", deadKey: true },
|
||||||
"_": { key: "Equal", shift: true },
|
_: { key: "Equal", shift: true },
|
||||||
"[": { key: "BracketLeft", altRight: true },
|
"[": { key: "BracketLeft", altRight: true },
|
||||||
"$": { key: "BracketRight" },
|
$: { key: "BracketRight" },
|
||||||
"*": { key: "BracketRight", altRight: true },
|
"*": { key: "BracketRight", altRight: true },
|
||||||
"]": { key: "BracketRight", altRight: true },
|
"]": { key: "BracketRight", altRight: true },
|
||||||
"ù": { key: "Quote" },
|
ù: { key: "Quote" },
|
||||||
"%": { key: "Quote", shift: true },
|
"%": { key: "Quote", shift: true },
|
||||||
"µ": { key: "Backslash" },
|
µ: { key: "Backslash" },
|
||||||
"£": { key: "Backslash", shift: true },
|
"£": { key: "Backslash", shift: true },
|
||||||
",": { key: "KeyM" },
|
",": { key: "KeyM" },
|
||||||
"?": { key: "KeyM", shift: true },
|
"?": { key: "KeyM", shift: true },
|
||||||
|
|
@ -176,5 +176,5 @@ export const fr_BE: KeyboardLayout = {
|
||||||
// TODO need to localize these maps and layouts
|
// TODO need to localize these maps and layouts
|
||||||
keyDisplayMap: en_US.keyDisplayMap,
|
keyDisplayMap: en_US.keyDisplayMap,
|
||||||
modifierDisplayMap: en_US.modifierDisplayMap,
|
modifierDisplayMap: en_US.modifierDisplayMap,
|
||||||
virtualKeyboard: en_US.virtualKeyboard
|
virtualKeyboard: en_US.virtualKeyboard,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,28 +1,28 @@
|
||||||
import { KeyboardLayout, KeyCombo } from "../keyboardLayouts"
|
import { KeyboardLayout, KeyCombo } from "../keyboardLayouts";
|
||||||
|
|
||||||
import { de_CH } from "./de_CH"
|
import { de_CH } from "./de_CH";
|
||||||
|
|
||||||
const name = "Français de Suisse";
|
const name = "Français de Suisse";
|
||||||
const isoCode = "fr-CH";
|
const isoCode = "fr-CH";
|
||||||
|
|
||||||
const chars = {
|
const chars = {
|
||||||
...de_CH.chars,
|
...de_CH.chars,
|
||||||
"è": { key: "BracketLeft" },
|
è: { key: "BracketLeft" },
|
||||||
"ü": { key: "BracketLeft", shift: true },
|
ü: { key: "BracketLeft", shift: true },
|
||||||
"é": { key: "Semicolon" },
|
é: { key: "Semicolon" },
|
||||||
"ö": { key: "Semicolon", shift: true },
|
ö: { key: "Semicolon", shift: true },
|
||||||
"à": { key: "Quote" },
|
à: { key: "Quote" },
|
||||||
"ä": { key: "Quote", shift: true },
|
ä: { key: "Quote", shift: true },
|
||||||
} as Record<string, KeyCombo>;
|
} as Record<string, KeyCombo>;
|
||||||
|
|
||||||
const keyDisplayMap = {
|
const keyDisplayMap = {
|
||||||
...de_CH.keyDisplayMap,
|
...de_CH.keyDisplayMap,
|
||||||
"BracketLeft": "è",
|
BracketLeft: "è",
|
||||||
"BracketLeftShift": "ü",
|
BracketLeftShift: "ü",
|
||||||
"Semicolon": "é",
|
Semicolon: "é",
|
||||||
"SemicolonShift": "ö",
|
SemicolonShift: "ö",
|
||||||
"Quote": "à",
|
Quote: "à",
|
||||||
"QuoteShift": "ä",
|
QuoteShift: "ä",
|
||||||
} as Record<string, string>;
|
} as Record<string, string>;
|
||||||
|
|
||||||
export const fr_CH: KeyboardLayout = {
|
export const fr_CH: KeyboardLayout = {
|
||||||
|
|
@ -32,5 +32,5 @@ export const fr_CH: KeyboardLayout = {
|
||||||
keyDisplayMap: keyDisplayMap,
|
keyDisplayMap: keyDisplayMap,
|
||||||
// TODO need to localize these maps and layouts
|
// TODO need to localize these maps and layouts
|
||||||
modifierDisplayMap: de_CH.modifierDisplayMap,
|
modifierDisplayMap: de_CH.modifierDisplayMap,
|
||||||
virtualKeyboard: de_CH.virtualKeyboard
|
virtualKeyboard: de_CH.virtualKeyboard,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,82 +1,82 @@
|
||||||
import { KeyboardLayout, KeyCombo } from "../keyboardLayouts"
|
import { KeyboardLayout, KeyCombo } from "../keyboardLayouts";
|
||||||
|
|
||||||
import { en_US } from "./en_US" // for fallback of keyDisplayMap, modifierDisplayMap, and virtualKeyboard
|
import { en_US } from "./en_US"; // for fallback of keyDisplayMap, modifierDisplayMap, and virtualKeyboard
|
||||||
|
|
||||||
const name = "Français";
|
const name = "Français";
|
||||||
const isoCode = "fr-FR";
|
const isoCode = "fr-FR";
|
||||||
|
|
||||||
const keyTrema: KeyCombo = { key: "BracketLeft", shift: true } // tréma (umlaut), two dots placed above a vowel
|
const keyTrema: KeyCombo = { key: "BracketLeft", shift: true }; // tréma (umlaut), two dots placed above a vowel
|
||||||
const keyHat: KeyCombo = { key: "BracketLeft" } // accent circonflexe (accent hat), mark ^ placed above the letter
|
const keyHat: KeyCombo = { key: "BracketLeft" }; // accent circonflexe (accent hat), mark ^ placed above the letter
|
||||||
|
|
||||||
const chars = {
|
const chars = {
|
||||||
A: { key: "KeyQ", shift: true },
|
A: { key: "KeyQ", shift: true },
|
||||||
"Ä": { key: "KeyQ", shift: true, accentKey: keyTrema },
|
Ä: { key: "KeyQ", shift: true, accentKey: keyTrema },
|
||||||
"Â": { key: "KeyQ", shift: true, accentKey: keyHat },
|
Â: { key: "KeyQ", shift: true, accentKey: keyHat },
|
||||||
B: { key: "KeyB", shift: true },
|
B: { key: "KeyB", shift: true },
|
||||||
C: { key: "KeyC", shift: true },
|
C: { key: "KeyC", shift: true },
|
||||||
D: { key: "KeyD", shift: true },
|
D: { key: "KeyD", shift: true },
|
||||||
E: { key: "KeyE", shift: true },
|
E: { key: "KeyE", shift: true },
|
||||||
"Ë": { key: "KeyE", shift: true, accentKey: keyTrema },
|
Ë: { key: "KeyE", shift: true, accentKey: keyTrema },
|
||||||
"Ê": { key: "KeyE", shift: true, accentKey: keyHat },
|
Ê: { key: "KeyE", shift: true, accentKey: keyHat },
|
||||||
F: { key: "KeyF", shift: true },
|
F: { key: "KeyF", shift: true },
|
||||||
G: { key: "KeyG", shift: true },
|
G: { key: "KeyG", shift: true },
|
||||||
H: { key: "KeyH", shift: true },
|
H: { key: "KeyH", shift: true },
|
||||||
I: { key: "KeyI", shift: true },
|
I: { key: "KeyI", shift: true },
|
||||||
"Ï": { key: "KeyI", shift: true, accentKey: keyTrema },
|
Ï: { key: "KeyI", shift: true, accentKey: keyTrema },
|
||||||
"Î": { key: "KeyI", shift: true, accentKey: keyHat },
|
Î: { key: "KeyI", shift: true, accentKey: keyHat },
|
||||||
J: { key: "KeyJ", shift: true },
|
J: { key: "KeyJ", shift: true },
|
||||||
K: { key: "KeyK", shift: true },
|
K: { key: "KeyK", shift: true },
|
||||||
L: { key: "KeyL", shift: true },
|
L: { key: "KeyL", shift: true },
|
||||||
M: { key: "Semicolon", shift: true },
|
M: { key: "Semicolon", shift: true },
|
||||||
N: { key: "KeyN", shift: true },
|
N: { key: "KeyN", shift: true },
|
||||||
O: { key: "KeyO", shift: true },
|
O: { key: "KeyO", shift: true },
|
||||||
"Ö": { key: "KeyO", shift: true, accentKey: keyTrema },
|
Ö: { key: "KeyO", shift: true, accentKey: keyTrema },
|
||||||
"Ô": { key: "KeyO", shift: true, accentKey: keyHat },
|
Ô: { key: "KeyO", shift: true, accentKey: keyHat },
|
||||||
P: { key: "KeyP", shift: true },
|
P: { key: "KeyP", shift: true },
|
||||||
Q: { key: "KeyA", shift: true },
|
Q: { key: "KeyA", shift: true },
|
||||||
R: { key: "KeyR", shift: true },
|
R: { key: "KeyR", shift: true },
|
||||||
S: { key: "KeyS", shift: true },
|
S: { key: "KeyS", shift: true },
|
||||||
T: { key: "KeyT", shift: true },
|
T: { key: "KeyT", shift: true },
|
||||||
U: { key: "KeyU", shift: true },
|
U: { key: "KeyU", shift: true },
|
||||||
"Ü": { key: "KeyU", shift: true, accentKey: keyTrema },
|
Ü: { key: "KeyU", shift: true, accentKey: keyTrema },
|
||||||
"Û": { key: "KeyU", shift: true, accentKey: keyHat },
|
Û: { key: "KeyU", shift: true, accentKey: keyHat },
|
||||||
V: { key: "KeyV", shift: true },
|
V: { key: "KeyV", shift: true },
|
||||||
W: { key: "KeyZ", shift: true },
|
W: { key: "KeyZ", shift: true },
|
||||||
X: { key: "KeyX", shift: true },
|
X: { key: "KeyX", shift: true },
|
||||||
Y: { key: "KeyY", shift: true },
|
Y: { key: "KeyY", shift: true },
|
||||||
Z: { key: "KeyW", shift: true },
|
Z: { key: "KeyW", shift: true },
|
||||||
a: { key: "KeyQ" },
|
a: { key: "KeyQ" },
|
||||||
"ä": { key: "KeyQ", accentKey: keyTrema },
|
ä: { key: "KeyQ", accentKey: keyTrema },
|
||||||
"â": { key: "KeyQ", accentKey: keyHat },
|
â: { key: "KeyQ", accentKey: keyHat },
|
||||||
b: { key: "KeyB" },
|
b: { key: "KeyB" },
|
||||||
c: { key: "KeyC" },
|
c: { key: "KeyC" },
|
||||||
d: { key: "KeyD" },
|
d: { key: "KeyD" },
|
||||||
e: { key: "KeyE" },
|
e: { key: "KeyE" },
|
||||||
"ë": { key: "KeyE", accentKey: keyTrema },
|
ë: { key: "KeyE", accentKey: keyTrema },
|
||||||
"ê": { key: "KeyE", accentKey: keyHat },
|
ê: { key: "KeyE", accentKey: keyHat },
|
||||||
"€": { key: "KeyE", altRight: true },
|
"€": { key: "KeyE", altRight: true },
|
||||||
f: { key: "KeyF" },
|
f: { key: "KeyF" },
|
||||||
g: { key: "KeyG" },
|
g: { key: "KeyG" },
|
||||||
h: { key: "KeyH" },
|
h: { key: "KeyH" },
|
||||||
i: { key: "KeyI" },
|
i: { key: "KeyI" },
|
||||||
"ï": { key: "KeyI", accentKey: keyTrema },
|
ï: { key: "KeyI", accentKey: keyTrema },
|
||||||
"î": { key: "KeyI", accentKey: keyHat },
|
î: { key: "KeyI", accentKey: keyHat },
|
||||||
j: { key: "KeyJ" },
|
j: { key: "KeyJ" },
|
||||||
k: { key: "KeyK" },
|
k: { key: "KeyK" },
|
||||||
l: { key: "KeyL" },
|
l: { key: "KeyL" },
|
||||||
m: { key: "Semicolon" },
|
m: { key: "Semicolon" },
|
||||||
n: { key: "KeyN" },
|
n: { key: "KeyN" },
|
||||||
o: { key: "KeyO" },
|
o: { key: "KeyO" },
|
||||||
"ö": { key: "KeyO", accentKey: keyTrema },
|
ö: { key: "KeyO", accentKey: keyTrema },
|
||||||
"ô": { key: "KeyO", accentKey: keyHat },
|
ô: { key: "KeyO", accentKey: keyHat },
|
||||||
p: { key: "KeyP" },
|
p: { key: "KeyP" },
|
||||||
q: { key: "KeyA" },
|
q: { key: "KeyA" },
|
||||||
r: { key: "KeyR" },
|
r: { key: "KeyR" },
|
||||||
s: { key: "KeyS" },
|
s: { key: "KeyS" },
|
||||||
t: { key: "KeyT" },
|
t: { key: "KeyT" },
|
||||||
u: { key: "KeyU" },
|
u: { key: "KeyU" },
|
||||||
"ü": { key: "KeyU", accentKey: keyTrema },
|
ü: { key: "KeyU", accentKey: keyTrema },
|
||||||
"û": { key: "KeyU", accentKey: keyHat },
|
û: { key: "KeyU", accentKey: keyHat },
|
||||||
v: { key: "KeyV" },
|
v: { key: "KeyV" },
|
||||||
w: { key: "KeyZ" },
|
w: { key: "KeyZ" },
|
||||||
x: { key: "KeyX" },
|
x: { key: "KeyX" },
|
||||||
|
|
@ -85,10 +85,10 @@ const chars = {
|
||||||
"²": { key: "Backquote" },
|
"²": { key: "Backquote" },
|
||||||
"&": { key: "Digit1" },
|
"&": { key: "Digit1" },
|
||||||
1: { key: "Digit1", shift: true },
|
1: { key: "Digit1", shift: true },
|
||||||
"é": { key: "Digit2" },
|
é: { key: "Digit2" },
|
||||||
2: { key: "Digit2", shift: true },
|
2: { key: "Digit2", shift: true },
|
||||||
"~": { key: "Digit2", altRight: true },
|
"~": { key: "Digit2", altRight: true },
|
||||||
"\"": { key: "Digit3" },
|
'"': { key: "Digit3" },
|
||||||
3: { key: "Digit3", shift: true },
|
3: { key: "Digit3", shift: true },
|
||||||
"#": { key: "Digit3", altRight: true },
|
"#": { key: "Digit3", altRight: true },
|
||||||
"'": { key: "Digit4" },
|
"'": { key: "Digit4" },
|
||||||
|
|
@ -100,16 +100,16 @@ const chars = {
|
||||||
"-": { key: "Digit6" },
|
"-": { key: "Digit6" },
|
||||||
6: { key: "Digit6", shift: true },
|
6: { key: "Digit6", shift: true },
|
||||||
"|": { key: "Digit6", altRight: true },
|
"|": { key: "Digit6", altRight: true },
|
||||||
"è": { key: "Digit7" },
|
è: { key: "Digit7" },
|
||||||
7: { key: "Digit7", shift: true },
|
7: { key: "Digit7", shift: true },
|
||||||
"`": { key: "Digit7", altRight: true },
|
"`": { key: "Digit7", altRight: true },
|
||||||
"_": { key: "Digit8" },
|
_: { key: "Digit8" },
|
||||||
8: { key: "Digit8", shift: true },
|
8: { key: "Digit8", shift: true },
|
||||||
"\\": { key: "Digit8", altRight: true },
|
"\\": { key: "Digit8", altRight: true },
|
||||||
"ç": { key: "Digit9" },
|
ç: { key: "Digit9" },
|
||||||
9: { key: "Digit9", shift: true },
|
9: { key: "Digit9", shift: true },
|
||||||
"^": { key: "Digit9", altRight: true },
|
"^": { key: "Digit9", altRight: true },
|
||||||
"à": { key: "Digit0" },
|
à: { key: "Digit0" },
|
||||||
0: { key: "Digit0", shift: true },
|
0: { key: "Digit0", shift: true },
|
||||||
"@": { key: "Digit0", altRight: true },
|
"@": { key: "Digit0", altRight: true },
|
||||||
")": { key: "Minus" },
|
")": { key: "Minus" },
|
||||||
|
|
@ -118,13 +118,13 @@ const chars = {
|
||||||
"=": { key: "Equal" },
|
"=": { key: "Equal" },
|
||||||
"+": { key: "Equal", shift: true },
|
"+": { key: "Equal", shift: true },
|
||||||
"}": { key: "Equal", altRight: true },
|
"}": { key: "Equal", altRight: true },
|
||||||
"$": { key: "BracketRight" },
|
$: { key: "BracketRight" },
|
||||||
"£": { key: "BracketRight", shift: true },
|
"£": { key: "BracketRight", shift: true },
|
||||||
"¤": { key: "BracketRight", altRight: true },
|
"¤": { key: "BracketRight", altRight: true },
|
||||||
"ù": { key: "Quote" },
|
ù: { key: "Quote" },
|
||||||
"%": { key: "Quote", shift: true },
|
"%": { key: "Quote", shift: true },
|
||||||
"*": { key: "Backslash" },
|
"*": { key: "Backslash" },
|
||||||
"µ": { key: "Backslash", shift: true },
|
µ: { key: "Backslash", shift: true },
|
||||||
",": { key: "KeyM" },
|
",": { key: "KeyM" },
|
||||||
"?": { key: "KeyM", shift: true },
|
"?": { key: "KeyM", shift: true },
|
||||||
";": { key: "Comma" },
|
";": { key: "Comma" },
|
||||||
|
|
@ -148,5 +148,5 @@ export const fr_FR: KeyboardLayout = {
|
||||||
// TODO need to localize these maps and layouts
|
// TODO need to localize these maps and layouts
|
||||||
keyDisplayMap: en_US.keyDisplayMap,
|
keyDisplayMap: en_US.keyDisplayMap,
|
||||||
modifierDisplayMap: en_US.modifierDisplayMap,
|
modifierDisplayMap: en_US.modifierDisplayMap,
|
||||||
virtualKeyboard: en_US.virtualKeyboard
|
virtualKeyboard: en_US.virtualKeyboard,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { KeyboardLayout, KeyCombo } from "../keyboardLayouts"
|
import { KeyboardLayout, KeyCombo } from "../keyboardLayouts";
|
||||||
|
|
||||||
import { en_US } from "./en_US" // for fallback of keyDisplayMap, modifierDisplayMap, and virtualKeyboard
|
import { en_US } from "./en_US"; // for fallback of keyDisplayMap, modifierDisplayMap, and virtualKeyboard
|
||||||
|
|
||||||
const name = "Italiano";
|
const name = "Italiano";
|
||||||
const isoCode = "it-IT";
|
const isoCode = "it-IT";
|
||||||
|
|
@ -64,11 +64,11 @@ const chars = {
|
||||||
1: { key: "Digit1" },
|
1: { key: "Digit1" },
|
||||||
"!": { key: "Digit1", shift: true },
|
"!": { key: "Digit1", shift: true },
|
||||||
2: { key: "Digit2" },
|
2: { key: "Digit2" },
|
||||||
"\"": { key: "Digit2", shift: true },
|
'"': { key: "Digit2", shift: true },
|
||||||
3: { key: "Digit3" },
|
3: { key: "Digit3" },
|
||||||
"£": { key: "Digit3", shift: true },
|
"£": { key: "Digit3", shift: true },
|
||||||
4: { key: "Digit4" },
|
4: { key: "Digit4" },
|
||||||
"$": { key: "Digit4", shift: true },
|
$: { key: "Digit4", shift: true },
|
||||||
5: { key: "Digit5" },
|
5: { key: "Digit5" },
|
||||||
"%": { key: "Digit5", shift: true },
|
"%": { key: "Digit5", shift: true },
|
||||||
6: { key: "Digit6" },
|
6: { key: "Digit6" },
|
||||||
|
|
@ -83,30 +83,30 @@ const chars = {
|
||||||
"=": { key: "Digit0", shift: true },
|
"=": { key: "Digit0", shift: true },
|
||||||
"'": { key: "Minus" },
|
"'": { key: "Minus" },
|
||||||
"?": { key: "Minus", shift: true },
|
"?": { key: "Minus", shift: true },
|
||||||
"ì": { key: "Equal" },
|
ì: { key: "Equal" },
|
||||||
"^": { key: "Equal", shift: true },
|
"^": { key: "Equal", shift: true },
|
||||||
"è": { key: "BracketLeft" },
|
è: { key: "BracketLeft" },
|
||||||
"é": { key: "BracketLeft", shift: true },
|
é: { key: "BracketLeft", shift: true },
|
||||||
"[": { key: "BracketLeft", altRight: true },
|
"[": { key: "BracketLeft", altRight: true },
|
||||||
"{": { key: "BracketLeft", shift: true, altRight: true },
|
"{": { key: "BracketLeft", shift: true, altRight: true },
|
||||||
"+": { key: "BracketRight" },
|
"+": { key: "BracketRight" },
|
||||||
"*": { key: "BracketRight", shift: true },
|
"*": { key: "BracketRight", shift: true },
|
||||||
"]": { key: "BracketRight", altRight: true },
|
"]": { key: "BracketRight", altRight: true },
|
||||||
"}": { key: "BracketRight", shift: true, altRight: true },
|
"}": { key: "BracketRight", shift: true, altRight: true },
|
||||||
"ò": { key: "Semicolon" },
|
ò: { key: "Semicolon" },
|
||||||
"ç": { key: "Semicolon", shift: true },
|
ç: { key: "Semicolon", shift: true },
|
||||||
"@": { key: "Semicolon", altRight: true },
|
"@": { key: "Semicolon", altRight: true },
|
||||||
"à": { key: "Quote" },
|
à: { key: "Quote" },
|
||||||
"°": { key: "Quote", shift: true },
|
"°": { key: "Quote", shift: true },
|
||||||
"#": { key: "Quote", altRight: true },
|
"#": { key: "Quote", altRight: true },
|
||||||
"ù": { key: "Backslash" },
|
ù: { key: "Backslash" },
|
||||||
"§": { key: "Backslash", shift: true },
|
"§": { key: "Backslash", shift: true },
|
||||||
",": { key: "Comma" },
|
",": { key: "Comma" },
|
||||||
";": { key: "Comma", shift: true },
|
";": { key: "Comma", shift: true },
|
||||||
".": { key: "Period" },
|
".": { key: "Period" },
|
||||||
":": { key: "Period", shift: true },
|
":": { key: "Period", shift: true },
|
||||||
"-": { key: "Slash" },
|
"-": { key: "Slash" },
|
||||||
"_": { key: "Slash", shift: true },
|
_: { key: "Slash", shift: true },
|
||||||
"<": { key: "IntlBackslash" },
|
"<": { key: "IntlBackslash" },
|
||||||
">": { key: "IntlBackslash", shift: true },
|
">": { key: "IntlBackslash", shift: true },
|
||||||
" ": { key: "Space" },
|
" ": { key: "Space" },
|
||||||
|
|
@ -122,5 +122,5 @@ export const it_IT: KeyboardLayout = {
|
||||||
// TODO need to localize these maps and layouts
|
// TODO need to localize these maps and layouts
|
||||||
keyDisplayMap: en_US.keyDisplayMap,
|
keyDisplayMap: en_US.keyDisplayMap,
|
||||||
modifierDisplayMap: en_US.modifierDisplayMap,
|
modifierDisplayMap: en_US.modifierDisplayMap,
|
||||||
virtualKeyboard: en_US.virtualKeyboard
|
virtualKeyboard: en_US.virtualKeyboard,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,115 +1,115 @@
|
||||||
import { KeyboardLayout, KeyCombo } from "../keyboardLayouts"
|
import { KeyboardLayout, KeyCombo } from "../keyboardLayouts";
|
||||||
|
|
||||||
import { en_US } from "./en_US" // for fallback of keyDisplayMap, modifierDisplayMap, and virtualKeyboard
|
import { en_US } from "./en_US"; // for fallback of keyDisplayMap, modifierDisplayMap, and virtualKeyboard
|
||||||
|
|
||||||
const name = "Norsk bokmål";
|
const name = "Norsk bokmål";
|
||||||
const isoCode = "nb-NO";
|
const isoCode = "nb-NO";
|
||||||
|
|
||||||
const keyTrema: KeyCombo = { key: "BracketRight" } // tréma (umlaut), two dots placed above a vowel
|
const keyTrema: KeyCombo = { key: "BracketRight" }; // tréma (umlaut), two dots placed above a vowel
|
||||||
const keyAcute: KeyCombo = { key: "Equal", altRight: true } // accent aigu (acute accent), mark ´ placed above the letter
|
const keyAcute: KeyCombo = { key: "Equal", altRight: true }; // accent aigu (acute accent), mark ´ placed above the letter
|
||||||
const keyHat: KeyCombo = { key: "BracketRight", shift: true } // accent circonflexe (accent hat), mark ^ placed above the letter
|
const keyHat: KeyCombo = { key: "BracketRight", shift: true }; // accent circonflexe (accent hat), mark ^ placed above the letter
|
||||||
const keyGrave: KeyCombo = { key: "Equal", shift: true } // accent grave, mark ` placed above the letter
|
const keyGrave: KeyCombo = { key: "Equal", shift: true }; // accent grave, mark ` placed above the letter
|
||||||
const keyTilde: KeyCombo = { key: "BracketRight", altRight: true } // tilde, mark ~ placed above the letter
|
const keyTilde: KeyCombo = { key: "BracketRight", altRight: true }; // tilde, mark ~ placed above the letter
|
||||||
|
|
||||||
const chars = {
|
const chars = {
|
||||||
A: { key: "KeyA", shift: true },
|
A: { key: "KeyA", shift: true },
|
||||||
"Ä": { key: "KeyA", shift: true, accentKey: keyTrema },
|
Ä: { key: "KeyA", shift: true, accentKey: keyTrema },
|
||||||
"Á": { key: "KeyA", shift: true, accentKey: keyAcute },
|
Á: { key: "KeyA", shift: true, accentKey: keyAcute },
|
||||||
"Â": { key: "KeyA", shift: true, accentKey: keyHat },
|
Â: { key: "KeyA", shift: true, accentKey: keyHat },
|
||||||
"À": { key: "KeyA", shift: true, accentKey: keyGrave },
|
À: { key: "KeyA", shift: true, accentKey: keyGrave },
|
||||||
"Ã": { key: "KeyA", shift: true, accentKey: keyTilde },
|
Ã: { key: "KeyA", shift: true, accentKey: keyTilde },
|
||||||
B: { key: "KeyB", shift: true },
|
B: { key: "KeyB", shift: true },
|
||||||
C: { key: "KeyC", shift: true },
|
C: { key: "KeyC", shift: true },
|
||||||
D: { key: "KeyD", shift: true },
|
D: { key: "KeyD", shift: true },
|
||||||
E: { key: "KeyE", shift: true },
|
E: { key: "KeyE", shift: true },
|
||||||
"Ë": { key: "KeyE", shift: true, accentKey: keyTrema },
|
Ë: { key: "KeyE", shift: true, accentKey: keyTrema },
|
||||||
"É": { key: "KeyE", shift: true, accentKey: keyAcute },
|
É: { key: "KeyE", shift: true, accentKey: keyAcute },
|
||||||
"Ê": { key: "KeyE", shift: true, accentKey: keyHat },
|
Ê: { key: "KeyE", shift: true, accentKey: keyHat },
|
||||||
"È": { key: "KeyE", shift: true, accentKey: keyGrave },
|
È: { key: "KeyE", shift: true, accentKey: keyGrave },
|
||||||
"Ẽ": { key: "KeyE", shift: true, accentKey: keyTilde },
|
Ẽ: { key: "KeyE", shift: true, accentKey: keyTilde },
|
||||||
F: { key: "KeyF", shift: true },
|
F: { key: "KeyF", shift: true },
|
||||||
G: { key: "KeyG", shift: true },
|
G: { key: "KeyG", shift: true },
|
||||||
H: { key: "KeyH", shift: true },
|
H: { key: "KeyH", shift: true },
|
||||||
I: { key: "KeyI", shift: true },
|
I: { key: "KeyI", shift: true },
|
||||||
"Ï": { key: "KeyI", shift: true, accentKey: keyTrema },
|
Ï: { key: "KeyI", shift: true, accentKey: keyTrema },
|
||||||
"Í": { key: "KeyI", shift: true, accentKey: keyAcute },
|
Í: { key: "KeyI", shift: true, accentKey: keyAcute },
|
||||||
"Î": { key: "KeyI", shift: true, accentKey: keyHat },
|
Î: { key: "KeyI", shift: true, accentKey: keyHat },
|
||||||
"Ì": { key: "KeyI", shift: true, accentKey: keyGrave },
|
Ì: { key: "KeyI", shift: true, accentKey: keyGrave },
|
||||||
"Ĩ": { key: "KeyI", shift: true, accentKey: keyTilde },
|
Ĩ: { key: "KeyI", shift: true, accentKey: keyTilde },
|
||||||
J: { key: "KeyJ", shift: true },
|
J: { key: "KeyJ", shift: true },
|
||||||
K: { key: "KeyK", shift: true },
|
K: { key: "KeyK", shift: true },
|
||||||
L: { key: "KeyL", shift: true },
|
L: { key: "KeyL", shift: true },
|
||||||
M: { key: "KeyM", shift: true },
|
M: { key: "KeyM", shift: true },
|
||||||
N: { key: "KeyN", shift: true },
|
N: { key: "KeyN", shift: true },
|
||||||
O: { key: "KeyO", shift: true },
|
O: { key: "KeyO", shift: true },
|
||||||
"Ö": { key: "KeyO", shift: true, accentKey: keyTrema },
|
Ö: { key: "KeyO", shift: true, accentKey: keyTrema },
|
||||||
"Ó": { key: "KeyO", shift: true, accentKey: keyAcute },
|
Ó: { key: "KeyO", shift: true, accentKey: keyAcute },
|
||||||
"Ô": { key: "KeyO", shift: true, accentKey: keyHat },
|
Ô: { key: "KeyO", shift: true, accentKey: keyHat },
|
||||||
"Ò": { key: "KeyO", shift: true, accentKey: keyGrave },
|
Ò: { key: "KeyO", shift: true, accentKey: keyGrave },
|
||||||
"Õ": { key: "KeyO", shift: true, accentKey: keyTilde },
|
Õ: { key: "KeyO", shift: true, accentKey: keyTilde },
|
||||||
P: { key: "KeyP", shift: true },
|
P: { key: "KeyP", shift: true },
|
||||||
Q: { key: "KeyQ", shift: true },
|
Q: { key: "KeyQ", shift: true },
|
||||||
R: { key: "KeyR", shift: true },
|
R: { key: "KeyR", shift: true },
|
||||||
S: { key: "KeyS", shift: true },
|
S: { key: "KeyS", shift: true },
|
||||||
T: { key: "KeyT", shift: true },
|
T: { key: "KeyT", shift: true },
|
||||||
U: { key: "KeyU", shift: true },
|
U: { key: "KeyU", shift: true },
|
||||||
"Ü": { key: "KeyU", shift: true, accentKey: keyTrema },
|
Ü: { key: "KeyU", shift: true, accentKey: keyTrema },
|
||||||
"Ú": { key: "KeyU", shift: true, accentKey: keyAcute },
|
Ú: { key: "KeyU", shift: true, accentKey: keyAcute },
|
||||||
"Û": { key: "KeyU", shift: true, accentKey: keyHat },
|
Û: { key: "KeyU", shift: true, accentKey: keyHat },
|
||||||
"Ù": { key: "KeyU", shift: true, accentKey: keyGrave },
|
Ù: { key: "KeyU", shift: true, accentKey: keyGrave },
|
||||||
"Ũ": { key: "KeyU", shift: true, accentKey: keyTilde },
|
Ũ: { key: "KeyU", shift: true, accentKey: keyTilde },
|
||||||
V: { key: "KeyV", shift: true },
|
V: { key: "KeyV", shift: true },
|
||||||
W: { key: "KeyW", shift: true },
|
W: { key: "KeyW", shift: true },
|
||||||
X: { key: "KeyX", shift: true },
|
X: { key: "KeyX", shift: true },
|
||||||
Y: { key: "KeyZ", shift: true },
|
Y: { key: "KeyZ", shift: true },
|
||||||
Z: { key: "KeyY", shift: true },
|
Z: { key: "KeyY", shift: true },
|
||||||
a: { key: "KeyA" },
|
a: { key: "KeyA" },
|
||||||
"ä": { key: "KeyA", accentKey: keyTrema },
|
ä: { key: "KeyA", accentKey: keyTrema },
|
||||||
"á": { key: "KeyA", accentKey: keyAcute },
|
á: { key: "KeyA", accentKey: keyAcute },
|
||||||
"â": { key: "KeyA", accentKey: keyHat },
|
â: { key: "KeyA", accentKey: keyHat },
|
||||||
"à": { key: "KeyA", accentKey: keyGrave },
|
à: { key: "KeyA", accentKey: keyGrave },
|
||||||
"ã": { key: "KeyA", accentKey: keyTilde },
|
ã: { key: "KeyA", accentKey: keyTilde },
|
||||||
b: { key: "KeyB" },
|
b: { key: "KeyB" },
|
||||||
c: { key: "KeyC" },
|
c: { key: "KeyC" },
|
||||||
d: { key: "KeyD" },
|
d: { key: "KeyD" },
|
||||||
e: { key: "KeyE" },
|
e: { key: "KeyE" },
|
||||||
"ë": { key: "KeyE", accentKey: keyTrema },
|
ë: { key: "KeyE", accentKey: keyTrema },
|
||||||
"é": { key: "KeyE", accentKey: keyAcute },
|
é: { key: "KeyE", accentKey: keyAcute },
|
||||||
"ê": { key: "KeyE", accentKey: keyHat },
|
ê: { key: "KeyE", accentKey: keyHat },
|
||||||
"è": { key: "KeyE", accentKey: keyGrave },
|
è: { key: "KeyE", accentKey: keyGrave },
|
||||||
"ẽ": { key: "KeyE", accentKey: keyTilde },
|
ẽ: { key: "KeyE", accentKey: keyTilde },
|
||||||
"€": { key: "KeyE", altRight: true },
|
"€": { key: "KeyE", altRight: true },
|
||||||
f: { key: "KeyF" },
|
f: { key: "KeyF" },
|
||||||
g: { key: "KeyG" },
|
g: { key: "KeyG" },
|
||||||
h: { key: "KeyH" },
|
h: { key: "KeyH" },
|
||||||
i: { key: "KeyI" },
|
i: { key: "KeyI" },
|
||||||
"ï": { key: "KeyI", accentKey: keyTrema },
|
ï: { key: "KeyI", accentKey: keyTrema },
|
||||||
"í": { key: "KeyI", accentKey: keyAcute },
|
í: { key: "KeyI", accentKey: keyAcute },
|
||||||
"î": { key: "KeyI", accentKey: keyHat },
|
î: { key: "KeyI", accentKey: keyHat },
|
||||||
"ì": { key: "KeyI", accentKey: keyGrave },
|
ì: { key: "KeyI", accentKey: keyGrave },
|
||||||
"ĩ": { key: "KeyI", accentKey: keyTilde },
|
ĩ: { key: "KeyI", accentKey: keyTilde },
|
||||||
j: { key: "KeyJ" },
|
j: { key: "KeyJ" },
|
||||||
k: { key: "KeyK" },
|
k: { key: "KeyK" },
|
||||||
l: { key: "KeyL" },
|
l: { key: "KeyL" },
|
||||||
m: { key: "KeyM" },
|
m: { key: "KeyM" },
|
||||||
n: { key: "KeyN" },
|
n: { key: "KeyN" },
|
||||||
o: { key: "KeyO" },
|
o: { key: "KeyO" },
|
||||||
"ö": { key: "KeyO", accentKey: keyTrema },
|
ö: { key: "KeyO", accentKey: keyTrema },
|
||||||
"ó": { key: "KeyO", accentKey: keyAcute },
|
ó: { key: "KeyO", accentKey: keyAcute },
|
||||||
"ô": { key: "KeyO", accentKey: keyHat },
|
ô: { key: "KeyO", accentKey: keyHat },
|
||||||
"ò": { key: "KeyO", accentKey: keyGrave },
|
ò: { key: "KeyO", accentKey: keyGrave },
|
||||||
"õ": { key: "KeyO", accentKey: keyTilde },
|
õ: { key: "KeyO", accentKey: keyTilde },
|
||||||
p: { key: "KeyP" },
|
p: { key: "KeyP" },
|
||||||
q: { key: "KeyQ" },
|
q: { key: "KeyQ" },
|
||||||
r: { key: "KeyR" },
|
r: { key: "KeyR" },
|
||||||
s: { key: "KeyS" },
|
s: { key: "KeyS" },
|
||||||
t: { key: "KeyT" },
|
t: { key: "KeyT" },
|
||||||
u: { key: "KeyU" },
|
u: { key: "KeyU" },
|
||||||
"ü": { key: "KeyU", accentKey: keyTrema },
|
ü: { key: "KeyU", accentKey: keyTrema },
|
||||||
"ú": { key: "KeyU", accentKey: keyAcute },
|
ú: { key: "KeyU", accentKey: keyAcute },
|
||||||
"û": { key: "KeyU", accentKey: keyHat },
|
û: { key: "KeyU", accentKey: keyHat },
|
||||||
"ù": { key: "KeyU", accentKey: keyGrave },
|
ù: { key: "KeyU", accentKey: keyGrave },
|
||||||
"ũ": { key: "KeyU", accentKey: keyTilde },
|
ũ: { key: "KeyU", accentKey: keyTilde },
|
||||||
v: { key: "KeyV" },
|
v: { key: "KeyV" },
|
||||||
w: { key: "KeyW" },
|
w: { key: "KeyW" },
|
||||||
x: { key: "KeyX" },
|
x: { key: "KeyX" },
|
||||||
|
|
@ -120,14 +120,14 @@ const chars = {
|
||||||
1: { key: "Digit1" },
|
1: { key: "Digit1" },
|
||||||
"!": { key: "Digit1", shift: true },
|
"!": { key: "Digit1", shift: true },
|
||||||
2: { key: "Digit2" },
|
2: { key: "Digit2" },
|
||||||
"\"": { key: "Digit2", shift: true },
|
'"': { key: "Digit2", shift: true },
|
||||||
"@": { key: "Digit2", altRight: true },
|
"@": { key: "Digit2", altRight: true },
|
||||||
3: { key: "Digit3" },
|
3: { key: "Digit3" },
|
||||||
"#": { key: "Digit3", shift: true },
|
"#": { key: "Digit3", shift: true },
|
||||||
"£": { key: "Digit3", altRight: true },
|
"£": { key: "Digit3", altRight: true },
|
||||||
4: { key: "Digit4" },
|
4: { key: "Digit4" },
|
||||||
"¤": { key: "Digit4", shift: true },
|
"¤": { key: "Digit4", shift: true },
|
||||||
"$": { key: "Digit4", altRight: true },
|
$: { key: "Digit4", altRight: true },
|
||||||
5: { key: "Digit5" },
|
5: { key: "Digit5" },
|
||||||
"%": { key: "Digit5", shift: true },
|
"%": { key: "Digit5", shift: true },
|
||||||
6: { key: "Digit6" },
|
6: { key: "Digit6" },
|
||||||
|
|
@ -147,12 +147,12 @@ const chars = {
|
||||||
"+": { key: "Minus" },
|
"+": { key: "Minus" },
|
||||||
"?": { key: "Minus", shift: true },
|
"?": { key: "Minus", shift: true },
|
||||||
"\\": { key: "Equal" },
|
"\\": { key: "Equal" },
|
||||||
"å": { key: "BracketLeft" },
|
å: { key: "BracketLeft" },
|
||||||
"Å": { key: "BracketLeft", shift: true },
|
Å: { key: "BracketLeft", shift: true },
|
||||||
"ø": { key: "Semicolon" },
|
ø: { key: "Semicolon" },
|
||||||
"Ø": { key: "Semicolon", shift: true },
|
Ø: { key: "Semicolon", shift: true },
|
||||||
"æ": { key: "Quote" },
|
æ: { key: "Quote" },
|
||||||
"Æ": { key: "Quote", shift: true },
|
Æ: { key: "Quote", shift: true },
|
||||||
"'": { key: "Backslash" },
|
"'": { key: "Backslash" },
|
||||||
"*": { key: "Backslash", shift: true },
|
"*": { key: "Backslash", shift: true },
|
||||||
",": { key: "Comma" },
|
",": { key: "Comma" },
|
||||||
|
|
@ -160,7 +160,7 @@ const chars = {
|
||||||
".": { key: "Period" },
|
".": { key: "Period" },
|
||||||
":": { key: "Period", shift: true },
|
":": { key: "Period", shift: true },
|
||||||
"-": { key: "Slash" },
|
"-": { key: "Slash" },
|
||||||
"_": { key: "Slash", shift: true },
|
_: { key: "Slash", shift: true },
|
||||||
"<": { key: "IntlBackslash" },
|
"<": { key: "IntlBackslash" },
|
||||||
">": { key: "IntlBackslash", shift: true },
|
">": { key: "IntlBackslash", shift: true },
|
||||||
" ": { key: "Space" },
|
" ": { key: "Space" },
|
||||||
|
|
@ -176,5 +176,5 @@ export const nb_NO: KeyboardLayout = {
|
||||||
// TODO need to localize these maps and layouts
|
// TODO need to localize these maps and layouts
|
||||||
keyDisplayMap: en_US.keyDisplayMap,
|
keyDisplayMap: en_US.keyDisplayMap,
|
||||||
modifierDisplayMap: en_US.modifierDisplayMap,
|
modifierDisplayMap: en_US.modifierDisplayMap,
|
||||||
virtualKeyboard: en_US.virtualKeyboard
|
virtualKeyboard: en_US.virtualKeyboard,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,111 +1,111 @@
|
||||||
import { KeyboardLayout, KeyCombo } from "../keyboardLayouts"
|
import { KeyboardLayout, KeyCombo } from "../keyboardLayouts";
|
||||||
|
|
||||||
import { en_US } from "./en_US" // for fallback of keyDisplayMap, modifierDisplayMap, and virtualKeyboard
|
import { en_US } from "./en_US"; // for fallback of keyDisplayMap, modifierDisplayMap, and virtualKeyboard
|
||||||
|
|
||||||
const name = "Svenska";
|
const name = "Svenska";
|
||||||
const isoCode = "sv-SE";
|
const isoCode = "sv-SE";
|
||||||
|
|
||||||
const keyTrema: KeyCombo = { key: "BracketRight" } // tréma (umlaut), two dots placed above a vowel
|
const keyTrema: KeyCombo = { key: "BracketRight" }; // tréma (umlaut), two dots placed above a vowel
|
||||||
const keyAcute: KeyCombo = { key: "Equal" } // accent aigu (acute accent), mark ´ placed above the letter
|
const keyAcute: KeyCombo = { key: "Equal" }; // accent aigu (acute accent), mark ´ placed above the letter
|
||||||
const keyHat: KeyCombo = { key: "BracketRight", shift: true } // accent circonflexe (accent hat), mark ^ placed above the letter
|
const keyHat: KeyCombo = { key: "BracketRight", shift: true }; // accent circonflexe (accent hat), mark ^ placed above the letter
|
||||||
const keyGrave: KeyCombo = { key: "Equal", shift: true } // accent grave, mark ` placed above the letter
|
const keyGrave: KeyCombo = { key: "Equal", shift: true }; // accent grave, mark ` placed above the letter
|
||||||
const keyTilde: KeyCombo = { key: "BracketRight", altRight: true } // tilde, mark ~ placed above the letter
|
const keyTilde: KeyCombo = { key: "BracketRight", altRight: true }; // tilde, mark ~ placed above the letter
|
||||||
|
|
||||||
const chars = {
|
const chars = {
|
||||||
A: { key: "KeyA", shift: true },
|
A: { key: "KeyA", shift: true },
|
||||||
"Á": { key: "KeyA", shift: true, accentKey: keyAcute },
|
Á: { key: "KeyA", shift: true, accentKey: keyAcute },
|
||||||
"Â": { key: "KeyA", shift: true, accentKey: keyHat },
|
Â: { key: "KeyA", shift: true, accentKey: keyHat },
|
||||||
"À": { key: "KeyA", shift: true, accentKey: keyGrave },
|
À: { key: "KeyA", shift: true, accentKey: keyGrave },
|
||||||
"Ã": { key: "KeyA", shift: true, accentKey: keyTilde },
|
Ã: { key: "KeyA", shift: true, accentKey: keyTilde },
|
||||||
B: { key: "KeyB", shift: true },
|
B: { key: "KeyB", shift: true },
|
||||||
C: { key: "KeyC", shift: true },
|
C: { key: "KeyC", shift: true },
|
||||||
D: { key: "KeyD", shift: true },
|
D: { key: "KeyD", shift: true },
|
||||||
E: { key: "KeyE", shift: true },
|
E: { key: "KeyE", shift: true },
|
||||||
"Ë": { key: "KeyE", shift: true, accentKey: keyTrema },
|
Ë: { key: "KeyE", shift: true, accentKey: keyTrema },
|
||||||
"É": { key: "KeyE", shift: true, accentKey: keyAcute },
|
É: { key: "KeyE", shift: true, accentKey: keyAcute },
|
||||||
"Ê": { key: "KeyE", shift: true, accentKey: keyHat },
|
Ê: { key: "KeyE", shift: true, accentKey: keyHat },
|
||||||
"È": { key: "KeyE", shift: true, accentKey: keyGrave },
|
È: { key: "KeyE", shift: true, accentKey: keyGrave },
|
||||||
"Ẽ": { key: "KeyE", shift: true, accentKey: keyTilde },
|
Ẽ: { key: "KeyE", shift: true, accentKey: keyTilde },
|
||||||
F: { key: "KeyF", shift: true },
|
F: { key: "KeyF", shift: true },
|
||||||
G: { key: "KeyG", shift: true },
|
G: { key: "KeyG", shift: true },
|
||||||
H: { key: "KeyH", shift: true },
|
H: { key: "KeyH", shift: true },
|
||||||
I: { key: "KeyI", shift: true },
|
I: { key: "KeyI", shift: true },
|
||||||
"Ï": { key: "KeyI", shift: true, accentKey: keyTrema },
|
Ï: { key: "KeyI", shift: true, accentKey: keyTrema },
|
||||||
"Í": { key: "KeyI", shift: true, accentKey: keyAcute },
|
Í: { key: "KeyI", shift: true, accentKey: keyAcute },
|
||||||
"Î": { key: "KeyI", shift: true, accentKey: keyHat },
|
Î: { key: "KeyI", shift: true, accentKey: keyHat },
|
||||||
"Ì": { key: "KeyI", shift: true, accentKey: keyGrave },
|
Ì: { key: "KeyI", shift: true, accentKey: keyGrave },
|
||||||
"Ĩ": { key: "KeyI", shift: true, accentKey: keyTilde },
|
Ĩ: { key: "KeyI", shift: true, accentKey: keyTilde },
|
||||||
J: { key: "KeyJ", shift: true },
|
J: { key: "KeyJ", shift: true },
|
||||||
K: { key: "KeyK", shift: true },
|
K: { key: "KeyK", shift: true },
|
||||||
L: { key: "KeyL", shift: true },
|
L: { key: "KeyL", shift: true },
|
||||||
M: { key: "KeyM", shift: true },
|
M: { key: "KeyM", shift: true },
|
||||||
N: { key: "KeyN", shift: true },
|
N: { key: "KeyN", shift: true },
|
||||||
O: { key: "KeyO", shift: true },
|
O: { key: "KeyO", shift: true },
|
||||||
"Ó": { key: "KeyO", shift: true, accentKey: keyAcute },
|
Ó: { key: "KeyO", shift: true, accentKey: keyAcute },
|
||||||
"Ô": { key: "KeyO", shift: true, accentKey: keyHat },
|
Ô: { key: "KeyO", shift: true, accentKey: keyHat },
|
||||||
"Ò": { key: "KeyO", shift: true, accentKey: keyGrave },
|
Ò: { key: "KeyO", shift: true, accentKey: keyGrave },
|
||||||
"Õ": { key: "KeyO", shift: true, accentKey: keyTilde },
|
Õ: { key: "KeyO", shift: true, accentKey: keyTilde },
|
||||||
P: { key: "KeyP", shift: true },
|
P: { key: "KeyP", shift: true },
|
||||||
Q: { key: "KeyQ", shift: true },
|
Q: { key: "KeyQ", shift: true },
|
||||||
R: { key: "KeyR", shift: true },
|
R: { key: "KeyR", shift: true },
|
||||||
S: { key: "KeyS", shift: true },
|
S: { key: "KeyS", shift: true },
|
||||||
T: { key: "KeyT", shift: true },
|
T: { key: "KeyT", shift: true },
|
||||||
U: { key: "KeyU", shift: true },
|
U: { key: "KeyU", shift: true },
|
||||||
"Ü": { key: "KeyU", shift: true, accentKey: keyTrema },
|
Ü: { key: "KeyU", shift: true, accentKey: keyTrema },
|
||||||
"Ú": { key: "KeyU", shift: true, accentKey: keyAcute },
|
Ú: { key: "KeyU", shift: true, accentKey: keyAcute },
|
||||||
"Û": { key: "KeyU", shift: true, accentKey: keyHat },
|
Û: { key: "KeyU", shift: true, accentKey: keyHat },
|
||||||
"Ù": { key: "KeyU", shift: true, accentKey: keyGrave },
|
Ù: { key: "KeyU", shift: true, accentKey: keyGrave },
|
||||||
"Ũ": { key: "KeyU", shift: true, accentKey: keyTilde },
|
Ũ: { key: "KeyU", shift: true, accentKey: keyTilde },
|
||||||
V: { key: "KeyV", shift: true },
|
V: { key: "KeyV", shift: true },
|
||||||
W: { key: "KeyW", shift: true },
|
W: { key: "KeyW", shift: true },
|
||||||
X: { key: "KeyX", shift: true },
|
X: { key: "KeyX", shift: true },
|
||||||
Y: { key: "KeyY", shift: true },
|
Y: { key: "KeyY", shift: true },
|
||||||
Z: { key: "KeyZ", shift: true },
|
Z: { key: "KeyZ", shift: true },
|
||||||
a: { key: "KeyA" },
|
a: { key: "KeyA" },
|
||||||
"á": { key: "KeyA", accentKey: keyAcute },
|
á: { key: "KeyA", accentKey: keyAcute },
|
||||||
"â": { key: "KeyA", accentKey: keyHat },
|
â: { key: "KeyA", accentKey: keyHat },
|
||||||
"à": { key: "KeyA", accentKey: keyGrave },
|
à: { key: "KeyA", accentKey: keyGrave },
|
||||||
"ã": { key: "KeyA", accentKey: keyTilde },
|
ã: { key: "KeyA", accentKey: keyTilde },
|
||||||
b: { key: "KeyB" },
|
b: { key: "KeyB" },
|
||||||
c: { key: "KeyC" },
|
c: { key: "KeyC" },
|
||||||
d: { key: "KeyD" },
|
d: { key: "KeyD" },
|
||||||
e: { key: "KeyE" },
|
e: { key: "KeyE" },
|
||||||
"ë": { key: "KeyE", accentKey: keyTrema },
|
ë: { key: "KeyE", accentKey: keyTrema },
|
||||||
"é": { key: "KeyE", accentKey: keyAcute },
|
é: { key: "KeyE", accentKey: keyAcute },
|
||||||
"ê": { key: "KeyE", accentKey: keyHat },
|
ê: { key: "KeyE", accentKey: keyHat },
|
||||||
"è": { key: "KeyE", accentKey: keyGrave },
|
è: { key: "KeyE", accentKey: keyGrave },
|
||||||
"ẽ": { key: "KeyE", accentKey: keyTilde },
|
ẽ: { key: "KeyE", accentKey: keyTilde },
|
||||||
"€": { key: "KeyE", altRight: true },
|
"€": { key: "KeyE", altRight: true },
|
||||||
f: { key: "KeyF" },
|
f: { key: "KeyF" },
|
||||||
g: { key: "KeyG" },
|
g: { key: "KeyG" },
|
||||||
h: { key: "KeyH" },
|
h: { key: "KeyH" },
|
||||||
i: { key: "KeyI" },
|
i: { key: "KeyI" },
|
||||||
"ï": { key: "KeyI", accentKey: keyTrema },
|
ï: { key: "KeyI", accentKey: keyTrema },
|
||||||
"í": { key: "KeyI", accentKey: keyAcute },
|
í: { key: "KeyI", accentKey: keyAcute },
|
||||||
"î": { key: "KeyI", accentKey: keyHat },
|
î: { key: "KeyI", accentKey: keyHat },
|
||||||
"ì": { key: "KeyI", accentKey: keyGrave },
|
ì: { key: "KeyI", accentKey: keyGrave },
|
||||||
"ĩ": { key: "KeyI", accentKey: keyTilde },
|
ĩ: { key: "KeyI", accentKey: keyTilde },
|
||||||
j: { key: "KeyJ" },
|
j: { key: "KeyJ" },
|
||||||
k: { key: "KeyK" },
|
k: { key: "KeyK" },
|
||||||
l: { key: "KeyL" },
|
l: { key: "KeyL" },
|
||||||
m: { key: "KeyM" },
|
m: { key: "KeyM" },
|
||||||
n: { key: "KeyN" },
|
n: { key: "KeyN" },
|
||||||
o: { key: "KeyO" },
|
o: { key: "KeyO" },
|
||||||
"ó": { key: "KeyO", accentKey: keyAcute },
|
ó: { key: "KeyO", accentKey: keyAcute },
|
||||||
"ô": { key: "KeyO", accentKey: keyHat },
|
ô: { key: "KeyO", accentKey: keyHat },
|
||||||
"ò": { key: "KeyO", accentKey: keyGrave },
|
ò: { key: "KeyO", accentKey: keyGrave },
|
||||||
"õ": { key: "KeyO", accentKey: keyTilde },
|
õ: { key: "KeyO", accentKey: keyTilde },
|
||||||
p: { key: "KeyP" },
|
p: { key: "KeyP" },
|
||||||
q: { key: "KeyQ" },
|
q: { key: "KeyQ" },
|
||||||
r: { key: "KeyR" },
|
r: { key: "KeyR" },
|
||||||
s: { key: "KeyS" },
|
s: { key: "KeyS" },
|
||||||
t: { key: "KeyT" },
|
t: { key: "KeyT" },
|
||||||
u: { key: "KeyU" },
|
u: { key: "KeyU" },
|
||||||
"ü": { key: "KeyU", accentKey: keyTrema },
|
ü: { key: "KeyU", accentKey: keyTrema },
|
||||||
"ú": { key: "KeyU", accentKey: keyAcute },
|
ú: { key: "KeyU", accentKey: keyAcute },
|
||||||
"û": { key: "KeyU", accentKey: keyHat },
|
û: { key: "KeyU", accentKey: keyHat },
|
||||||
"ù": { key: "KeyU", accentKey: keyGrave },
|
ù: { key: "KeyU", accentKey: keyGrave },
|
||||||
"ũ": { key: "KeyU", accentKey: keyTilde },
|
ũ: { key: "KeyU", accentKey: keyTilde },
|
||||||
v: { key: "KeyV" },
|
v: { key: "KeyV" },
|
||||||
w: { key: "KeyW" },
|
w: { key: "KeyW" },
|
||||||
x: { key: "KeyX" },
|
x: { key: "KeyX" },
|
||||||
|
|
@ -116,14 +116,14 @@ const chars = {
|
||||||
1: { key: "Digit1" },
|
1: { key: "Digit1" },
|
||||||
"!": { key: "Digit1", shift: true },
|
"!": { key: "Digit1", shift: true },
|
||||||
2: { key: "Digit2" },
|
2: { key: "Digit2" },
|
||||||
"\"": { key: "Digit2", shift: true },
|
'"': { key: "Digit2", shift: true },
|
||||||
"@": { key: "Digit2", altRight: true },
|
"@": { key: "Digit2", altRight: true },
|
||||||
3: { key: "Digit3" },
|
3: { key: "Digit3" },
|
||||||
"#": { key: "Digit3", shift: true },
|
"#": { key: "Digit3", shift: true },
|
||||||
"£": { key: "Digit3", altRight: true },
|
"£": { key: "Digit3", altRight: true },
|
||||||
4: { key: "Digit4" },
|
4: { key: "Digit4" },
|
||||||
"¤": { key: "Digit4", shift: true },
|
"¤": { key: "Digit4", shift: true },
|
||||||
"$": { key: "Digit4", altRight: true },
|
$: { key: "Digit4", altRight: true },
|
||||||
5: { key: "Digit5" },
|
5: { key: "Digit5" },
|
||||||
"%": { key: "Digit5", shift: true },
|
"%": { key: "Digit5", shift: true },
|
||||||
6: { key: "Digit6" },
|
6: { key: "Digit6" },
|
||||||
|
|
@ -143,12 +143,12 @@ const chars = {
|
||||||
"+": { key: "Minus" },
|
"+": { key: "Minus" },
|
||||||
"?": { key: "Minus", shift: true },
|
"?": { key: "Minus", shift: true },
|
||||||
"\\": { key: "Minus", altRight: true },
|
"\\": { key: "Minus", altRight: true },
|
||||||
"å": { key: "BracketLeft" },
|
å: { key: "BracketLeft" },
|
||||||
"Å": { key: "BracketLeft", shift: true },
|
Å: { key: "BracketLeft", shift: true },
|
||||||
"ö": { key: "Semicolon" },
|
ö: { key: "Semicolon" },
|
||||||
"Ö": { key: "Semicolon", shift: true },
|
Ö: { key: "Semicolon", shift: true },
|
||||||
"ä": { key: "Quote" },
|
ä: { key: "Quote" },
|
||||||
"Ä": { key: "Quote", shift: true },
|
Ä: { key: "Quote", shift: true },
|
||||||
"'": { key: "Backslash" },
|
"'": { key: "Backslash" },
|
||||||
"*": { key: "Backslash", shift: true },
|
"*": { key: "Backslash", shift: true },
|
||||||
",": { key: "Comma" },
|
",": { key: "Comma" },
|
||||||
|
|
@ -156,7 +156,7 @@ const chars = {
|
||||||
".": { key: "Period" },
|
".": { key: "Period" },
|
||||||
":": { key: "Period", shift: true },
|
":": { key: "Period", shift: true },
|
||||||
"-": { key: "Slash" },
|
"-": { key: "Slash" },
|
||||||
"_": { key: "Slash", shift: true },
|
_: { key: "Slash", shift: true },
|
||||||
"<": { key: "IntlBackslash" },
|
"<": { key: "IntlBackslash" },
|
||||||
">": { key: "IntlBackslash", shift: true },
|
">": { key: "IntlBackslash", shift: true },
|
||||||
"|": { key: "IntlBackslash", altRight: true },
|
"|": { key: "IntlBackslash", altRight: true },
|
||||||
|
|
@ -173,5 +173,5 @@ export const sv_SE: KeyboardLayout = {
|
||||||
// TODO need to localize these maps and layouts
|
// TODO need to localize these maps and layouts
|
||||||
keyDisplayMap: en_US.keyDisplayMap,
|
keyDisplayMap: en_US.keyDisplayMap,
|
||||||
modifierDisplayMap: en_US.modifierDisplayMap,
|
modifierDisplayMap: en_US.modifierDisplayMap,
|
||||||
virtualKeyboard: en_US.virtualKeyboard
|
virtualKeyboard: en_US.virtualKeyboard,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
export const keys = {
|
export const keys = {
|
||||||
Again: 0x79,
|
Again: 0x79,
|
||||||
AlternateErase: 0x9d,
|
AlternateErase: 0x9d,
|
||||||
AltGr: 0xe6, // aka AltRight
|
AltGr: 0xe6, // aka AltRight
|
||||||
AltLeft: 0xe2,
|
AltLeft: 0xe2,
|
||||||
AltRight: 0xe6,
|
AltRight: 0xe6,
|
||||||
Application: 0x65,
|
Application: 0x65,
|
||||||
|
|
@ -14,10 +14,10 @@ export const keys = {
|
||||||
ArrowRight: 0x4f,
|
ArrowRight: 0x4f,
|
||||||
ArrowUp: 0x52,
|
ArrowUp: 0x52,
|
||||||
Attention: 0x9a,
|
Attention: 0x9a,
|
||||||
Backquote: 0x35, // aka Grave
|
Backquote: 0x35, // aka Grave
|
||||||
Backslash: 0x31,
|
Backslash: 0x31,
|
||||||
Backspace: 0x2a,
|
Backspace: 0x2a,
|
||||||
BracketLeft: 0x2f, // aka LeftBrace
|
BracketLeft: 0x2f, // aka LeftBrace
|
||||||
BracketRight: 0x30, // aka RightBrace
|
BracketRight: 0x30, // aka RightBrace
|
||||||
Cancel: 0x9b,
|
Cancel: 0x9b,
|
||||||
CapsLock: 0x39,
|
CapsLock: 0x39,
|
||||||
|
|
@ -77,7 +77,7 @@ export const keys = {
|
||||||
F24: 0x73,
|
F24: 0x73,
|
||||||
Find: 0x7e,
|
Find: 0x7e,
|
||||||
Grave: 0x35,
|
Grave: 0x35,
|
||||||
HashTilde: 0x32, // non-US # and ~
|
HashTilde: 0x32, // non-US # and ~
|
||||||
Help: 0x75,
|
Help: 0x75,
|
||||||
Home: 0x4a,
|
Home: 0x4a,
|
||||||
Insert: 0x49,
|
Insert: 0x49,
|
||||||
|
|
@ -134,19 +134,19 @@ export const keys = {
|
||||||
MetaRight: 0xe7,
|
MetaRight: 0xe7,
|
||||||
Minus: 0x2d,
|
Minus: 0x2d,
|
||||||
Mute: 0x7f,
|
Mute: 0x7f,
|
||||||
NumLock: 0x53, // and Clear
|
NumLock: 0x53, // and Clear
|
||||||
Numpad0: 0x62, // and Insert
|
Numpad0: 0x62, // and Insert
|
||||||
Numpad00: 0xb0,
|
Numpad00: 0xb0,
|
||||||
Numpad000: 0xb1,
|
Numpad000: 0xb1,
|
||||||
Numpad1: 0x59, // and End
|
Numpad1: 0x59, // and End
|
||||||
Numpad2: 0x5a, // and Down Arrow
|
Numpad2: 0x5a, // and Down Arrow
|
||||||
Numpad3: 0x5b, // and Page Down
|
Numpad3: 0x5b, // and Page Down
|
||||||
Numpad4: 0x5c, // and Left Arrow
|
Numpad4: 0x5c, // and Left Arrow
|
||||||
Numpad5: 0x5d,
|
Numpad5: 0x5d,
|
||||||
Numpad6: 0x5e, // and Right Arrow
|
Numpad6: 0x5e, // and Right Arrow
|
||||||
Numpad7: 0x5f, // and Home
|
Numpad7: 0x5f, // and Home
|
||||||
Numpad8: 0x60, // and Up Arrow
|
Numpad8: 0x60, // and Up Arrow
|
||||||
Numpad9: 0x61, // and Page Up
|
Numpad9: 0x61, // and Page Up
|
||||||
NumpadAdd: 0x57,
|
NumpadAdd: 0x57,
|
||||||
NumpadAnd: 0xc7,
|
NumpadAnd: 0xc7,
|
||||||
NumpadAt: 0xce,
|
NumpadAt: 0xce,
|
||||||
|
|
@ -226,7 +226,7 @@ export const keys = {
|
||||||
Slash: 0x38,
|
Slash: 0x38,
|
||||||
Space: 0x2c,
|
Space: 0x2c,
|
||||||
Stop: 0x78,
|
Stop: 0x78,
|
||||||
SystemRequest: 0x9a, // aka Attention
|
SystemRequest: 0x9a, // aka Attention
|
||||||
Tab: 0x2b,
|
Tab: 0x2b,
|
||||||
ThousandsSeparator: 0xb2,
|
ThousandsSeparator: 0xb2,
|
||||||
Tilde: 0x35,
|
Tilde: 0x35,
|
||||||
|
|
@ -251,7 +251,7 @@ export const deadKeys = {
|
||||||
Slash: 0x02f8,
|
Slash: 0x02f8,
|
||||||
Tilde: 0x007e,
|
Tilde: 0x007e,
|
||||||
Umlaut: 0x00a8,
|
Umlaut: 0x00a8,
|
||||||
} as Record<string, number>
|
} as Record<string, number>;
|
||||||
|
|
||||||
export const modifiers = {
|
export const modifiers = {
|
||||||
ControlLeft: 0x01,
|
ControlLeft: 0x01,
|
||||||
|
|
@ -286,4 +286,4 @@ export function decodeModifiers(modifier: number) {
|
||||||
isMetaActive: (modifier & (modifiers.MetaLeft | modifiers.MetaRight)) !== 0,
|
isMetaActive: (modifier & (modifiers.MetaLeft | modifiers.MetaRight)) !== 0,
|
||||||
isAltGrActive: (modifier & modifiers.AltGr) !== 0,
|
isAltGrActive: (modifier & modifiers.AltGr) !== 0,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -43,10 +43,16 @@ const SettingsHardwareRoute = lazy(() => import("@routes/devices.$id.settings.ha
|
||||||
const SettingsVideoRoute = lazy(() => import("@routes/devices.$id.settings.video"));
|
const SettingsVideoRoute = lazy(() => import("@routes/devices.$id.settings.video"));
|
||||||
const SettingsAppearanceRoute = lazy(() => import("@routes/devices.$id.settings.appearance"));
|
const SettingsAppearanceRoute = lazy(() => import("@routes/devices.$id.settings.appearance"));
|
||||||
const SettingsGeneralIndexRoute = lazy(() => import("@routes/devices.$id.settings.general._index"));
|
const SettingsGeneralIndexRoute = lazy(() => import("@routes/devices.$id.settings.general._index"));
|
||||||
const SettingsGeneralRebootRoute = lazy(() => import("@routes/devices.$id.settings.general.reboot"));
|
const SettingsGeneralRebootRoute = lazy(
|
||||||
const SettingsGeneralUpdateRoute = lazy(() => import("@routes/devices.$id.settings.general.update"));
|
() => import("@routes/devices.$id.settings.general.reboot"),
|
||||||
|
);
|
||||||
|
const SettingsGeneralUpdateRoute = lazy(
|
||||||
|
() => import("@routes/devices.$id.settings.general.update"),
|
||||||
|
);
|
||||||
const SettingsNetworkRoute = lazy(() => import("@routes/devices.$id.settings.network"));
|
const SettingsNetworkRoute = lazy(() => import("@routes/devices.$id.settings.network"));
|
||||||
const SecurityAccessLocalAuthRoute = lazy(() => import("@routes/devices.$id.settings.access.local-auth"));
|
const SecurityAccessLocalAuthRoute = lazy(
|
||||||
|
() => import("@routes/devices.$id.settings.access.local-auth"),
|
||||||
|
);
|
||||||
const SettingsMacrosRoute = lazy(() => import("@routes/devices.$id.settings.macros"));
|
const SettingsMacrosRoute = lazy(() => import("@routes/devices.$id.settings.macros"));
|
||||||
const SettingsMacrosAddRoute = lazy(() => import("@routes/devices.$id.settings.macros.add"));
|
const SettingsMacrosAddRoute = lazy(() => import("@routes/devices.$id.settings.macros.add"));
|
||||||
const SettingsMacrosEditRoute = lazy(() => import("@routes/devices.$id.settings.macros.edit"));
|
const SettingsMacrosEditRoute = lazy(() => import("@routes/devices.$id.settings.macros.edit"));
|
||||||
|
|
@ -364,7 +370,7 @@ if (isOnDevice) {
|
||||||
{
|
{
|
||||||
path: "devices",
|
path: "devices",
|
||||||
element: <DevicesRoute />,
|
element: <DevicesRoute />,
|
||||||
loader: DevicesRoute.loader
|
loader: DevicesRoute.loader,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -19,8 +19,9 @@ const ToastContent = ({
|
||||||
t: Toast;
|
t: Toast;
|
||||||
}) => (
|
}) => (
|
||||||
<Card
|
<Card
|
||||||
className={`${t.visible ? "animate-enter" : "animate-leave"
|
className={`${
|
||||||
} pointer-events-auto z-30 w-full max-w-sm shadow-xl!`}
|
t.visible ? "animate-enter" : "animate-leave"
|
||||||
|
} pointer-events-auto z-30 w-full max-w-sm shadow-xl!`}
|
||||||
>
|
>
|
||||||
<div className="flex items-center gap-x-2 p-2.5 px-2">
|
<div className="flex items-center gap-x-2 p-2.5 px-2">
|
||||||
{icon}
|
{icon}
|
||||||
|
|
@ -34,7 +35,7 @@ const notifications = {
|
||||||
return toast.custom(
|
return toast.custom(
|
||||||
(t: Toast) => (
|
(t: Toast) => (
|
||||||
<ToastContent
|
<ToastContent
|
||||||
icon={<CheckCircleIcon className="w-5 h-5 text-green-500 dark:text-green-400" />}
|
icon={<CheckCircleIcon className="h-5 w-5 text-green-500 dark:text-green-400" />}
|
||||||
message={message}
|
message={message}
|
||||||
t={t}
|
t={t}
|
||||||
/>
|
/>
|
||||||
|
|
@ -47,7 +48,7 @@ const notifications = {
|
||||||
return toast.custom(
|
return toast.custom(
|
||||||
(t: Toast) => (
|
(t: Toast) => (
|
||||||
<ToastContent
|
<ToastContent
|
||||||
icon={<XCircleIcon className="w-5 h-5 text-red-500 dark:text-red-400" />}
|
icon={<XCircleIcon className="h-5 w-5 text-red-500 dark:text-red-400" />}
|
||||||
message={message}
|
message={message}
|
||||||
t={t}
|
t={t}
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,5 @@ export const FeatureFlagProvider = ({
|
||||||
|
|
||||||
const value = { appVersion, isFeatureEnabled };
|
const value = { appVersion, isFeatureEnabled };
|
||||||
|
|
||||||
return (
|
return <FeatureFlagContext.Provider value={value}>{children}</FeatureFlagContext.Provider>;
|
||||||
<FeatureFlagContext.Provider value={value}>{children}</FeatureFlagContext.Provider>
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,7 @@ const loader: LoaderFunction = async ({ request }: LoaderFunctionArgs) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function AdoptRoute() {
|
export default function AdoptRoute() {
|
||||||
return (<></>);
|
return <></>;
|
||||||
}
|
}
|
||||||
|
|
||||||
AdoptRoute.loader = loader;
|
AdoptRoute.loader = loader;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,10 @@
|
||||||
import { Form, redirect, useActionData, useLoaderData } from "react-router";
|
import { Form, redirect, useActionData, useLoaderData } from "react-router";
|
||||||
import type { ActionFunction, ActionFunctionArgs, LoaderFunction, LoaderFunctionArgs } from "react-router";
|
import type {
|
||||||
|
ActionFunction,
|
||||||
|
ActionFunctionArgs,
|
||||||
|
LoaderFunction,
|
||||||
|
LoaderFunctionArgs,
|
||||||
|
} from "react-router";
|
||||||
import { ChevronLeftIcon } from "@heroicons/react/16/solid";
|
import { ChevronLeftIcon } from "@heroicons/react/16/solid";
|
||||||
|
|
||||||
import { User } from "@hooks/stores";
|
import { User } from "@hooks/stores";
|
||||||
|
|
@ -76,9 +81,9 @@ export default function DevicesIdDeregister() {
|
||||||
kvmName={device?.name}
|
kvmName={device?.name}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div className="w-full h-full">
|
<div className="h-full w-full">
|
||||||
<div className="mt-4">
|
<div className="mt-4">
|
||||||
<div className="w-full h-full px-4 mx-auto space-y-6 sm:max-w-6xl sm:px-8 md:max-w-7xl md:px-12 lg:max-w-8xl">
|
<div className="mx-auto h-full w-full space-y-6 px-4 sm:max-w-6xl sm:px-8 md:max-w-7xl md:px-12 lg:max-w-8xl">
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<LinkButton
|
<LinkButton
|
||||||
size="SM"
|
size="SM"
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,6 @@
|
||||||
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||||
import { useNavigate } from "react-router";
|
import { useNavigate } from "react-router";
|
||||||
import {
|
import { LuLink, LuRadioReceiver, LuCheck, LuUpload } from "react-icons/lu";
|
||||||
LuLink,
|
|
||||||
LuRadioReceiver,
|
|
||||||
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 { TrashIcon } from "@heroicons/react/16/solid";
|
||||||
|
|
||||||
|
|
@ -43,13 +38,8 @@ export default function MountRoute() {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function Dialog({ onClose }: Readonly<{ onClose: () => void }>) {
|
export function Dialog({ onClose }: Readonly<{ onClose: () => void }>) {
|
||||||
const {
|
const { modalView, setModalView, setRemoteVirtualMediaState, errorMessage, setErrorMessage } =
|
||||||
modalView,
|
useMountMediaStore();
|
||||||
setModalView,
|
|
||||||
setRemoteVirtualMediaState,
|
|
||||||
errorMessage,
|
|
||||||
setErrorMessage,
|
|
||||||
} = useMountMediaStore();
|
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
const [incompleteFileName, setIncompleteFileName] = useState<string | null>(null);
|
const [incompleteFileName, setIncompleteFileName] = useState<string | null>(null);
|
||||||
|
|
@ -65,9 +55,7 @@ export function Dialog({ onClose }: Readonly<{ onClose: () => void }>) {
|
||||||
if ("error" in resp) {
|
if ("error" in resp) {
|
||||||
reject(new Error(resp.error.message));
|
reject(new Error(resp.error.message));
|
||||||
} else {
|
} else {
|
||||||
setRemoteVirtualMediaState(
|
setRemoteVirtualMediaState(resp as unknown as MountMediaState["remoteVirtualMediaState"]);
|
||||||
resp as unknown as MountMediaState["remoteVirtualMediaState"],
|
|
||||||
);
|
|
||||||
resolve(null);
|
resolve(null);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
@ -132,10 +120,7 @@ export function Dialog({ onClose }: Readonly<{ onClose: () => void }>) {
|
||||||
className={cx("mx-auto max-w-4xl px-4 transition-all duration-300 ease-in-out", {
|
className={cx("mx-auto max-w-4xl px-4 transition-all duration-300 ease-in-out", {
|
||||||
"max-w-4xl": modalView === "mode",
|
"max-w-4xl": modalView === "mode",
|
||||||
"max-w-2xl": modalView === "device",
|
"max-w-2xl": modalView === "device",
|
||||||
"max-w-xl":
|
"max-w-xl": modalView === "url" || modalView === "upload" || modalView === "error",
|
||||||
modalView === "url" ||
|
|
||||||
modalView === "upload" ||
|
|
||||||
modalView === "error",
|
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<GridCard cardClassName="relative w-full text-left pointer-events-auto">
|
<GridCard cardClassName="relative w-full text-left pointer-events-auto">
|
||||||
|
|
@ -281,9 +266,7 @@ function ModeSelectionView({
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className="relative z-50 flex flex-col items-start p-4 select-none"
|
className="relative z-50 flex flex-col items-start p-4 select-none"
|
||||||
onClick={() =>
|
onClick={() => (disabled ? null : setSelectedMode(mode as "url" | "device"))}
|
||||||
disabled ? null : setSelectedMode(mode as "url" | "device")
|
|
||||||
}
|
|
||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
<Card>
|
<Card>
|
||||||
|
|
@ -293,14 +276,10 @@ function ModeSelectionView({
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-2 space-y-0">
|
<div className="mt-2 space-y-0">
|
||||||
<p className="block pt-1 text-xs text-red-500">
|
<p className="block pt-1 text-xs text-red-500">{tag ? tag : <> </>}</p>
|
||||||
{tag ? tag : <> </>}
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<h3 className="text-sm font-medium dark:text-white">{label}</h3>
|
<h3 className="text-sm font-medium dark:text-white">{label}</h3>
|
||||||
<p className="text-sm text-gray-700 dark:text-slate-400">
|
<p className="text-sm text-gray-700 dark:text-slate-400">{description}</p>
|
||||||
{description}
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
<input
|
<input
|
||||||
type="radio"
|
type="radio"
|
||||||
|
|
@ -414,10 +393,7 @@ function UrlView({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-full space-y-4">
|
<div className="w-full space-y-4">
|
||||||
<ViewHeader
|
<ViewHeader title={m.mount_view_url_title()} description={m.mount_view_url_description()} />
|
||||||
title={m.mount_view_url_title()}
|
|
||||||
description={m.mount_view_url_description()}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<div
|
<div
|
||||||
className="animate-fadeIn opacity-0"
|
className="animate-fadeIn opacity-0"
|
||||||
|
|
@ -452,9 +428,7 @@ function UrlView({
|
||||||
loading={mountInProgress}
|
loading={mountInProgress}
|
||||||
text={m.mount_button_mount_url()}
|
text={m.mount_button_mount_url()}
|
||||||
onClick={() => onMount(url, usbMode)}
|
onClick={() => onMount(url, usbMode)}
|
||||||
disabled={
|
disabled={mountInProgress || !isUrlValid || url.length === 0}
|
||||||
mountInProgress || !isUrlValid || url.length === 0
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -538,10 +512,9 @@ function DeviceFileView({
|
||||||
const percentageUsed = useMemo(() => {
|
const percentageUsed = useMemo(() => {
|
||||||
if (!storageSpace) return 0;
|
if (!storageSpace) return 0;
|
||||||
return Number(
|
return Number(
|
||||||
(
|
((storageSpace.bytesUsed / (storageSpace.bytesUsed + storageSpace.bytesFree)) * 100).toFixed(
|
||||||
(storageSpace.bytesUsed / (storageSpace.bytesUsed + storageSpace.bytesFree)) *
|
1,
|
||||||
100
|
),
|
||||||
).toFixed(1),
|
|
||||||
);
|
);
|
||||||
}, [storageSpace]);
|
}, [storageSpace]);
|
||||||
|
|
||||||
|
|
@ -681,9 +654,7 @@ function DeviceFileView({
|
||||||
onDelete={() => {
|
onDelete={() => {
|
||||||
const selectedFile = onStorageFiles.find(f => f.name === file.name);
|
const selectedFile = onStorageFiles.find(f => f.name === file.name);
|
||||||
if (!selectedFile) return;
|
if (!selectedFile) return;
|
||||||
if (
|
if (window.confirm(m.mount_confirm_delete({ name: selectedFile.name }))) {
|
||||||
window.confirm(m.mount_confirm_delete({ name: selectedFile.name }))
|
|
||||||
) {
|
|
||||||
handleDeleteFile(selectedFile);
|
handleDeleteFile(selectedFile);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
|
|
@ -698,7 +669,7 @@ function DeviceFileView({
|
||||||
{m.mount_button_showing_results({
|
{m.mount_button_showing_results({
|
||||||
from: indexOfFirstFile + 1,
|
from: indexOfFirstFile + 1,
|
||||||
to: Math.min(indexOfLastFile, onStorageFiles.length),
|
to: Math.min(indexOfLastFile, onStorageFiles.length),
|
||||||
total: onStorageFiles.length
|
total: onStorageFiles.length,
|
||||||
})}
|
})}
|
||||||
</p>
|
</p>
|
||||||
<div className="flex items-center gap-x-2">
|
<div className="flex items-center gap-x-2">
|
||||||
|
|
@ -827,9 +798,7 @@ function UploadFileView({
|
||||||
onCancelUpload: () => void;
|
onCancelUpload: () => void;
|
||||||
incompleteFileName?: string;
|
incompleteFileName?: string;
|
||||||
}) {
|
}) {
|
||||||
const [uploadState, setUploadState] = useState<"idle" | "uploading" | "success">(
|
const [uploadState, setUploadState] = useState<"idle" | "uploading" | "success">("idle");
|
||||||
"idle",
|
|
||||||
);
|
|
||||||
const [uploadProgress, setUploadProgress] = useState(0);
|
const [uploadProgress, setUploadProgress] = useState(0);
|
||||||
const [uploadedFileName, setUploadedFileName] = useState<string | null>(null);
|
const [uploadedFileName, setUploadedFileName] = useState<string | null>(null);
|
||||||
const [uploadedFileSize, setUploadedFileSize] = useState<number | null>(null);
|
const [uploadedFileSize, setUploadedFileSize] = useState<number | null>(null);
|
||||||
|
|
@ -854,14 +823,8 @@ function UploadFileView({
|
||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
function handleWebRTCUpload(
|
function handleWebRTCUpload(file: File, alreadyUploadedBytes: number, dataChannel: string) {
|
||||||
file: File,
|
const rtcDataChannel = useRTCStore.getState().peerConnection?.createDataChannel(dataChannel);
|
||||||
alreadyUploadedBytes: number,
|
|
||||||
dataChannel: string,
|
|
||||||
) {
|
|
||||||
const rtcDataChannel = useRTCStore
|
|
||||||
.getState()
|
|
||||||
.peerConnection?.createDataChannel(dataChannel);
|
|
||||||
|
|
||||||
if (!rtcDataChannel) {
|
if (!rtcDataChannel) {
|
||||||
console.error("Failed to create data channel for file upload");
|
console.error("Failed to create data channel for file upload");
|
||||||
|
|
@ -903,8 +866,7 @@ function UploadFileView({
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate average speed
|
// Calculate average speed
|
||||||
const averageSpeed =
|
const averageSpeed = speedHistory.reduce((a, b) => a + b, 0) / speedHistory.length;
|
||||||
speedHistory.reduce((a, b) => a + b, 0) / speedHistory.length;
|
|
||||||
|
|
||||||
setUploadSpeed(averageSpeed);
|
setUploadSpeed(averageSpeed);
|
||||||
setUploadProgress((AlreadyUploadedBytes / Size) * 100);
|
setUploadProgress((AlreadyUploadedBytes / Size) * 100);
|
||||||
|
|
@ -961,11 +923,7 @@ function UploadFileView({
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handleHttpUpload(
|
async function handleHttpUpload(file: File, alreadyUploadedBytes: number, dataChannel: string) {
|
||||||
file: File,
|
|
||||||
alreadyUploadedBytes: number,
|
|
||||||
dataChannel: string,
|
|
||||||
) {
|
|
||||||
const uploadUrl = `${DEVICE_API}/storage/upload?uploadId=${dataChannel}`;
|
const uploadUrl = `${DEVICE_API}/storage/upload?uploadId=${dataChannel}`;
|
||||||
|
|
||||||
const xhr = new XMLHttpRequest();
|
const xhr = new XMLHttpRequest();
|
||||||
|
|
@ -994,8 +952,7 @@ function UploadFileView({
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate average speed
|
// Calculate average speed
|
||||||
const averageSpeed =
|
const averageSpeed = speedHistory.reduce((a, b) => a + b, 0) / speedHistory.length;
|
||||||
speedHistory.reduce((a, b) => a + b, 0) / speedHistory.length;
|
|
||||||
|
|
||||||
setUploadSpeed(averageSpeed);
|
setUploadSpeed(averageSpeed);
|
||||||
setUploadProgress((totalUploaded / totalSize) * 100);
|
setUploadProgress((totalUploaded / totalSize) * 100);
|
||||||
|
|
@ -1035,10 +992,7 @@ function UploadFileView({
|
||||||
// Reset the upload error when a new file is selected
|
// Reset the upload error when a new file is selected
|
||||||
setUploadError(null);
|
setUploadError(null);
|
||||||
|
|
||||||
if (
|
if (incompleteFileName && file.name !== incompleteFileName.replace(".incomplete", "")) {
|
||||||
incompleteFileName &&
|
|
||||||
file.name !== incompleteFileName.replace(".incomplete", "")
|
|
||||||
) {
|
|
||||||
setFileError(
|
setFileError(
|
||||||
m.mount_please_select_file({ name: incompleteFileName.replace(".incomplete", "") }),
|
m.mount_please_select_file({ name: incompleteFileName.replace(".incomplete", "") }),
|
||||||
);
|
);
|
||||||
|
|
@ -1052,31 +1006,35 @@ function UploadFileView({
|
||||||
setUploadState("uploading");
|
setUploadState("uploading");
|
||||||
console.log("Upload state set to 'uploading'");
|
console.log("Upload state set to 'uploading'");
|
||||||
|
|
||||||
send("startStorageFileUpload", { filename: file.name, size: file.size }, (resp: JsonRpcResponse) => {
|
send(
|
||||||
console.log("startStorageFileUpload response:", resp);
|
"startStorageFileUpload",
|
||||||
if ("error" in resp) {
|
{ filename: file.name, size: file.size },
|
||||||
console.error("Upload error:", resp.error.message);
|
(resp: JsonRpcResponse) => {
|
||||||
setUploadError(resp.error.data || resp.error.message);
|
console.log("startStorageFileUpload response:", resp);
|
||||||
setUploadState("idle");
|
if ("error" in resp) {
|
||||||
console.log("Upload state set to 'idle'");
|
console.error("Upload error:", resp.error.message);
|
||||||
return;
|
setUploadError(resp.error.data || resp.error.message);
|
||||||
}
|
setUploadState("idle");
|
||||||
|
console.log("Upload state set to 'idle'");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const { alreadyUploadedBytes, dataChannel } = resp.result as {
|
const { alreadyUploadedBytes, dataChannel } = resp.result as {
|
||||||
alreadyUploadedBytes: number;
|
alreadyUploadedBytes: number;
|
||||||
dataChannel: string;
|
dataChannel: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
console.log(
|
console.log(
|
||||||
`Already uploaded bytes: ${alreadyUploadedBytes}, Data channel: ${dataChannel}`,
|
`Already uploaded bytes: ${alreadyUploadedBytes}, Data channel: ${dataChannel}`,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (isOnDevice) {
|
if (isOnDevice) {
|
||||||
handleHttpUpload(file, alreadyUploadedBytes, dataChannel);
|
handleHttpUpload(file, alreadyUploadedBytes, dataChannel);
|
||||||
} else {
|
} else {
|
||||||
handleWebRTCUpload(file, alreadyUploadedBytes, dataChannel);
|
handleWebRTCUpload(file, alreadyUploadedBytes, dataChannel);
|
||||||
}
|
}
|
||||||
});
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -1086,7 +1044,9 @@ function UploadFileView({
|
||||||
title={m.mount_upload_title()}
|
title={m.mount_upload_title()}
|
||||||
description={
|
description={
|
||||||
incompleteFileName
|
incompleteFileName
|
||||||
? m.mount_continue_uploading_with_name({ name: incompleteFileName.replace(".incomplete", "") })
|
? m.mount_continue_uploading_with_name({
|
||||||
|
name: incompleteFileName.replace(".incomplete", ""),
|
||||||
|
})
|
||||||
: m.mount_upload_description()
|
: m.mount_upload_description()
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|
@ -1124,7 +1084,9 @@ function UploadFileView({
|
||||||
</div>
|
</div>
|
||||||
<h3 className="text-sm leading-none font-semibold text-black dark:text-white">
|
<h3 className="text-sm leading-none font-semibold text-black dark:text-white">
|
||||||
{incompleteFileName
|
{incompleteFileName
|
||||||
? m.mount_click_to_select_incomplete({ name: incompleteFileName.replace(".incomplete", "") })
|
? m.mount_click_to_select_incomplete({
|
||||||
|
name: incompleteFileName.replace(".incomplete", ""),
|
||||||
|
})
|
||||||
: m.mount_click_to_select_file()}
|
: m.mount_click_to_select_file()}
|
||||||
</h3>
|
</h3>
|
||||||
<p className="text-xs leading-none text-slate-700 dark:text-slate-300">
|
<p className="text-xs leading-none text-slate-700 dark:text-slate-300">
|
||||||
|
|
@ -1143,7 +1105,9 @@ function UploadFileView({
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
<h3 className="leading-non text-lg font-semibold text-black dark:text-white">
|
<h3 className="leading-non text-lg font-semibold text-black dark:text-white">
|
||||||
{m.mount_uploading_with_name({ name: formatters.truncateMiddle(uploadedFileName, 30) })}
|
{m.mount_uploading_with_name({
|
||||||
|
name: formatters.truncateMiddle(uploadedFileName, 30),
|
||||||
|
})}
|
||||||
</h3>
|
</h3>
|
||||||
<p className="text-xs leading-none text-slate-700 dark:text-slate-300">
|
<p className="text-xs leading-none text-slate-700 dark:text-slate-300">
|
||||||
{formatters.bytes(uploadedFileSize || 0)}
|
{formatters.bytes(uploadedFileSize || 0)}
|
||||||
|
|
@ -1180,7 +1144,9 @@ function UploadFileView({
|
||||||
{m.mount_upload_successful()}
|
{m.mount_upload_successful()}
|
||||||
</h3>
|
</h3>
|
||||||
<p className="text-xs leading-none text-slate-700 dark:text-slate-300">
|
<p className="text-xs leading-none text-slate-700 dark:text-slate-300">
|
||||||
{m.mount_uploaded_has_been_uploaded({ name: formatters.truncateMiddle(uploadedFileName, 40) })}
|
{m.mount_uploaded_has_been_uploaded({
|
||||||
|
name: formatters.truncateMiddle(uploadedFileName, 40),
|
||||||
|
})}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
@ -1196,15 +1162,13 @@ function UploadFileView({
|
||||||
className="hidden"
|
className="hidden"
|
||||||
accept=".iso, .img"
|
accept=".iso, .img"
|
||||||
/>
|
/>
|
||||||
{fileError && (
|
{fileError && <p className="mt-2 text-sm text-red-600 dark:text-red-400">{fileError}</p>}
|
||||||
<p className="mt-2 text-sm text-red-600 dark:text-red-400">{fileError}</p>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Display upload error if present */}
|
{/* Display upload error if present */}
|
||||||
{uploadError && (
|
{uploadError && (
|
||||||
<div
|
<div
|
||||||
className="mt-2 animate-fadeIn truncate text-sm text-red-600 dark:text-red-400 opacity-0"
|
className="mt-2 animate-fadeIn truncate text-sm text-red-600 opacity-0 dark:text-red-400"
|
||||||
style={{ animationDuration: "0.7s" }}
|
style={{ animationDuration: "0.7s" }}
|
||||||
>
|
>
|
||||||
{m.mount_upload_error({ error: String(uploadError) })}
|
{m.mount_upload_error({ error: String(uploadError) })}
|
||||||
|
|
@ -1263,9 +1227,7 @@ function ErrorView({
|
||||||
<ExclamationTriangleIcon className="h-6 w-6" />
|
<ExclamationTriangleIcon className="h-6 w-6" />
|
||||||
<h2 className="text-lg leading-tight font-bold">{m.mount_error_title()}</h2>
|
<h2 className="text-lg leading-tight font-bold">{m.mount_error_title()}</h2>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-sm leading-snug text-slate-600">
|
<p className="text-sm leading-snug text-slate-600">{m.mount_error_description()}</p>
|
||||||
{m.mount_error_description()}
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
{errorMessage && (
|
{errorMessage && (
|
||||||
<Card className="border border-red-200 bg-red-50 p-4">
|
<Card className="border border-red-200 bg-red-50 p-4">
|
||||||
|
|
@ -1274,7 +1236,12 @@ function ErrorView({
|
||||||
)}
|
)}
|
||||||
<div className="flex justify-end space-x-2">
|
<div className="flex justify-end space-x-2">
|
||||||
<Button size="SM" theme="light" text={m.close()} onClick={onClose} />
|
<Button size="SM" theme="light" text={m.close()} onClick={onClose} />
|
||||||
<Button size="SM" theme="primary" text={m.mount_button_back_to_overview()} onClick={onRetry} />
|
<Button
|
||||||
|
size="SM"
|
||||||
|
theme="primary"
|
||||||
|
text={m.mount_button_back_to_overview()}
|
||||||
|
onClick={onRetry}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
@ -1379,12 +1346,8 @@ function PreUploadedImageItem({
|
||||||
function ViewHeader({ title, description }: { title: string; description: string }) {
|
function ViewHeader({ title, description }: { title: string; description: string }) {
|
||||||
return (
|
return (
|
||||||
<div className="space-y-0">
|
<div className="space-y-0">
|
||||||
<h2 className="text-lg leading-tight font-bold text-black dark:text-white">
|
<h2 className="text-lg leading-tight font-bold text-black dark:text-white">{title}</h2>
|
||||||
{title}
|
<div className="text-sm leading-snug text-slate-600 dark:text-slate-400">{description}</div>
|
||||||
</h2>
|
|
||||||
<div className="text-sm leading-snug text-slate-600 dark:text-slate-400">
|
|
||||||
{description}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -30,14 +30,17 @@ export default function OtherSessionRoute() {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="text-left">
|
<div className="text-left">
|
||||||
<p className="text-base font-semibold dark:text-white">
|
<p className="text-base font-semibold dark:text-white">{m.other_session_detected()}</p>
|
||||||
{m.other_session_detected()}
|
|
||||||
</p>
|
|
||||||
<p className="mb-4 text-sm text-slate-600 dark:text-slate-400">
|
<p className="mb-4 text-sm text-slate-600 dark:text-slate-400">
|
||||||
{m.other_session_take_over()}
|
{m.other_session_take_over()}
|
||||||
</p>
|
</p>
|
||||||
<div className="flex items-center justify-start space-x-4">
|
<div className="flex items-center justify-start space-x-4">
|
||||||
<Button size="SM" theme="primary" text={m.other_session_use_here_button()} onClick={handleClose} />
|
<Button
|
||||||
|
size="SM"
|
||||||
|
theme="primary"
|
||||||
|
text={m.other_session_use_here_button()}
|
||||||
|
onClick={handleClose}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,10 @@
|
||||||
import { Form, redirect, useActionData, useLoaderData } from "react-router";
|
import { Form, redirect, useActionData, useLoaderData } from "react-router";
|
||||||
import type { ActionFunction, ActionFunctionArgs, LoaderFunction, LoaderFunctionArgs } from "react-router";
|
import type {
|
||||||
|
ActionFunction,
|
||||||
|
ActionFunctionArgs,
|
||||||
|
LoaderFunction,
|
||||||
|
LoaderFunctionArgs,
|
||||||
|
} from "react-router";
|
||||||
import { ChevronLeftIcon } from "@heroicons/react/16/solid";
|
import { ChevronLeftIcon } from "@heroicons/react/16/solid";
|
||||||
|
|
||||||
import { Button, LinkButton } from "@components/Button";
|
import { Button, LinkButton } from "@components/Button";
|
||||||
|
|
|
||||||
|
|
@ -5,10 +5,10 @@ import { getDeviceUiPath } from "@hooks/useAppNavigation";
|
||||||
|
|
||||||
const loader: LoaderFunction = ({ params }: LoaderFunctionArgs) => {
|
const loader: LoaderFunction = ({ params }: LoaderFunctionArgs) => {
|
||||||
return redirect(getDeviceUiPath("/settings/general", params.id));
|
return redirect(getDeviceUiPath("/settings/general", params.id));
|
||||||
}
|
};
|
||||||
|
|
||||||
export default function SettingIndexRoute() {
|
export default function SettingIndexRoute() {
|
||||||
return (<></>);
|
return <></>;
|
||||||
}
|
}
|
||||||
|
|
||||||
SettingIndexRoute.loader = loader;
|
SettingIndexRoute.loader = loader;
|
||||||
|
|
|
||||||
|
|
@ -125,10 +125,7 @@ export default function SettingsAccessIndexRoute() {
|
||||||
returnTo.search = "";
|
returnTo.search = "";
|
||||||
returnTo.hash = "";
|
returnTo.hash = "";
|
||||||
window.location.href =
|
window.location.href =
|
||||||
cloudAppUrl +
|
cloudAppUrl + "/signup?deviceId=" + deviceId + `&returnTo=${returnTo.toString()}`;
|
||||||
"/signup?deviceId=" +
|
|
||||||
deviceId +
|
|
||||||
`&returnTo=${returnTo.toString()}`;
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
[deviceId, send],
|
[deviceId, send],
|
||||||
|
|
@ -168,7 +165,9 @@ export default function SettingsAccessIndexRoute() {
|
||||||
|
|
||||||
notifications.success(m.access_tls_updated());
|
notifications.success(m.access_tls_updated());
|
||||||
});
|
});
|
||||||
}, [send]);
|
},
|
||||||
|
[send],
|
||||||
|
);
|
||||||
|
|
||||||
// Handle TLS mode change
|
// Handle TLS mode change
|
||||||
const handleTlsModeChange = (value: string) => {
|
const handleTlsModeChange = (value: string) => {
|
||||||
|
|
@ -206,10 +205,7 @@ export default function SettingsAccessIndexRoute() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<SettingsPageHeader
|
<SettingsPageHeader title={m.access_title()} description={m.access_description()} />
|
||||||
title={m.access_title()}
|
|
||||||
description={m.access_description()}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{loaderData?.authMode && (
|
{loaderData?.authMode && (
|
||||||
<>
|
<>
|
||||||
|
|
@ -246,9 +242,7 @@ export default function SettingsAccessIndexRoute() {
|
||||||
<TextAreaWithLabel
|
<TextAreaWithLabel
|
||||||
label={m.access_certificate_label()}
|
label={m.access_certificate_label()}
|
||||||
rows={3}
|
rows={3}
|
||||||
placeholder={
|
placeholder={"-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----"}
|
||||||
"-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----"
|
|
||||||
}
|
|
||||||
value={tlsCert}
|
value={tlsCert}
|
||||||
onChange={e => handleTlsCertChange(e.target.value)}
|
onChange={e => handleTlsCertChange(e.target.value)}
|
||||||
/>
|
/>
|
||||||
|
|
@ -256,9 +250,7 @@ export default function SettingsAccessIndexRoute() {
|
||||||
label={m.access_private_key_label()}
|
label={m.access_private_key_label()}
|
||||||
description={m.access_private_key_description()}
|
description={m.access_private_key_description()}
|
||||||
rows={3}
|
rows={3}
|
||||||
placeholder={
|
placeholder={"-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----"}
|
||||||
"-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----"
|
|
||||||
}
|
|
||||||
value={tlsKey}
|
value={tlsKey}
|
||||||
onChange={e => handleTlsKeyChange(e.target.value)}
|
onChange={e => handleTlsKeyChange(e.target.value)}
|
||||||
/>
|
/>
|
||||||
|
|
@ -275,7 +267,11 @@ export default function SettingsAccessIndexRoute() {
|
||||||
|
|
||||||
<SettingsItem
|
<SettingsItem
|
||||||
title={m.access_authentication_mode_title()}
|
title={m.access_authentication_mode_title()}
|
||||||
description={loaderData.authMode === "password" ? m.access_auth_mode_password() : m.access_auth_mode_no_password()}
|
description={
|
||||||
|
loaderData.authMode === "password"
|
||||||
|
? m.access_auth_mode_password()
|
||||||
|
: m.access_auth_mode_no_password()
|
||||||
|
}
|
||||||
>
|
>
|
||||||
{loaderData.authMode === "password" ? (
|
{loaderData.authMode === "password" ? (
|
||||||
<Button
|
<Button
|
||||||
|
|
@ -320,10 +316,7 @@ export default function SettingsAccessIndexRoute() {
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<SettingsSectionHeader
|
<SettingsSectionHeader title="Remote" description={m.access_remote_description()} />
|
||||||
title="Remote"
|
|
||||||
description={m.access_remote_description()}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
{!isAdopted && (
|
{!isAdopted && (
|
||||||
|
|
@ -438,11 +431,7 @@ export default function SettingsAccessIndexRoute() {
|
||||||
className="text-red-600"
|
className="text-red-600"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (deviceId) {
|
if (deviceId) {
|
||||||
if (
|
if (window.confirm(m.access_confirm_deregister())) {
|
||||||
window.confirm(
|
|
||||||
m.access_confirm_deregister(),
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
deregisterDevice();
|
deregisterDevice();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue