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": {
|
||||
"ghcr.io/devcontainers/features/node:1": {
|
||||
// Should match what is defined in ui/package.json
|
||||
"version": "22.20.0"
|
||||
"version": "22.21.1"
|
||||
}
|
||||
},
|
||||
"mounts": [
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
"features": {
|
||||
"ghcr.io/devcontainers/features/node:1": {
|
||||
// Should match what is defined in ui/package.json
|
||||
"version": "22.20.0"
|
||||
"version": "22.21.1"
|
||||
}
|
||||
},
|
||||
"runArgs": [
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "kvm-ui",
|
||||
"private": true,
|
||||
"version": "2025.11.11.1900",
|
||||
"version": "2025.11.20.0300",
|
||||
"type": "module",
|
||||
"engines": {
|
||||
"node": "^22.21.1"
|
||||
|
|
@ -47,11 +47,11 @@
|
|||
"react": "^19.2.0",
|
||||
"react-animate-height": "^3.2.3",
|
||||
"react-dom": "^19.2.0",
|
||||
"react-hook-form": "^7.66.0",
|
||||
"react-hook-form": "^7.66.1",
|
||||
"react-hot-toast": "^2.6.0",
|
||||
"react-icons": "^5.5.0",
|
||||
"react-router": "^7.9.5",
|
||||
"react-simple-keyboard": "^3.8.133",
|
||||
"react-router": "^7.9.6",
|
||||
"react-simple-keyboard": "^3.8.136",
|
||||
"react-use-websocket": "^4.13.0",
|
||||
"react-xtermjs": "^1.0.10",
|
||||
"recharts": "^3.4.1",
|
||||
|
|
@ -61,11 +61,11 @@
|
|||
"zustand": "^4.5.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/compat": "^1.4.1",
|
||||
"@eslint/compat": "^2.0.0",
|
||||
"@eslint/eslintrc": "^3.3.1",
|
||||
"@eslint/js": "^9.39.1",
|
||||
"@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-message-format": "^4.0.0",
|
||||
"@inlang/sdk": "^2.4.9",
|
||||
|
|
@ -73,13 +73,13 @@
|
|||
"@tailwindcss/postcss": "^4.1.17",
|
||||
"@tailwindcss/typography": "^0.5.19",
|
||||
"@tailwindcss/vite": "^4.1.17",
|
||||
"@types/react": "^19.2.3",
|
||||
"@types/react-dom": "^19.2.2",
|
||||
"@types/react": "^19.2.6",
|
||||
"@types/react-dom": "^19.2.3",
|
||||
"@types/semver": "^7.7.1",
|
||||
"@types/validator": "^13.15.5",
|
||||
"@typescript-eslint/eslint-plugin": "^8.46.4",
|
||||
"@typescript-eslint/parser": "^8.46.4",
|
||||
"@vitejs/plugin-react-swc": "^4.2.1",
|
||||
"@types/validator": "^13.15.10",
|
||||
"@typescript-eslint/eslint-plugin": "^8.47.0",
|
||||
"@typescript-eslint/parser": "^8.47.0",
|
||||
"@vitejs/plugin-react-swc": "^4.2.2",
|
||||
"autoprefixer": "^10.4.22",
|
||||
"eslint": "^9.39.1",
|
||||
"eslint-config-prettier": "^10.1.8",
|
||||
|
|
@ -95,7 +95,7 @@
|
|||
"prettier-plugin-tailwindcss": "^0.7.1",
|
||||
"tailwindcss": "^4.1.17",
|
||||
"typescript": "^5.9.3",
|
||||
"typescript-eslint": "^8.46.4",
|
||||
"typescript-eslint": "^8.47.0",
|
||||
"vite": "^7.2.2",
|
||||
"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 { cx } from "@/cva.config";
|
||||
import {
|
||||
useHidStore,
|
||||
useMountMediaStore,
|
||||
useSettingsStore,
|
||||
useUiStore,
|
||||
} from "@hooks/stores";
|
||||
import { useHidStore, useMountMediaStore, useSettingsStore, useUiStore } from "@hooks/stores";
|
||||
import { useDeviceUiNavigation } from "@hooks/useAppNavigation";
|
||||
import { Button } from "@components/Button";
|
||||
import Container from "@components/Container";
|
||||
|
|
@ -28,7 +23,8 @@ export default function Actionbar({
|
|||
}) {
|
||||
const { navigateTo } = useDeviceUiNavigation();
|
||||
const { isVirtualKeyboardEnabled, setVirtualKeyboardEnabled } = useHidStore();
|
||||
const { setDisableVideoFocusTrap, terminalType, setTerminalType, toggleSidebarView } = useUiStore();
|
||||
const { setDisableVideoFocusTrap, terminalType, setTerminalType, toggleSidebarView } =
|
||||
useUiStore();
|
||||
const { remoteVirtualMediaState } = useMountMediaStore();
|
||||
const { developerMode } = useSettingsStore();
|
||||
|
||||
|
|
@ -246,10 +242,7 @@ export default function Actionbar({
|
|||
theme="light"
|
||||
text={m.action_bar_connection_stats()}
|
||||
LeadingIcon={({ className }) => (
|
||||
<LuSignal
|
||||
className={cx(className, "mb-0.5 text-green-500")}
|
||||
strokeWidth={4}
|
||||
/>
|
||||
<LuSignal className={cx(className, "mb-0.5 text-green-500")} strokeWidth={4} />
|
||||
)}
|
||||
onClick={() => {
|
||||
toggleSidebarView("connection-stats");
|
||||
|
|
@ -264,7 +257,7 @@ export default function Actionbar({
|
|||
LeadingIcon={LuSettings}
|
||||
onClick={() => {
|
||||
setDisableVideoFocusTrap(true);
|
||||
navigateTo("/settings")
|
||||
navigateTo("/settings");
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -55,9 +55,7 @@ export default function AuthLayout({
|
|||
</div>
|
||||
) : null}
|
||||
<div className="space-y-2 text-center">
|
||||
<h1 className="text-4xl font-semibold text-black dark:text-white">
|
||||
{title}
|
||||
</h1>
|
||||
<h1 className="text-4xl font-semibold text-black dark:text-white">{title}</h1>
|
||||
<p className="text-slate-600 dark:text-slate-400">{description}</p>
|
||||
</div>
|
||||
|
||||
|
|
@ -65,12 +63,8 @@ export default function AuthLayout({
|
|||
<div className="mx-auto max-w-sm space-y-4">
|
||||
<form action={`${CLOUD_API}/oidc/google`} method="POST">
|
||||
{/*This could be the KVM ID*/}
|
||||
{deviceId ? (
|
||||
<input type="hidden" name="deviceId" value={deviceId} />
|
||||
) : null}
|
||||
{returnTo ? (
|
||||
<input type="hidden" name="returnTo" value={returnTo} />
|
||||
) : null}
|
||||
{deviceId ? <input type="hidden" name="deviceId" value={deviceId} /> : null}
|
||||
{returnTo ? <input type="hidden" name="returnTo" value={returnTo} /> : null}
|
||||
<Button
|
||||
size="LG"
|
||||
theme="light"
|
||||
|
|
@ -80,8 +74,7 @@ export default function AuthLayout({
|
|||
textAlign="center"
|
||||
type="submit"
|
||||
loading={
|
||||
(navigation.state === "submitting" ||
|
||||
navigation.state === "loading") &&
|
||||
(navigation.state === "submitting" || navigation.state === "loading") &&
|
||||
navigation.formMethod?.toLowerCase() === "post" &&
|
||||
navigation.formAction?.includes("auth/google")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ const sizes = {
|
|||
const themes = {
|
||||
primary: cx(
|
||||
// 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
|
||||
"group-hover:bg-blue-800",
|
||||
// Active states
|
||||
|
|
@ -24,9 +24,9 @@ const themes = {
|
|||
),
|
||||
danger: cx(
|
||||
// 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
|
||||
"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
|
||||
"group-active:bg-red-800 dark:group-active:bg-red-800",
|
||||
// Focus states
|
||||
|
|
@ -34,7 +34,7 @@ const themes = {
|
|||
),
|
||||
light: cx(
|
||||
// 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
|
||||
"group-hover:bg-blue-50/80 dark:group-hover:bg-slate-700",
|
||||
// Active states
|
||||
|
|
@ -44,7 +44,7 @@ const themes = {
|
|||
),
|
||||
lightDanger: cx(
|
||||
// Base styles
|
||||
"bg-white text-black border-red-400/60 shadow-xs",
|
||||
"border-red-400/60 bg-white text-black shadow-xs",
|
||||
// Hover states
|
||||
"group-hover:bg-red-50/80",
|
||||
// Active states
|
||||
|
|
@ -54,9 +54,9 @@ const themes = {
|
|||
),
|
||||
blank: cx(
|
||||
// Base styles
|
||||
"bg-white/0 text-black border-transparent dark:text-white",
|
||||
"border-transparent bg-white/0 text-black dark:text-white",
|
||||
// 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
|
||||
"group-active:bg-slate-100/80",
|
||||
),
|
||||
|
|
@ -65,16 +65,16 @@ const themes = {
|
|||
const btnVariants = cva({
|
||||
base: cx(
|
||||
// Base styles
|
||||
"border rounded-sm select-none",
|
||||
"rounded-sm border select-none",
|
||||
// Size classes
|
||||
"justify-center items-center shrink-0",
|
||||
"shrink-0 items-center justify-center",
|
||||
// Transition classes
|
||||
"outline-hidden transition-all duration-200",
|
||||
// Text classes
|
||||
"font-display text-center font-medium leading-tight",
|
||||
"text-center font-display leading-tight font-medium",
|
||||
// States
|
||||
"group-focus:outline-hidden group-focus:ring-2 group-focus:ring-offset-2 group-focus:ring-blue-700",
|
||||
"group-disabled:opacity-50 group-disabled:pointer-events-none",
|
||||
"group-focus:ring-2 group-focus:ring-blue-700 group-focus:ring-offset-2 group-focus:outline-hidden",
|
||||
"group-disabled:pointer-events-none group-disabled:opacity-50",
|
||||
),
|
||||
|
||||
variants: {
|
||||
|
|
@ -115,8 +115,7 @@ interface ButtonContentPropsType {
|
|||
}
|
||||
|
||||
function ButtonContent(props: ButtonContentPropsType) {
|
||||
const { text, LeadingIcon, TrailingIcon, fullWidth, className, textAlign, loading } =
|
||||
props;
|
||||
const { text, LeadingIcon, TrailingIcon, fullWidth, className, textAlign, loading } = props;
|
||||
|
||||
// Based on the size prop, we'll use the corresponding variant classnames
|
||||
const iconClassName = iconVariants(props);
|
||||
|
|
@ -136,9 +135,7 @@ function ButtonContent(props: ButtonContentPropsType) {
|
|||
<LoadingSpinner className={cx(iconClassName, "animate-spin")} />
|
||||
</div>
|
||||
) : (
|
||||
LeadingIcon && (
|
||||
<LeadingIcon className={cx(iconClassName, "shrink-0 justify-start")} />
|
||||
)
|
||||
LeadingIcon && <LeadingIcon className={cx(iconClassName, "shrink-0 justify-start")} />
|
||||
)}
|
||||
|
||||
{text && typeof text === "string" ? (
|
||||
|
|
@ -147,9 +144,7 @@ function ButtonContent(props: ButtonContentPropsType) {
|
|||
text
|
||||
)}
|
||||
|
||||
{TrailingIcon && (
|
||||
<TrailingIcon className={cx(iconClassName, "shrink-0 justify-end")} />
|
||||
)}
|
||||
{TrailingIcon && <TrailingIcon className={cx(iconClassName, "shrink-0 justify-end")} />}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
@ -175,7 +170,7 @@ type ButtonPropsType = Pick<
|
|||
export const Button = React.forwardRef<HTMLButtonElement, ButtonPropsType>(
|
||||
({ type, disabled, onClick, formNoValidate, loading, fetcher, ...props }, ref) => {
|
||||
const classes = cx(
|
||||
"group outline-hidden cursor-pointer",
|
||||
"group cursor-pointer outline-hidden",
|
||||
props.fullWidth ? "w-full" : "",
|
||||
loading ? "pointer-events-none" : "",
|
||||
);
|
||||
|
|
@ -212,7 +207,7 @@ export const Button = React.forwardRef<HTMLButtonElement, ButtonPropsType>(
|
|||
Button.displayName = "Button";
|
||||
|
||||
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) => {
|
||||
const classes = cx(
|
||||
"group outline-hidden",
|
||||
|
|
@ -241,7 +236,7 @@ type LabelPropsType = Pick<HTMLLabelElement, "htmlFor"> &
|
|||
React.ComponentProps<typeof ButtonContent> & { disabled?: boolean };
|
||||
export const LabelButton = ({ htmlFor, ...props }: LabelPropsType) => {
|
||||
const classes = cx(
|
||||
"group outline-hidden block cursor-pointer",
|
||||
"group block cursor-pointer outline-hidden",
|
||||
props.disabled ? "pointer-events-none opacity-70!" : "",
|
||||
props.fullWidth ? "w-full" : "",
|
||||
props.loading ? "pointer-events-none" : "",
|
||||
|
|
|
|||
|
|
@ -8,10 +8,12 @@ interface Props {
|
|||
|
||||
export const CardHeader = ({ headline, description, Button }: Props) => {
|
||||
return (
|
||||
<div className="flex items-center justify-between pb-0 gap-x-4">
|
||||
<div className="space-y-1 grow">
|
||||
<h3 className="text-lg font-bold leading-none text-black dark:text-white">{headline}</h3>
|
||||
{description && <div className="text-sm text-slate-700 dark:text-slate-300">{description}</div>}
|
||||
<div className="flex items-center justify-between gap-x-4 pb-0">
|
||||
<div className="grow space-y-1">
|
||||
<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>
|
||||
)}
|
||||
</div>
|
||||
{Button && <div>{Button}</div>}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ const checkboxVariants = cva({
|
|||
"form-checkbox block rounded",
|
||||
|
||||
// Colors
|
||||
"border-slate-300 dark:border-slate-600 bg-slate-50 dark:bg-slate-800 checked:accent-blue-700 checked:dark:accent-blue-500 transition-colors",
|
||||
"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: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",
|
||||
|
||||
// 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:pointer-events-none disabled:opacity-30",
|
||||
|
|
@ -41,9 +41,7 @@ const Checkbox = forwardRef<HTMLInputElement, CheckBoxProps>(function Checkbox(
|
|||
ref,
|
||||
) {
|
||||
const classes = checkboxVariants({ size });
|
||||
return (
|
||||
<input ref={ref} {...props} type="checkbox" className={clsx(classes, className)} />
|
||||
);
|
||||
return <input ref={ref} {...props} type="checkbox" className={clsx(classes, className)} />;
|
||||
});
|
||||
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",
|
||||
|
||||
// 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 &&
|
||||
"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}
|
||||
displayValue={displayValue}
|
||||
|
|
@ -95,7 +95,7 @@ export function Combobox({
|
|||
value={option}
|
||||
className={clsx(
|
||||
// General styling
|
||||
"cursor-default select-none px-4 py-2",
|
||||
"cursor-default px-4 py-2 select-none",
|
||||
|
||||
// Hover and active states
|
||||
"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="p-6">
|
||||
<div className="flex items-start gap-3.5">
|
||||
<Icon aria-hidden="true" className={cx("size-[18px] shrink-0 mt-[2px]", iconClass)} />
|
||||
<div className="flex-1 min-w-0 space-y-2">
|
||||
<h2 className="font-semibold text-slate-950 dark:text-white">
|
||||
{title}
|
||||
</h2>
|
||||
<div className="text-sm text-slate-700 dark:text-slate-300">
|
||||
{description}
|
||||
</div>
|
||||
<Icon
|
||||
aria-hidden="true"
|
||||
className={cx("mt-[2px] size-[18px] shrink-0", iconClass)}
|
||||
/>
|
||||
<div className="min-w-0 flex-1 space-y-2">
|
||||
<h2 className="font-semibold text-slate-950 dark:text-white">{title}</h2>
|
||||
<div className="text-sm text-slate-700 dark:text-slate-300">{description}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ import { LifeTimeLabel } from "@routes/devices.$id.settings.network";
|
|||
import { NetworkState } from "@hooks/stores";
|
||||
import { m } from "@localizations/messages.js";
|
||||
|
||||
|
||||
export default function DhcpLeaseCard({
|
||||
networkState,
|
||||
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">
|
||||
<span className="text-sm text-slate-600 dark:text-slate-400">
|
||||
{m.ip_address()}
|
||||
</span>
|
||||
<span className="text-sm font-medium">
|
||||
{networkState?.dhcp_lease?.ip}
|
||||
</span>
|
||||
|
||||
<span className="text-sm font-medium">{networkState?.dhcp_lease?.ip}</span>
|
||||
</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">
|
||||
<span className="text-sm text-slate-600 dark:text-slate-400">
|
||||
{m.subnet_mask()}
|
||||
</span>
|
||||
<span className="text-sm font-medium">
|
||||
{networkState?.dhcp_lease?.netmask}
|
||||
</span>
|
||||
|
||||
<span className="text-sm font-medium">{networkState?.dhcp_lease?.netmask}</span>
|
||||
</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">
|
||||
<span className="text-sm text-slate-600 dark:text-slate-400">
|
||||
{m.dns_servers()}
|
||||
</span>
|
||||
</span>
|
||||
|
||||
<span className="text-right text-sm font-medium">
|
||||
{networkState?.dhcp_lease?.dns_servers.map(dns => (
|
||||
<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">
|
||||
<span className="text-sm text-slate-600 dark:text-slate-400">
|
||||
{m.dhcp_lease_broadcast()}
|
||||
</span>
|
||||
<span className="text-sm font-medium">
|
||||
{networkState?.dhcp_lease?.broadcast}
|
||||
</span>
|
||||
|
||||
<span className="text-sm font-medium">{networkState?.dhcp_lease?.broadcast}</span>
|
||||
</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">
|
||||
<span className="text-sm text-slate-600 dark:text-slate-400">
|
||||
{m.dhcp_lease_domain()}
|
||||
</span>
|
||||
<span className="text-sm font-medium">
|
||||
{networkState?.dhcp_lease?.domain}
|
||||
</span>
|
||||
|
||||
<span className="text-sm font-medium">{networkState?.dhcp_lease?.domain}</span>
|
||||
</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="w-full grow text-sm text-slate-600 dark:text-slate-400">
|
||||
{m.ntp_servers()}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="shrink text-right text-sm font-medium">
|
||||
{networkState?.dhcp_lease?.ntp_servers.map(server => (
|
||||
<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">
|
||||
<span className="text-sm text-slate-600 dark:text-slate-400">
|
||||
{m.dhcp_lease_hostname()}
|
||||
</span>
|
||||
<span className="text-sm font-medium">
|
||||
{networkState?.dhcp_lease?.hostname}
|
||||
</span>
|
||||
|
||||
<span className="text-sm font-medium">{networkState?.dhcp_lease?.hostname}</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
|
@ -139,7 +135,8 @@ export default function DhcpLeaseCard({
|
|||
<div className="flex justify-between pt-2">
|
||||
<span className="text-sm text-slate-600 dark:text-slate-400">
|
||||
{m.dhcp_lease_gateway()}
|
||||
</span>
|
||||
</span>
|
||||
|
||||
<span className="text-right text-sm font-medium">
|
||||
{networkState?.dhcp_lease?.routers.map(router => (
|
||||
<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">
|
||||
<span className="text-sm text-slate-600 dark:text-slate-400">
|
||||
{m.dhcp_server()}
|
||||
</span>
|
||||
<span className="text-sm font-medium">
|
||||
{networkState?.dhcp_lease?.server_id}
|
||||
</span>
|
||||
|
||||
<span className="text-sm font-medium">{networkState?.dhcp_lease?.server_id}</span>
|
||||
</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">
|
||||
<span className="text-sm text-slate-600 dark:text-slate-400">
|
||||
{m.dhcp_lease_lease_expires()}
|
||||
</span>
|
||||
</span>
|
||||
|
||||
<span className="text-sm font-medium">
|
||||
<LifeTimeLabel
|
||||
lifetime={`${networkState?.dhcp_lease?.lease_expiry}`}
|
||||
/>
|
||||
<LifeTimeLabel lifetime={`${networkState?.dhcp_lease?.lease_expiry}`} />
|
||||
</span>
|
||||
</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">
|
||||
<span className="text-sm text-slate-600 dark:text-slate-400">
|
||||
{m.dhcp_lease_broadcast()}
|
||||
</span>
|
||||
<span className="text-sm font-medium">
|
||||
{networkState?.dhcp_lease?.broadcast}
|
||||
</span>
|
||||
|
||||
<span className="text-sm font-medium">{networkState?.dhcp_lease?.broadcast}</span>
|
||||
</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">
|
||||
<span className="text-sm text-slate-600 dark:text-slate-400">
|
||||
{m.dhcp_lease_maximum_transfer_unit()}
|
||||
</span>
|
||||
<span className="text-sm font-medium">
|
||||
{networkState?.dhcp_lease?.mtu}
|
||||
</span>
|
||||
|
||||
<span className="text-sm font-medium">{networkState?.dhcp_lease?.mtu}</span>
|
||||
</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">
|
||||
<span className="text-sm text-slate-600 dark:text-slate-400">
|
||||
{m.dhcp_lease_time_to_live()}
|
||||
</span>
|
||||
<span className="text-sm font-medium">
|
||||
{networkState?.dhcp_lease?.ttl}
|
||||
</span>
|
||||
|
||||
<span className="text-sm font-medium">{networkState?.dhcp_lease?.ttl}</span>
|
||||
</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">
|
||||
<span className="text-sm text-slate-600 dark:text-slate-400">
|
||||
{m.dhcp_lease_boot_next_server()}
|
||||
</span>
|
||||
</span>
|
||||
|
||||
<span className="text-sm font-medium">
|
||||
{networkState?.dhcp_lease?.bootp_next_server}
|
||||
</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">
|
||||
<span className="text-sm text-slate-600 dark:text-slate-400">
|
||||
{m.dhcp_lease_boot_server_name()}
|
||||
</span>
|
||||
</span>
|
||||
|
||||
<span className="text-sm font-medium">
|
||||
{networkState?.dhcp_lease?.bootp_server_name}
|
||||
</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">
|
||||
<span className="text-sm text-slate-600 dark:text-slate-400">
|
||||
{m.dhcp_lease_boot_file()}
|
||||
</span>
|
||||
</span>
|
||||
|
||||
<span className="text-sm font-medium">
|
||||
{networkState?.dhcp_lease?.bootp_file}
|
||||
</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">
|
||||
<span className="text-sm text-slate-600 dark:text-slate-400">
|
||||
{m.network_dhcp_client_title()}
|
||||
</span>
|
||||
</span>
|
||||
|
||||
<span className="text-sm font-medium">
|
||||
{networkState?.dhcp_lease?.dhcp_client}
|
||||
</span>
|
||||
|
|
|
|||
|
|
@ -11,13 +11,7 @@ interface Props {
|
|||
className?: string;
|
||||
}
|
||||
|
||||
export default function EmptyCard({
|
||||
IconElm,
|
||||
headline,
|
||||
description,
|
||||
BtnElm,
|
||||
className,
|
||||
}: Props) {
|
||||
export default function EmptyCard({ IconElm, headline, description, BtnElm, className }: Props) {
|
||||
return (
|
||||
<GridCard>
|
||||
<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="space-y-2">
|
||||
{IconElm && (
|
||||
<IconElm className="mx-auto h-5 w-5 text-blue-600 dark:text-blue-600" />
|
||||
)}
|
||||
<h4 className="text-base font-bold leading-none text-black dark:text-white">
|
||||
{IconElm && <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">
|
||||
{headline}
|
||||
</h4>
|
||||
</div>
|
||||
<p className="mx-auto text-sm text-slate-600 dark:text-slate-400">
|
||||
{description}
|
||||
</p>
|
||||
<p className="mx-auto text-sm text-slate-600 dark:text-slate-400">{description}</p>
|
||||
</div>
|
||||
{BtnElm}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -18,13 +18,10 @@ export function FailsafeModeBanner({ reason }: FailsafeModeBannerProps) {
|
|||
|
||||
return (
|
||||
<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" />
|
||||
<p className="text-sm font-medium text-red-800 dark:text-white">
|
||||
{getReasonMessage()}
|
||||
</p>
|
||||
<p className="text-sm font-medium text-red-800 dark:text-white">{getReasonMessage()}</p>
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -14,8 +14,6 @@ import { DOWNGRADE_VERSION } from "@/ui.config";
|
|||
|
||||
import { GitHubIcon } from "./Icons";
|
||||
|
||||
|
||||
|
||||
interface FailSafeModeOverlayProps {
|
||||
reason: string;
|
||||
}
|
||||
|
|
@ -45,13 +43,12 @@ function Tooltip({ children, text, show }: TooltipProps) {
|
|||
return <>{children}</>;
|
||||
}
|
||||
|
||||
|
||||
return (
|
||||
<div className="group/tooltip relative">
|
||||
{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">
|
||||
<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" />
|
||||
{text}
|
||||
</div>
|
||||
|
|
@ -152,7 +149,7 @@ Please attach the recovery logs file that was downloaded to your computer:
|
|||
return (
|
||||
<AnimatePresence>
|
||||
<motion.div
|
||||
className="aspect-video h-full w-full isolate"
|
||||
className="isolate aspect-video h-full w-full"
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
exit={{ opacity: 0, transition: { duration: 0 } }}
|
||||
|
|
@ -178,10 +175,12 @@ Please attach the recovery logs file that was downloaded to your computer:
|
|||
size="SM"
|
||||
disabled={isDownloadingLogs}
|
||||
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}>
|
||||
<Button
|
||||
onClick={() => navigateTo("/settings/general/reboot")}
|
||||
|
|
@ -202,8 +201,6 @@ Please attach the recovery logs file that was downloaded to your computer:
|
|||
/>
|
||||
</Tooltip>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -213,4 +210,3 @@ Please attach the recovery logs file that was downloaded to your computer:
|
|||
</AnimatePresence>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,19 +9,13 @@ interface Props {
|
|||
description?: string | React.ReactNode | null;
|
||||
disabled?: boolean;
|
||||
}
|
||||
export default function FieldLabel({
|
||||
label,
|
||||
id,
|
||||
as = "label",
|
||||
description,
|
||||
disabled,
|
||||
}: Props) {
|
||||
export default function FieldLabel({ label, id, as = "label", description, disabled }: Props) {
|
||||
if (as === "label") {
|
||||
return (
|
||||
<label
|
||||
htmlFor={id}
|
||||
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",
|
||||
)}
|
||||
>
|
||||
|
|
@ -35,8 +29,8 @@ export default function FieldLabel({
|
|||
);
|
||||
} else if (as === "span") {
|
||||
return (
|
||||
<div className="flex select-none flex-col">
|
||||
<span className="font-display text-[13px] font-semibold leading-snug text-black dark:text-white">
|
||||
<div className="flex flex-col select-none">
|
||||
<span className="font-display text-[13px] leading-snug font-semibold text-black dark:text-white">
|
||||
{label}
|
||||
</span>
|
||||
{description && (
|
||||
|
|
|
|||
|
|
@ -18,11 +18,7 @@ export default function GridBackground() {
|
|||
</pattern>
|
||||
</defs>
|
||||
|
||||
<svg
|
||||
x="50%"
|
||||
y={-1}
|
||||
className="overflow-visible fill-blue-100 dark:fill-blue-900/30"
|
||||
>
|
||||
<svg x="50%" y={-1} className="overflow-visible fill-blue-100 dark:fill-blue-900/30">
|
||||
<path
|
||||
d="M-200 0h201v201h-201Z M600 0h201v201h-201Z M-400 600h201v201h-201Z M200 800h201v201h-201Z"
|
||||
strokeWidth={0}
|
||||
|
|
|
|||
|
|
@ -59,8 +59,8 @@ export default function DashboardNavbar({
|
|||
<div className="flex h-14 items-center justify-between">
|
||||
<div className="flex shrink-0 items-center gap-x-8">
|
||||
<div className="inline-block shrink-0">
|
||||
<img src={LogoBlueIcon} alt="" className="h-[24px] dark:hidden" />
|
||||
<img src={LogoWhiteIcon} alt="" className="hidden h-[24px] dark:block" />
|
||||
<img src={LogoBlueIcon} alt="" className="h-6 dark:hidden" />
|
||||
<img src={LogoWhiteIcon} alt="" className="hidden h-6 dark:block" />
|
||||
</div>
|
||||
|
||||
<div className="flex gap-x-2">
|
||||
|
|
@ -84,22 +84,16 @@ export default function DashboardNavbar({
|
|||
{showConnectionStatus && (
|
||||
<>
|
||||
<div className="w-[159px]">
|
||||
<PeerConnectionStatusCard
|
||||
state={peerConnectionState}
|
||||
title={kvmName}
|
||||
/>
|
||||
<PeerConnectionStatusCard state={peerConnectionState} title={kvmName} />
|
||||
</div>
|
||||
<div className="hidden w-[159px] md:block">
|
||||
<USBStateStatus
|
||||
state={usbState}
|
||||
peerConnectionState={peerConnectionState}
|
||||
/>
|
||||
<USBStateStatus state={usbState} peerConnectionState={peerConnectionState} />
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
{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">
|
||||
<Menu>
|
||||
<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"
|
||||
/>
|
||||
) : 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}
|
||||
</span>
|
||||
) : null}
|
||||
|
|
@ -129,20 +123,15 @@ export default function DashboardNavbar({
|
|||
<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="p-2">
|
||||
<div className="font-display text-xs">
|
||||
{m.logged_in_as()}
|
||||
</div>
|
||||
<div className="font-display max-w-[200px] truncate text-sm font-semibold">
|
||||
<div className="font-display text-xs">{m.logged_in_as()}</div>
|
||||
<div className="max-w-[200px] truncate font-display text-sm font-semibold">
|
||||
{userEmail}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<div
|
||||
className="space-y-1 p-1 dark:text-white"
|
||||
onClick={onLogout}
|
||||
>
|
||||
<div 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">
|
||||
<ArrowLeftEndOnRectangleIcon className="size-4" />
|
||||
<div className="font-display">{m.log_out()}</div>
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import {
|
|||
useRTCStore,
|
||||
useSettingsStore,
|
||||
useVideoStore,
|
||||
VideoState
|
||||
VideoState,
|
||||
} from "@hooks/stores";
|
||||
import { useHidRpc } from "@hooks/useHidRpc";
|
||||
import { keys, modifiers } from "@/keyboardMappings";
|
||||
|
|
@ -48,10 +48,10 @@ export default function InfoBar() {
|
|||
}, [keysDownState, showPressedKeys]);
|
||||
|
||||
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 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 ? (
|
||||
<div className="flex">
|
||||
<span className="text-xs font-semibold">{m.info_resolution()}</span>{" "}
|
||||
|
|
@ -66,18 +66,22 @@ export default function InfoBar() {
|
|||
</div>
|
||||
) : null}
|
||||
|
||||
{(debugMode && mouseMode == "absolute") ? (
|
||||
{debugMode && mouseMode == "absolute" ? (
|
||||
<div className="flex w-[118px] items-center gap-x-1">
|
||||
<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>
|
||||
) : null}
|
||||
|
||||
{(debugMode && mouseMode == "relative") ? (
|
||||
{debugMode && mouseMode == "relative" ? (
|
||||
<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">
|
||||
{mouseMove ? `${mouseMove.x},${mouseMove.y} ${mouseMove.buttons ? `(${mouseMove.buttons})` : ""}` : "N/A"}
|
||||
{mouseMove
|
||||
? `${mouseMove.x},${mouseMove.y} ${mouseMove.buttons ? `(${mouseMove.buttons})` : ""}`
|
||||
: "N/A"}
|
||||
</span>
|
||||
</div>
|
||||
) : null}
|
||||
|
|
@ -119,7 +123,7 @@ export default function InfoBar() {
|
|||
</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 && (
|
||||
<div className="shrink-0 p-1 px-1.5 text-xs text-black dark:text-white">
|
||||
{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",
|
||||
|
||||
// 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: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 && (
|
||||
<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}
|
||||
</div>
|
||||
)}
|
||||
|
|
@ -60,12 +64,12 @@ const InputField = forwardRef<HTMLInputElement, InputFieldProps>(function InputF
|
|||
sizeClasses,
|
||||
TrailingElm ? "pr-2" : "",
|
||||
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}
|
||||
/>
|
||||
{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>
|
||||
{error && <FieldError error={error} />}
|
||||
|
|
@ -75,15 +79,10 @@ const InputField = forwardRef<HTMLInputElement, InputFieldProps>(function InputF
|
|||
InputField.displayName = "InputField";
|
||||
|
||||
const InputFieldWithLabel = forwardRef<HTMLInputElement, InputFieldWithLabelProps>(
|
||||
function InputFieldWithLabel(
|
||||
{ label, description, id, ...props },
|
||||
ref: Ref<HTMLInputElement>,
|
||||
) {
|
||||
function InputFieldWithLabel({ label, description, id, ...props }, ref: Ref<HTMLInputElement>) {
|
||||
return (
|
||||
<div className="w-full space-y-1">
|
||||
{(label || description) && (
|
||||
<FieldLabel label={label} id={id} description={description} />
|
||||
)}
|
||||
{(label || description) && <FieldLabel label={label} id={id} description={description} />}
|
||||
<InputField ref={ref as never} id={id} {...props} />
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -4,16 +4,14 @@ import { GridCard } from "@components/Card";
|
|||
import { LifeTimeLabel } from "@routes/devices.$id.settings.network";
|
||||
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(
|
||||
"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",
|
||||
className,
|
||||
);
|
||||
|
||||
return <span className={classes}>
|
||||
{flag}
|
||||
</span>
|
||||
return <span className={classes}>{flag}</span>;
|
||||
}
|
||||
|
||||
export default function Ipv6NetworkCard({
|
||||
|
|
@ -33,28 +31,22 @@ export default function Ipv6NetworkCard({
|
|||
<div className="flex flex-col justify-between">
|
||||
<span className="text-sm text-slate-600 dark:text-slate-400">
|
||||
{m.ipv6_link_local()}
|
||||
</span>
|
||||
<span className="text-sm font-medium">
|
||||
{networkState?.ipv6_link_local}
|
||||
</span>
|
||||
|
||||
<span className="text-sm font-medium">{networkState?.ipv6_link_local}</span>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col justify-between">
|
||||
<span className="text-sm text-slate-600 dark:text-slate-400">
|
||||
{m.ipv6_gateway()}
|
||||
</span>
|
||||
<span className="text-sm font-medium">
|
||||
{networkState?.ipv6_gateway}
|
||||
</span>
|
||||
<span className="text-sm text-slate-600 dark:text-slate-400">{m.ipv6_gateway()}</span>
|
||||
|
||||
<span className="text-sm font-medium">{networkState?.ipv6_gateway}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-3 pt-2">
|
||||
{networkState?.ipv6_addresses && networkState?.ipv6_addresses.length > 0 && (
|
||||
<div className="space-y-3">
|
||||
<h4 className="text-sm font-semibold">
|
||||
{m.network_ipv6_addresses_header()}
|
||||
</h4>
|
||||
<h4 className="text-sm font-semibold">{m.network_ipv6_addresses_header()}</h4>
|
||||
{networkState.ipv6_addresses.map(addr => (
|
||||
<div
|
||||
key={addr.address}
|
||||
|
|
@ -64,12 +56,17 @@ export default function Ipv6NetworkCard({
|
|||
<div className="col-span-2 flex flex-col justify-between">
|
||||
<span className="text-sm text-slate-600 dark:text-slate-400">
|
||||
{m.ipv6_address_label()}
|
||||
</span>
|
||||
<span className="text-sm font-medium flex">
|
||||
</span>
|
||||
|
||||
<span className="flex text-sm font-medium">
|
||||
<span className="flex-1">{addr.address}</span>
|
||||
<span className="text-sm font-medium flex gap-x-1">
|
||||
{addr.flag_deprecated ? <FlagLabel flag={m.network_ipv6_flag_deprecated()} /> : null}
|
||||
{addr.flag_dad_failed ? <FlagLabel flag={m.network_ipv6_flag_dad_failed()} /> : null}
|
||||
<span className="flex gap-x-1 text-sm font-medium">
|
||||
{addr.flag_deprecated ? (
|
||||
<FlagLabel flag={m.network_ipv6_flag_deprecated()} />
|
||||
) : null}
|
||||
{addr.flag_dad_failed ? (
|
||||
<FlagLabel flag={m.network_ipv6_flag_dad_failed()} />
|
||||
) : null}
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
|
|
@ -78,7 +75,8 @@ export default function Ipv6NetworkCard({
|
|||
<div className="flex flex-col justify-between">
|
||||
<span className="text-sm text-slate-600 dark:text-slate-400">
|
||||
{m.ipv6_valid_lifetime()}
|
||||
</span>
|
||||
</span>
|
||||
|
||||
<span className="text-sm font-medium">
|
||||
{addr.valid_lifetime === "" ? (
|
||||
<span className="text-slate-400 dark:text-slate-600">
|
||||
|
|
@ -95,7 +93,8 @@ export default function Ipv6NetworkCard({
|
|||
<div className="flex flex-col justify-between">
|
||||
<span className="text-sm text-slate-600 dark:text-slate-400">
|
||||
{m.ipv6_preferred_lifetime()}
|
||||
</span>
|
||||
</span>
|
||||
|
||||
<span className="text-sm font-medium">
|
||||
{addr.preferred_lifetime === "" ? (
|
||||
<span className="text-slate-400 dark:text-slate-600">
|
||||
|
|
|
|||
|
|
@ -72,7 +72,9 @@ export function JigglerSetting({
|
|||
return (
|
||||
<div className="space-y-4">
|
||||
<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">
|
||||
{exampleConfigs.map((example, index) => (
|
||||
<Button
|
||||
|
|
|
|||
|
|
@ -53,12 +53,10 @@ export default function KvmCard({
|
|||
}) {
|
||||
return (
|
||||
<Card>
|
||||
<div className="px-5 py-5 space-y-3">
|
||||
<div className="flex justify-between items-center">
|
||||
<div className="space-y-3 px-5 py-5">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="space-y-1.5">
|
||||
<div className="text-lg font-bold leading-none text-black dark:text-white">
|
||||
{title}
|
||||
</div>
|
||||
<div className="text-lg leading-none font-bold text-black dark:text-white">{title}</div>
|
||||
|
||||
{online ? (
|
||||
<div className="flex items-center gap-x-1.5">
|
||||
|
|
@ -67,7 +65,7 @@ export default function KvmCard({
|
|||
</div>
|
||||
) : (
|
||||
<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">
|
||||
{lastSeen ? (
|
||||
<>{m.last_online({ time: getRelativeTimeString(lastSeen) })}</>
|
||||
|
|
@ -109,14 +107,14 @@ export default function KvmCard({
|
|||
></MenuButton>
|
||||
<MenuItems
|
||||
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">
|
||||
<MenuItem>
|
||||
<div>
|
||||
<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
|
||||
className="block w-full py-1.5 text-black dark:text-white"
|
||||
to={`./${id}/rename`}
|
||||
|
|
@ -130,7 +128,7 @@ export default function KvmCard({
|
|||
<MenuItem>
|
||||
<div>
|
||||
<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
|
||||
className="block w-full py-1.5 text-black dark:text-white"
|
||||
to={`./${id}/deregister`}
|
||||
|
|
|
|||
|
|
@ -1,25 +1,14 @@
|
|||
import clsx from "clsx";
|
||||
|
||||
export default function LoadingSpinner({
|
||||
className,
|
||||
}: {
|
||||
className: string | undefined;
|
||||
}) {
|
||||
export default function LoadingSpinner({ className }: { className: string | undefined }) {
|
||||
return (
|
||||
<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"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<circle
|
||||
className="opacity-25"
|
||||
cx="12"
|
||||
cy="12"
|
||||
r="10"
|
||||
stroke="currentColor"
|
||||
strokeWidth="4"
|
||||
/>
|
||||
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4" />
|
||||
<path
|
||||
// className="opacity-75"
|
||||
fill="currentColor"
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ export default function MacroBar() {
|
|||
}
|
||||
|
||||
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="absolute -ml-5">
|
||||
<LuCommand className="h-4 w-4 text-slate-500" />
|
||||
|
|
|
|||
|
|
@ -8,11 +8,7 @@ import FieldLabel from "@components/FieldLabel";
|
|||
import Fieldset from "@components/Fieldset";
|
||||
import { InputFieldWithLabel, FieldError } from "@components/InputField";
|
||||
import { MacroStepCard } from "@components/MacroStepCard";
|
||||
import {
|
||||
DEFAULT_DELAY,
|
||||
MAX_STEPS_PER_MACRO,
|
||||
MAX_KEYS_PER_STEP,
|
||||
} from "@/constants/macros";
|
||||
import { DEFAULT_DELAY, MAX_STEPS_PER_MACRO, MAX_KEYS_PER_STEP } from "@/constants/macros";
|
||||
import { m } from "@localizations/messages.js";
|
||||
|
||||
interface ValidationErrors {
|
||||
|
|
@ -61,7 +57,7 @@ export function MacroForm({
|
|||
newErrors.name = m.macro_name_too_long();
|
||||
}
|
||||
|
||||
const steps = (macro.steps || []);
|
||||
const steps = macro.steps || [];
|
||||
|
||||
if (steps.length) {
|
||||
const hasKeyOrModifier = steps.some(
|
||||
|
|
@ -91,7 +87,9 @@ export function MacroForm({
|
|||
await onSubmit(macro);
|
||||
} catch (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 {
|
||||
showTemporaryError(m.macro_save_failed());
|
||||
}
|
||||
|
|
@ -196,13 +194,13 @@ export function MacroForm({
|
|||
<div>
|
||||
<div className="flex items-center justify-between text-sm">
|
||||
<div className="flex items-center gap-1">
|
||||
<FieldLabel
|
||||
label={m.macro_steps_label()}
|
||||
description={m.macro_steps_description()}
|
||||
/>
|
||||
<FieldLabel label={m.macro_steps_label()} description={m.macro_steps_description()} />
|
||||
</div>
|
||||
<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>
|
||||
</div>
|
||||
{errors.steps?.[0]?.keys && (
|
||||
|
|
@ -231,9 +229,7 @@ export function MacroForm({
|
|||
onKeySelect={option => handleKeySelect(stepIndex, option)}
|
||||
onKeyQueryChange={query => handleKeyQueryChange(stepIndex, query)}
|
||||
keyQuery={keyQueries[stepIndex] || ""}
|
||||
onModifierChange={modifiers =>
|
||||
handleModifierChange(stepIndex, modifiers)
|
||||
}
|
||||
onModifierChange={modifiers => handleModifierChange(stepIndex, modifiers)}
|
||||
onDelayChange={delay => handleDelayChange(stepIndex, delay)}
|
||||
isLastStep={stepIndex === (macro.steps?.length || 0) - 1}
|
||||
keyboard={selectedKeyboard}
|
||||
|
|
@ -248,7 +244,11 @@ export function MacroForm({
|
|||
theme="light"
|
||||
fullWidth
|
||||
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={() => {
|
||||
if (isMaxStepsReached) {
|
||||
showTemporaryError(m.macro_max_steps_error({ max: MAX_STEPS_PER_MACRO }));
|
||||
|
|
@ -257,10 +257,7 @@ export function MacroForm({
|
|||
|
||||
setMacro(prev => ({
|
||||
...prev,
|
||||
steps: [
|
||||
...(prev.steps || []),
|
||||
{ keys: [], modifiers: [], delay: DEFAULT_DELAY },
|
||||
],
|
||||
steps: [...(prev.steps || []), { keys: [], modifiers: [], delay: DEFAULT_DELAY }],
|
||||
}));
|
||||
setErrors({});
|
||||
}}
|
||||
|
|
|
|||
|
|
@ -72,7 +72,7 @@ const ensureArray = <T,>(arr: T[] | null | undefined): T[] => {
|
|||
};
|
||||
|
||||
const keyDisplay = (keyDisplayMap: Record<string, string>, key: string): string => {
|
||||
return keyDisplayMap[key] || key
|
||||
return keyDisplayMap[key] || key;
|
||||
};
|
||||
|
||||
export function MacroStepCard({
|
||||
|
|
@ -113,9 +113,7 @@ export function MacroStepCard({
|
|||
|
||||
const filteredKeys = useMemo(() => {
|
||||
const selectedKeys = ensureArray(step.keys);
|
||||
const availableKeys = keyOptions.filter(
|
||||
option => !selectedKeys.includes(option.value),
|
||||
);
|
||||
const availableKeys = keyOptions.filter(option => !selectedKeys.includes(option.value));
|
||||
|
||||
if (keyQuery === "") {
|
||||
return availableKeys;
|
||||
|
|
@ -186,9 +184,7 @@ export function MacroStepCard({
|
|||
key={option.value}
|
||||
size="XS"
|
||||
theme={
|
||||
ensureArray(step.modifiers).includes(option.value)
|
||||
? "primary"
|
||||
: "light"
|
||||
ensureArray(step.modifiers).includes(option.value) ? "primary" : "light"
|
||||
}
|
||||
text={option.label.split(" ")[1] || option.label}
|
||||
onClick={() => handleModifierToggle(option.value)}
|
||||
|
|
@ -221,9 +217,7 @@ export function MacroStepCard({
|
|||
className=""
|
||||
theme="blank"
|
||||
onClick={() => {
|
||||
const newKeys = step.keys.filter(
|
||||
(_, i) => i !== keyIndex,
|
||||
);
|
||||
const newKeys = step.keys.filter((_, i) => i !== keyIndex);
|
||||
onKeySelect({ value: null, keys: newKeys });
|
||||
}}
|
||||
LeadingIcon={LuX}
|
||||
|
|
|
|||
|
|
@ -35,10 +35,7 @@ interface MetricProps<T, K extends keyof T> {
|
|||
* @param metrics - Expected to be ordered from oldest to newest.
|
||||
* @param metricName - Name of the metric to create a chart array for.
|
||||
*/
|
||||
export function createChartArray<T, K extends keyof T>(
|
||||
metrics: Map<number, T>,
|
||||
metricName: K,
|
||||
) {
|
||||
export function createChartArray<T, K extends keyof T>(metrics: Map<number, T>, metricName: K) {
|
||||
const result: { date: number; metric: number | null }[] = [];
|
||||
const iter = metrics.entries();
|
||||
let next = iter.next() as IteratorResult<[number, T]>;
|
||||
|
|
@ -146,12 +143,7 @@ export function Metric<T, K extends keyof T>({
|
|||
|
||||
return (
|
||||
<div className="space-y-2">
|
||||
<MetricHeader
|
||||
title={title}
|
||||
description={description}
|
||||
badge={badge}
|
||||
badgeTheme={badgeTheme}
|
||||
/>
|
||||
<MetricHeader title={title} description={description} badge={badge} badgeTheme={badgeTheme} />
|
||||
|
||||
<GridCard>
|
||||
<div
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import {
|
|||
YAxis,
|
||||
} from "recharts";
|
||||
|
||||
import { getLocale } from '@localizations/runtime.js';
|
||||
import { getLocale } from "@localizations/runtime.js";
|
||||
import CustomTooltip, { CustomTooltipProps } from "@components/CustomTooltip";
|
||||
|
||||
export default function MetricsChart({
|
||||
|
|
|
|||
|
|
@ -18,27 +18,25 @@ const Modal = React.memo(function Modal({
|
|||
<Dialog open={open} onClose={onClose} className="relative z-20">
|
||||
<DialogBackdrop
|
||||
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={{
|
||||
scrollbarGutter: 'stable'
|
||||
}}>
|
||||
<div
|
||||
className="fixed inset-0 z-20 w-screen overflow-y-auto"
|
||||
style={{ scrollbarGutter: "stable" }}
|
||||
>
|
||||
{/* TODO: This doesn't work well with other-sessions */}
|
||||
<div className="flex min-h-full items-end justify-center p-4 text-center md:items-baseline md:p-4">
|
||||
<DialogPanel
|
||||
transition
|
||||
className={cx(
|
||||
"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,
|
||||
)}
|
||||
>
|
||||
<div className="pointer-events-auto inline-block w-full text-left">
|
||||
<div className="flex justify-center" onClick={onClose}>
|
||||
<div
|
||||
className="pointer-events-none w-full"
|
||||
onClick={e => e.stopPropagation()}
|
||||
>
|
||||
<div className="pointer-events-none w-full" onClick={e => e.stopPropagation()}>
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ export function NestedSettingsGroup(props: NestedSettingsGroupProps) {
|
|||
return (
|
||||
<div
|
||||
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,
|
||||
)}
|
||||
>
|
||||
|
|
@ -19,4 +19,3 @@ export function NestedSettingsGroup(props: NestedSettingsGroupProps) {
|
|||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ import { JsonRpcResponse, useJsonRpc } from "@hooks/useJsonRpc";
|
|||
import notifications from "@/notifications";
|
||||
import { formatters } from "@/utils";
|
||||
|
||||
|
||||
const TimeAgoLabel = ({ date }: { date: Date }) => {
|
||||
const [timeAgo, setTimeAgo] = useState<string | undefined>(formatters.timeAgo(date));
|
||||
useEffect(() => {
|
||||
|
|
@ -19,11 +18,7 @@ const TimeAgoLabel = ({ date }: { date: Date }) => {
|
|||
return () => clearInterval(interval);
|
||||
}, [date]);
|
||||
|
||||
return (
|
||||
<span className="text-sm text-slate-600 dark:text-slate-400 select-none">
|
||||
{timeAgo}
|
||||
</span>
|
||||
);
|
||||
return <span className="text-sm text-slate-600 select-none dark:text-slate-400">{timeAgo}</span>;
|
||||
};
|
||||
|
||||
export default function PublicIPCard() {
|
||||
|
|
@ -34,19 +29,23 @@ export default function PublicIPCard() {
|
|||
send("getPublicIPAddresses", { refresh: true }, (resp: JsonRpcResponse) => {
|
||||
setPublicIPs([]);
|
||||
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;
|
||||
}
|
||||
const publicIPs = resp.result as PublicIP[];
|
||||
// sort the public IPs by IP address
|
||||
// IPv6 addresses are sorted after IPv4 addresses
|
||||
setPublicIPs(publicIPs.sort(({ ip: aIp }, { ip: bIp }) => {
|
||||
setPublicIPs(
|
||||
publicIPs.sort(({ ip: aIp }, { ip: bIp }) => {
|
||||
const aIsIPv6 = aIp.includes(":");
|
||||
const bIsIPv6 = bIp.includes(":");
|
||||
if (aIsIPv6 && !bIsIPv6) return 1;
|
||||
if (!aIsIPv6 && bIsIPv6) return -1;
|
||||
return aIp.localeCompare(bIp);
|
||||
}));
|
||||
}),
|
||||
);
|
||||
});
|
||||
}, [send, setPublicIPs]);
|
||||
|
||||
|
|
@ -89,10 +88,11 @@ export default function PublicIPCard() {
|
|||
<div className="flex gap-x-6 gap-y-2">
|
||||
<div className="flex-1 space-y-2">
|
||||
{publicIPs?.map(ip => (
|
||||
<div key={ip.ip} className="flex justify-between border-slate-800/10 pt-2 dark:border-slate-300/20">
|
||||
<span className="text-sm font-medium">
|
||||
{ip.ip}
|
||||
</span>
|
||||
<div
|
||||
key={ip.ip}
|
||||
className="flex justify-between border-slate-800/10 pt-2 dark:border-slate-300/20"
|
||||
>
|
||||
<span className="text-sm font-medium">{ip.ip}</span>
|
||||
{ip.last_updated && <TimeAgoLabel date={new Date(ip.last_updated)} />}
|
||||
</div>
|
||||
))}
|
||||
|
|
|
|||
|
|
@ -82,10 +82,10 @@ export const SelectMenuBasic = React.forwardRef<HTMLSelectElement, SelectMenuPro
|
|||
"invalid:ring-2 invalid:ring-red-600 invalid:ring-offset-2",
|
||||
|
||||
// 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: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}
|
||||
id={id}
|
||||
|
|
|
|||
|
|
@ -15,17 +15,14 @@ export function SettingsItem(props: SettingsItemProps) {
|
|||
|
||||
return (
|
||||
<label
|
||||
className={cx(
|
||||
"flex select-none items-center justify-between gap-x-8 rounded",
|
||||
className,
|
||||
)}
|
||||
className={cx("flex items-center justify-between gap-x-8 rounded select-none", className)}
|
||||
>
|
||||
<div className="space-y-0.5">
|
||||
<div className="flex items-center gap-x-2">
|
||||
<div className="flex items-center text-base font-semibold text-black dark:text-white">
|
||||
{title}
|
||||
{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}
|
||||
</span>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,4 @@
|
|||
export default function SettingsNestedSection({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
export default function SettingsNestedSection({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<div className="ml-2 border-l border-slate-800/30 pl-4 dark:border-slate-300/30">
|
||||
{children}
|
||||
|
|
|
|||
|
|
@ -11,9 +11,9 @@ export default function SidebarHeader({
|
|||
setSidebarView: (view: AvailableSidebarViews | null) => void;
|
||||
}) {
|
||||
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 }}>
|
||||
<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>
|
||||
<Button
|
||||
size="XS"
|
||||
|
|
|
|||
|
|
@ -5,16 +5,19 @@ import LogoBlueIcon from "@assets/logo-blue.png";
|
|||
import LogoWhiteIcon from "@assets/logo-white.svg";
|
||||
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) {
|
||||
return (
|
||||
<div>
|
||||
<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">
|
||||
<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" />
|
||||
</Link>
|
||||
<div>{actionElement}</div>
|
||||
|
|
|
|||
|
|
@ -38,12 +38,13 @@ export default function StaticIpv4Card() {
|
|||
}, [ipv4StaticAddress, setValue]);
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
|
|
@ -55,27 +56,35 @@ export default function StaticIpv4Card() {
|
|||
{m.network_static_ipv4_header()}
|
||||
</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
|
||||
label={m.network_ipv4_address()}
|
||||
type="text"
|
||||
size="SM"
|
||||
placeholder="192.168.1.100"
|
||||
{
|
||||
...register("ipv4_static.address", {
|
||||
validate: (value: string | undefined) => validateIsIPOrCIDR4(value ?? "")
|
||||
{...register("ipv4_static.address", {
|
||||
validate: (value: string | undefined) => validateIsIPOrCIDR4(value ?? ""),
|
||||
})}
|
||||
error={formState.errors.ipv4_static?.address?.message}
|
||||
/>
|
||||
|
||||
{!hideSubnetMask && <InputFieldWithLabel
|
||||
{!hideSubnetMask && (
|
||||
<InputFieldWithLabel
|
||||
label={m.network_ipv4_netmask()}
|
||||
type="text"
|
||||
size="SM"
|
||||
placeholder="255.255.255.0"
|
||||
{...register("ipv4_static.netmask", { validate: (value: string | undefined) => ipv4Validation(value ?? "") })}
|
||||
{...register("ipv4_static.netmask", {
|
||||
validate: (value: string | undefined) => ipv4Validation(value ?? ""),
|
||||
})}
|
||||
error={formState.errors.ipv4_static?.netmask?.message}
|
||||
/>}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<InputFieldWithLabel
|
||||
|
|
@ -83,7 +92,9 @@ export default function StaticIpv4Card() {
|
|||
type="text"
|
||||
size="SM"
|
||||
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}
|
||||
/>
|
||||
|
||||
|
|
@ -99,10 +110,9 @@ export default function StaticIpv4Card() {
|
|||
type="text"
|
||||
size="SM"
|
||||
placeholder="1.1.1.1"
|
||||
{...register(
|
||||
`ipv4_static.dns.${index}`,
|
||||
{ validate: (value: string | undefined) => ipv4Validation(value ?? "") }
|
||||
)}
|
||||
{...register(`ipv4_static.dns.${index}`, {
|
||||
validate: (value: string | undefined) => ipv4Validation(value ?? ""),
|
||||
})}
|
||||
error={formState.errors.ipv4_static?.dns?.[index]?.message}
|
||||
/>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ export default function StaticIpv6Card() {
|
|||
};
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
|
|
@ -56,7 +56,9 @@ export default function StaticIpv6Card() {
|
|||
type="text"
|
||||
size="SM"
|
||||
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}
|
||||
/>
|
||||
|
||||
|
|
@ -65,7 +67,9 @@ export default function StaticIpv6Card() {
|
|||
type="text"
|
||||
size="SM"
|
||||
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}
|
||||
/>
|
||||
|
||||
|
|
@ -81,7 +85,9 @@ export default function StaticIpv6Card() {
|
|||
type="text"
|
||||
size="SM"
|
||||
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}
|
||||
/>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ export default function StatusCard({
|
|||
statusIndicatorClassName,
|
||||
}: Props) {
|
||||
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 ? (
|
||||
<span>
|
||||
<Icon className={cx(iconClassName, "shrink-0")} />
|
||||
|
|
@ -26,16 +26,11 @@ export default function StatusCard({
|
|||
) : null}
|
||||
|
||||
<div className="space-y-1">
|
||||
<div className="text-xs font-semibold leading-none transition text-ellipsis">
|
||||
{title}
|
||||
</div>
|
||||
<div className="text-xs leading-none font-semibold text-ellipsis transition">{title}</div>
|
||||
<div className="text-xs leading-none">
|
||||
<div className="flex items-center gap-x-1">
|
||||
<div
|
||||
className={cx(
|
||||
"h-2 w-2 rounded-full border transition",
|
||||
statusIndicatorClassName,
|
||||
)}
|
||||
className={cx("h-2 w-2 rounded-full border transition", statusIndicatorClassName)}
|
||||
/>
|
||||
<span className={cx("transition")}>{status}</span>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ const variants = cva({
|
|||
export default function StepCounter({ nSteps, currStepIdx, size = "MD" }: Props) {
|
||||
const textStyle = variants({ size });
|
||||
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 => {
|
||||
if (i < currStepIdx) {
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ const TERMINAL_CONFIG = {
|
|||
// Add these configurations:
|
||||
cursorStyle: "block",
|
||||
rendererType: "canvas", // Ensure we're using the canvas renderer
|
||||
unicode: { activeVersion: "11" }
|
||||
unicode: { activeVersion: "11" },
|
||||
} as const;
|
||||
|
||||
function Terminal({
|
||||
|
|
@ -162,10 +162,7 @@ function Terminal({
|
|||
}, [instance]);
|
||||
|
||||
return (
|
||||
<div
|
||||
onKeyDown={e => e.stopPropagation()}
|
||||
onKeyUp={e => e.stopPropagation()}
|
||||
>
|
||||
<div onKeyDown={e => e.stopPropagation()} onKeyUp={e => e.stopPropagation()}>
|
||||
<div>
|
||||
<div
|
||||
className={cx(
|
||||
|
|
@ -184,7 +181,7 @@ function Terminal({
|
|||
>
|
||||
<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">
|
||||
<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}
|
||||
</h2>
|
||||
<div className="absolute right-2">
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ const TextArea = React.forwardRef<HTMLTextAreaElement, TextAreaProps>(
|
|||
className={cx(
|
||||
"relative w-full",
|
||||
"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
|
||||
|
|
@ -25,7 +25,7 @@ const TextArea = React.forwardRef<HTMLTextAreaElement, TextAreaProps>(
|
|||
{...props}
|
||||
id="asd"
|
||||
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,
|
||||
)}
|
||||
/>
|
||||
|
|
@ -41,10 +41,8 @@ type TextAreaWithLabelProps = {
|
|||
error?: string | null;
|
||||
} & React.ComponentProps<typeof TextArea>;
|
||||
|
||||
export const TextAreaWithLabel = React.forwardRef<
|
||||
HTMLTextAreaElement,
|
||||
TextAreaWithLabelProps
|
||||
>(function TextAreaWithLabel({ label, error, id, description, ...props }, ref) {
|
||||
export const TextAreaWithLabel = React.forwardRef<HTMLTextAreaElement, TextAreaWithLabelProps>(
|
||||
function TextAreaWithLabel({ label, error, id, description, ...props }, ref) {
|
||||
return (
|
||||
<div className="space-y-1">
|
||||
<FieldLabel label={label} id={id} description={description} />
|
||||
|
|
@ -52,6 +50,7 @@ export const TextAreaWithLabel = React.forwardRef<
|
|||
{error && <FieldError error={error} />}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
export default TextArea;
|
||||
|
|
|
|||
|
|
@ -64,7 +64,6 @@ export default function USBStateStatus({
|
|||
state: USBStates;
|
||||
peerConnectionState?: RTCPeerConnectionState | null;
|
||||
}) {
|
||||
|
||||
const props = StatusCardProps[state];
|
||||
if (!props) {
|
||||
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 (peerConnectionState !== "connected") {
|
||||
const {
|
||||
icon: Icon,
|
||||
iconClassName,
|
||||
statusIndicatorClassName,
|
||||
} = StatusCardProps["not attached"];
|
||||
const { icon: Icon, iconClassName, statusIndicatorClassName } = StatusCardProps["not attached"];
|
||||
|
||||
return (
|
||||
<StatusCard
|
||||
|
|
@ -90,7 +85,5 @@ export default function USBStateStatus({
|
|||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<StatusCard title={m.usb()} status={USBStateMap[state]} {...StatusCardProps[state]} />
|
||||
);
|
||||
return <StatusCard title={m.usb()} status={USBStateMap[state]} {...StatusCardProps[state]} />;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,20 +9,18 @@ export default function UpdateInProgressStatusCard() {
|
|||
const { navigateTo } = useDeviceUiNavigation();
|
||||
|
||||
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!">
|
||||
<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">
|
||||
<LoadingSpinner className={cx("h-5 w-5", "shrink-0 text-blue-700")} />
|
||||
<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()}
|
||||
</div>
|
||||
<div className="text-sm leading-none">
|
||||
<div className="flex items-center gap-x-1">
|
||||
<span className={cx("transition")}>
|
||||
{m.updating_leave_device_on()}
|
||||
</span>
|
||||
<span className={cx("transition")}>{m.updating_leave_device_on()}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
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
|
||||
|
|
@ -11,13 +10,7 @@ export interface UpdatePart {
|
|||
complete: boolean;
|
||||
}
|
||||
|
||||
export default function UpdatingStatusCard({
|
||||
label,
|
||||
part,
|
||||
}: {
|
||||
label: string;
|
||||
part: UpdatePart;
|
||||
}) {
|
||||
export default function UpdatingStatusCard({ label, part }: { label: string; part: UpdatePart }) {
|
||||
return (
|
||||
<div className="space-y-2">
|
||||
<div className="flex items-center justify-between">
|
||||
|
|
|
|||
|
|
@ -64,8 +64,7 @@ export function UsbDeviceSetting() {
|
|||
const { send } = useJsonRpc();
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
const [usbDeviceConfig, setUsbDeviceConfig] =
|
||||
useState<UsbDeviceConfig>(defaultUsbDeviceConfig);
|
||||
const [usbDeviceConfig, setUsbDeviceConfig] = useState<UsbDeviceConfig>(defaultUsbDeviceConfig);
|
||||
const [selectedPreset, setSelectedPreset] = useState<string>("default");
|
||||
|
||||
const syncUsbDeviceConfig = useCallback(() => {
|
||||
|
|
@ -134,9 +133,7 @@ export function UsbDeviceSetting() {
|
|||
setSelectedPreset(newPreset);
|
||||
|
||||
if (newPreset !== "custom") {
|
||||
const presetConfig = usbPresets.find(
|
||||
preset => preset.value === newPreset,
|
||||
)?.config;
|
||||
const presetConfig = usbPresets.find(preset => preset.value === newPreset)?.config;
|
||||
|
||||
if (presetConfig) {
|
||||
handleUsbConfigChange(presetConfig);
|
||||
|
|
@ -179,7 +176,10 @@ export function UsbDeviceSetting() {
|
|||
<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">
|
||||
<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
|
||||
checked={usbDeviceConfig.keyboard}
|
||||
onChange={onUsbConfigItemChange("keyboard")}
|
||||
|
|
|
|||
|
|
@ -31,7 +31,6 @@ export interface USBConfig {
|
|||
product: string;
|
||||
}
|
||||
|
||||
|
||||
const usbConfigs = [
|
||||
{
|
||||
label: m.usb_config_default(),
|
||||
|
|
@ -127,7 +126,10 @@ export function UsbInfoSetting() {
|
|||
await sleep(2000);
|
||||
setLoading(false);
|
||||
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();
|
||||
|
|
@ -178,9 +180,7 @@ export function UsbInfoSetting() {
|
|||
<USBConfigDialog
|
||||
loading={loading}
|
||||
onSetUsbConfig={usbConfig => handleUsbConfigChange(usbConfig)}
|
||||
onRestoreToDefault={() =>
|
||||
handleUsbConfigChange(usbConfigData[usbConfigs[0].value])
|
||||
}
|
||||
onRestoreToDefault={() => handleUsbConfigChange(usbConfigData[usbConfigs[0].value])}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@ import LogoWhite from "@/assets/logo-white.svg";
|
|||
import { isOnDevice } from "@/main";
|
||||
import { sleep } from "@/utils";
|
||||
|
||||
|
||||
interface OverlayContentProps {
|
||||
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">
|
||||
<LoadingSpinner className="h-8 w-8 text-blue-800 dark:text-blue-200" />
|
||||
</div>
|
||||
<p className="text-center text-sm text-slate-700 dark:text-slate-300">
|
||||
{text}
|
||||
</p>
|
||||
<p className="text-center text-sm text-slate-700 dark:text-slate-300">{text}</p>
|
||||
</div>
|
||||
</OverlayContent>
|
||||
</motion.div>
|
||||
|
|
@ -125,7 +122,9 @@ export function ConnectionFailedOverlay({
|
|||
<div className="text-left text-sm text-slate-700 dark:text-slate-300">
|
||||
<div className="space-y-4">
|
||||
<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">
|
||||
<li>{m.video_overlay_conn_verify_power()}</li>
|
||||
<li>{m.video_overlay_conn_check_cables()}</li>
|
||||
|
|
@ -163,9 +162,7 @@ interface PeerConnectionDisconnectedOverlay {
|
|||
readonly show: boolean;
|
||||
}
|
||||
|
||||
export function PeerConnectionDisconnectedOverlay({
|
||||
show,
|
||||
}: PeerConnectionDisconnectedOverlay) {
|
||||
export function PeerConnectionDisconnectedOverlay({ show }: PeerConnectionDisconnectedOverlay) {
|
||||
return (
|
||||
<AnimatePresence>
|
||||
{show && (
|
||||
|
|
@ -185,7 +182,9 @@ export function PeerConnectionDisconnectedOverlay({
|
|||
<div className="text-left text-sm text-slate-700 dark:text-slate-300">
|
||||
<div className="space-y-4">
|
||||
<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">
|
||||
<li>{m.video_overlay_conn_verify_power()}</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)
|
||||
const [hasSeenDisconnect, setHasSeenDisconnect] = useState(
|
||||
['disconnected', 'closed', 'failed'].includes(peerConnectionState ?? '')
|
||||
["disconnected", "closed", "failed"].includes(peerConnectionState ?? ""),
|
||||
);
|
||||
|
||||
// Track if we've timed out
|
||||
|
|
@ -416,8 +415,8 @@ export function RebootingOverlay({ show, postRebootAction }: RebootingOverlayPro
|
|||
if (!show) return;
|
||||
if (hasSeenDisconnect) return;
|
||||
|
||||
if (['disconnected', 'closed', 'failed'].includes(peerConnectionState ?? '')) {
|
||||
console.log('hasSeenDisconnect', hasSeenDisconnect);
|
||||
if (["disconnected", "closed", "failed"].includes(peerConnectionState ?? "")) {
|
||||
console.log("hasSeenDisconnect", hasSeenDisconnect);
|
||||
setHasSeenDisconnect(true);
|
||||
}
|
||||
}, [show, peerConnectionState, hasSeenDisconnect]);
|
||||
|
|
@ -438,7 +437,6 @@ export function RebootingOverlay({ show, postRebootAction }: RebootingOverlayPro
|
|||
};
|
||||
}, [show]);
|
||||
|
||||
|
||||
// Poll suggested IP in device mode to detect when it's available
|
||||
const abortControllerRef = useRef<AbortController | null>(null);
|
||||
const isFetchingRef = useRef(false);
|
||||
|
|
@ -465,17 +463,16 @@ export function RebootingOverlay({ show, postRebootAction }: RebootingOverlayPro
|
|||
abortControllerRef.current = abortController;
|
||||
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);
|
||||
try {
|
||||
const response = await fetch(
|
||||
postRebootAction.healthCheck,
|
||||
{ signal: abortController.signal, }
|
||||
);
|
||||
const response = await fetch(postRebootAction.healthCheck, {
|
||||
signal: abortController.signal,
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
// 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:
|
||||
// - Absolute paths: resolved against current origin
|
||||
|
|
@ -492,8 +489,8 @@ export function RebootingOverlay({ show, postRebootAction }: RebootingOverlayPro
|
|||
} catch (err) {
|
||||
// Ignore errors - they're expected while device is rebooting
|
||||
// Only log if it's not an abort error
|
||||
if (err instanceof Error && err.name !== 'AbortError') {
|
||||
console.debug('Error checking post-reboot health:', err);
|
||||
if (err instanceof Error && err.name !== "AbortError") {
|
||||
console.debug("Error checking post-reboot health:", err);
|
||||
}
|
||||
} finally {
|
||||
clearTimeout(timeoutId);
|
||||
|
|
@ -531,8 +528,7 @@ export function RebootingOverlay({ show, postRebootAction }: RebootingOverlayPro
|
|||
}}
|
||||
>
|
||||
<OverlayContent>
|
||||
|
||||
<div className="flex flex-col items-start gap-y-4 w-full max-w-md">
|
||||
<div className="flex w-full max-w-md flex-col items-start gap-y-4">
|
||||
<div className="h-[24px]">
|
||||
<img src={LogoBlue} alt="" className="h-full dark:hidden" />
|
||||
<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="space-y-4">
|
||||
<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">
|
||||
{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>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -21,8 +21,7 @@ export const DetachIcon = ({ className }: { className?: string }) => {
|
|||
|
||||
function KeyboardWrapper() {
|
||||
const keyboardRef = useRef<HTMLDivElement>(null);
|
||||
const { isAttachedVirtualKeyboardVisible, setAttachedVirtualKeyboardVisibility } =
|
||||
useUiStore();
|
||||
const { isAttachedVirtualKeyboardVisible, setAttachedVirtualKeyboardVisibility } = useUiStore();
|
||||
const { keyboardLedState, keysDownState, isVirtualKeyboardEnabled, setVirtualKeyboardEnabled } =
|
||||
useHidStore();
|
||||
const { handleKeyPress, executeMacro } = useKeyboard();
|
||||
|
|
@ -159,9 +158,7 @@ function KeyboardWrapper() {
|
|||
}
|
||||
|
||||
if (key === "AltMetaEscape") {
|
||||
await executeMacro([
|
||||
{ keys: ["Escape"], modifiers: ["AltLeft", "MetaLeft"], delay: 100 },
|
||||
]);
|
||||
await executeMacro([{ keys: ["Escape"], modifiers: ["AltLeft", "MetaLeft"], delay: 100 }]);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -258,7 +255,7 @@ function KeyboardWrapper() {
|
|||
{m.virtual_keyboard_header()}
|
||||
</h2>
|
||||
<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
|
||||
size="XS"
|
||||
to="settings/keyboard"
|
||||
|
|
|
|||
|
|
@ -4,11 +4,7 @@ import { useResizeObserver } from "usehooks-ts";
|
|||
import { cx } from "@/cva.config";
|
||||
import useKeyboard from "@hooks/useKeyboard";
|
||||
import useMouse from "@hooks/useMouse";
|
||||
import {
|
||||
useRTCStore,
|
||||
useSettingsStore,
|
||||
useVideoStore,
|
||||
} from "@hooks/stores";
|
||||
import { useRTCStore, useSettingsStore, useVideoStore } from "@hooks/stores";
|
||||
import VirtualKeyboard from "@components/VirtualKeyboard";
|
||||
import Actionbar from "@components/ActionBar";
|
||||
import MacroBar from "@components/MacroBar";
|
||||
|
|
@ -32,7 +28,8 @@ export default function WebRTCVideo({ hasConnectionIssues }: { hasConnectionIssu
|
|||
const [isPointerLockActive, setIsPointerLockActive] = 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
|
||||
const settings = useSettingsStore();
|
||||
|
|
@ -71,7 +68,7 @@ export default function WebRTCVideo({ hasConnectionIssues }: { hasConnectionIssu
|
|||
setVideoClientSize(width || 0, height || 0);
|
||||
setVideoSize(videoElm.current.videoWidth, videoElm.current.videoHeight);
|
||||
},
|
||||
[setVideoClientSize, setVideoSize]
|
||||
[setVideoClientSize, setVideoSize],
|
||||
);
|
||||
|
||||
useResizeObserver({
|
||||
|
|
@ -119,9 +116,7 @@ export default function WebRTCVideo({ hasConnectionIssues }: { hasConnectionIssu
|
|||
}, []);
|
||||
|
||||
const requestPointerLock = useCallback(async () => {
|
||||
if (!isPointerLockPossible
|
||||
|| videoElm.current === null
|
||||
|| document.pointerLockElement) return;
|
||||
if (!isPointerLockPossible || videoElm.current === null || document.pointerLockElement) return;
|
||||
|
||||
const isPointerLockGranted = await checkNavigatorPermissions("pointer-lock");
|
||||
|
||||
|
|
@ -151,7 +146,11 @@ export default function WebRTCVideo({ hasConnectionIssues }: { hasConnectionIssu
|
|||
}, [checkNavigatorPermissions, setIsKeyboardLockActive]);
|
||||
|
||||
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) {
|
||||
try {
|
||||
|
|
@ -216,7 +215,8 @@ export default function WebRTCVideo({ hasConnectionIssues }: { hasConnectionIssu
|
|||
}, [releaseKeyboardLock]);
|
||||
|
||||
const absMouseMoveHandler = useMemo(
|
||||
() => getAbsMouseMoveHandler({
|
||||
() =>
|
||||
getAbsMouseMoveHandler({
|
||||
videoClientWidth,
|
||||
videoClientHeight,
|
||||
videoWidth,
|
||||
|
|
@ -225,15 +225,9 @@ export default function WebRTCVideo({ hasConnectionIssues }: { hasConnectionIssu
|
|||
[getAbsMouseMoveHandler, videoClientWidth, videoClientHeight, videoWidth, videoHeight],
|
||||
);
|
||||
|
||||
const relMouseMoveHandler = useMemo(
|
||||
() => getRelMouseMoveHandler(),
|
||||
[getRelMouseMoveHandler],
|
||||
);
|
||||
const relMouseMoveHandler = useMemo(() => getRelMouseMoveHandler(), [getRelMouseMoveHandler]);
|
||||
|
||||
const mouseWheelHandler = useMemo(
|
||||
() => getMouseWheelHandler(),
|
||||
[getMouseWheelHandler],
|
||||
);
|
||||
const mouseWheelHandler = useMemo(() => getMouseWheelHandler(), [getMouseWheelHandler]);
|
||||
|
||||
function getAdjustedKeyCode(e: KeyboardEvent) {
|
||||
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
|
||||
// https://bugs.chromium.org/p/chromium/issues/detail?id=28089
|
||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=1299553
|
||||
if (e.metaKey && hidKey < 0xE0) {
|
||||
if (e.metaKey && hidKey < 0xe0) {
|
||||
setTimeout(() => {
|
||||
console.debug(`Forcing the meta key release of associated key: ${hidKey}`);
|
||||
handleKeyPress(hidKey, false);
|
||||
|
|
@ -403,7 +397,7 @@ export default function WebRTCVideo({ hasConnectionIssues }: { hasConnectionIssu
|
|||
const videoElmRefValue = videoElm.current;
|
||||
if (!videoElmRefValue) return;
|
||||
|
||||
const isRelativeMouseMode = (settings.mouseMode === "relative");
|
||||
const isRelativeMouseMode = settings.mouseMode === "relative";
|
||||
const mouseHandler = isRelativeMouseMode ? relMouseMoveHandler : absMouseMoveHandler;
|
||||
|
||||
const abortController = new AbortController();
|
||||
|
|
@ -418,7 +412,8 @@ export default function WebRTCVideo({ hasConnectionIssues }: { hasConnectionIssu
|
|||
});
|
||||
|
||||
if (isRelativeMouseMode) {
|
||||
videoElmRefValue.addEventListener("click",
|
||||
videoElmRefValue.addEventListener(
|
||||
"click",
|
||||
() => {
|
||||
if (isPointerLockPossible && !isPointerLockActive && !document.pointerLockElement) {
|
||||
requestPointerLock();
|
||||
|
|
@ -469,7 +464,15 @@ export default function WebRTCVideo({ hasConnectionIssues }: { hasConnectionIssu
|
|||
if (!isPlaying) return false;
|
||||
if (videoHeight === 0 || videoWidth === 0) return false;
|
||||
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
|
||||
const videoStyle = useMemo(() => {
|
||||
|
|
@ -481,15 +484,11 @@ export default function WebRTCVideo({ hasConnectionIssues }: { hasConnectionIssu
|
|||
};
|
||||
}, [videoSaturation, videoBrightness, videoContrast]);
|
||||
|
||||
|
||||
return (
|
||||
<div className="grid h-full w-full grid-rows-(--grid-layout)">
|
||||
<div className="flex min-h-[39.5px] flex-col">
|
||||
<div className="flex flex-col">
|
||||
<fieldset
|
||||
disabled={peerConnection?.connectionState !== "connected"}
|
||||
className="contents"
|
||||
>
|
||||
<fieldset disabled={peerConnection?.connectionState !== "connected"} className="contents">
|
||||
<Actionbar requestFullscreen={requestFullscreen} />
|
||||
<MacroBar />
|
||||
</fieldset>
|
||||
|
|
@ -546,7 +545,7 @@ export default function WebRTCVideo({ hasConnectionIssues }: { hasConnectionIssu
|
|||
{peerConnection?.connectionState == "connected" && !hasConnectionIssues && (
|
||||
<div
|
||||
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">
|
||||
<LoadingVideoOverlay show={isVideoLoading} />
|
||||
|
|
|
|||
|
|
@ -18,9 +18,9 @@ interface ATXState {
|
|||
|
||||
export function ATXPowerControl() {
|
||||
const [isPowerPressed, setIsPowerPressed] = useState(false);
|
||||
const [powerPressTimer, setPowerPressTimer] = useState<ReturnType<
|
||||
typeof setTimeout
|
||||
> | null>(null);
|
||||
const [powerPressTimer, setPowerPressTimer] = useState<ReturnType<typeof setTimeout> | null>(
|
||||
null,
|
||||
);
|
||||
const [atxState, setAtxState] = useState<ATXState | null>(null);
|
||||
|
||||
const { send } = useJsonRpc(function onRequest(resp) {
|
||||
|
|
@ -33,7 +33,9 @@ export function ATXPowerControl() {
|
|||
useEffect(() => {
|
||||
send("getATXState", {}, (resp: JsonRpcResponse) => {
|
||||
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;
|
||||
}
|
||||
setAtxState(resp.result as ATXState);
|
||||
|
|
@ -54,7 +56,12 @@ export function ATXPowerControl() {
|
|||
console.log("Sending long press ATX power action");
|
||||
send("setATXPowerAction", { action: "power-long" }, (resp: JsonRpcResponse) => {
|
||||
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);
|
||||
});
|
||||
|
|
@ -73,7 +80,12 @@ export function ATXPowerControl() {
|
|||
console.log("Sending short press ATX power action");
|
||||
send("setATXPowerAction", { action: "power-short" }, (resp: JsonRpcResponse) => {
|
||||
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={() => {
|
||||
send("setATXPowerAction", { action: "reset" }, (resp: JsonRpcResponse) => {
|
||||
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;
|
||||
}
|
||||
});
|
||||
|
|
@ -149,9 +166,7 @@ export function ATXPowerControl() {
|
|||
<span className="text-sm text-slate-600 dark:text-slate-400">
|
||||
<LuHardDrive
|
||||
strokeWidth={3}
|
||||
className={`mr-1 inline ${
|
||||
atxState?.hdd ? "text-blue-400" : "text-slate-300"
|
||||
}`}
|
||||
className={`mr-1 inline ${atxState?.hdd ? "text-blue-400" : "text-slate-300"}`}
|
||||
/>
|
||||
{m.atx_power_control_hdd_led()}
|
||||
</span>
|
||||
|
|
|
|||
|
|
@ -26,7 +26,9 @@ export function DCPowerControl() {
|
|||
const getDCPowerState = useCallback(() => {
|
||||
send("getDCPowerState", {}, (resp: JsonRpcResponse) => {
|
||||
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;
|
||||
}
|
||||
setPowerState(resp.result as DCPowerState);
|
||||
|
|
@ -36,7 +38,12 @@ export function DCPowerControl() {
|
|||
const handlePowerToggle = (enabled: boolean) => {
|
||||
send("setDCPowerState", { enabled }, (resp: JsonRpcResponse) => {
|
||||
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;
|
||||
}
|
||||
getDCPowerState(); // Refresh state after change
|
||||
|
|
@ -46,7 +53,12 @@ export function DCPowerControl() {
|
|||
// const state = powerState?.restoreState === 0 ? 1 : powerState?.restoreState === 1 ? 2 : 0;
|
||||
send("setDCRestoreState", { state }, (resp: JsonRpcResponse) => {
|
||||
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;
|
||||
}
|
||||
getDCPowerState(); // Refresh state after change
|
||||
|
|
@ -101,9 +113,9 @@ export function DCPowerControl() {
|
|||
value={powerState.restoreState}
|
||||
onChange={e => handleRestoreChange(parseInt(e.target.value))}
|
||||
options={[
|
||||
{ value: '0', label: m.dc_power_control_power_off_state()},
|
||||
{ value: '1', label: m.dc_power_control_power_on_state()},
|
||||
{ value: '2', label: m.dc_power_control_restore_last_state()},
|
||||
{ value: "0", label: m.dc_power_control_power_off_state() },
|
||||
{ value: "1", label: m.dc_power_control_power_on_state() },
|
||||
{ value: "2", label: m.dc_power_control_restore_last_state() },
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -29,7 +29,9 @@ export function SerialConsole() {
|
|||
useEffect(() => {
|
||||
send("getSerialSettings", {}, (resp: JsonRpcResponse) => {
|
||||
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;
|
||||
}
|
||||
setSettings(resp.result as SerialSettings);
|
||||
|
|
@ -40,7 +42,12 @@ export function SerialConsole() {
|
|||
const newSettings = { ...settings, [setting]: value };
|
||||
send("setSerialSettings", { settings: newSettings }, (resp: JsonRpcResponse) => {
|
||||
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;
|
||||
}
|
||||
setSettings(newSettings);
|
||||
|
|
|
|||
|
|
@ -61,7 +61,9 @@ export default function ExtensionPopover() {
|
|||
send("setActiveExtension", { extensionId: extension?.id || "" }, (resp: JsonRpcResponse) => {
|
||||
if ("error" in resp) {
|
||||
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;
|
||||
}
|
||||
|
|
@ -93,7 +95,7 @@ export default function ExtensionPopover() {
|
|||
{renderActiveExtension()}
|
||||
|
||||
<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={{
|
||||
animationDuration: "0.7s",
|
||||
animationDelay: "0.2s",
|
||||
|
|
@ -117,12 +119,9 @@ export default function ExtensionPopover() {
|
|||
<Card className="animate-fadeIn opacity-0">
|
||||
<div className="w-full divide-y divide-slate-700/30 dark:divide-slate-600/30">
|
||||
{AVAILABLE_EXTENSIONS.map(extension => (
|
||||
<div
|
||||
key={extension.id}
|
||||
className="flex items-center justify-between p-3"
|
||||
>
|
||||
<div key={extension.id} className="flex items-center justify-between p-3">
|
||||
<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}
|
||||
</p>
|
||||
<p className="text-sm text-slate-600 dark:text-slate-400">
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ const MountPopopover = forwardRef<HTMLDivElement, object>((_props, ref) => {
|
|||
</Card>
|
||||
</div>
|
||||
<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()}
|
||||
</h3>
|
||||
<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
|
||||
className="animate-fadeIn opacity-0 space-y-2"
|
||||
className="animate-fadeIn space-y-2 opacity-0"
|
||||
style={{
|
||||
animationDuration: "0.7s",
|
||||
animationDelay: "0.1s",
|
||||
|
|
@ -156,11 +156,13 @@ const MountPopopover = forwardRef<HTMLDivElement, object>((_props, ref) => {
|
|||
</div>
|
||||
</div>
|
||||
{remoteVirtualMediaState ? (
|
||||
<div className="flex select-none items-center justify-between text-xs">
|
||||
<div className="select-none text-white dark:text-slate-300">
|
||||
<div className="flex items-center justify-between text-xs select-none">
|
||||
<div className="text-white select-none dark:text-slate-300">
|
||||
<span>{m.mount_mounted_as()}</span>{" "}
|
||||
<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>
|
||||
</div>
|
||||
|
||||
|
|
@ -189,10 +191,7 @@ const MountPopopover = forwardRef<HTMLDivElement, object>((_props, ref) => {
|
|||
d="M4.99933 0.775635L0 5.77546H10L4.99933 0.775635Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<path
|
||||
d="M10 7.49976H0V9.22453H10V7.49976Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<path d="M10 7.49976H0V9.22453H10V7.49976Z" fill="currentColor" />
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_3137_1186">
|
||||
|
|
@ -213,7 +212,7 @@ const MountPopopover = forwardRef<HTMLDivElement, object>((_props, ref) => {
|
|||
|
||||
{!remoteVirtualMediaState && (
|
||||
<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={{
|
||||
animationDuration: "0.7s",
|
||||
animationDelay: "0.2s",
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ export default function PasteModal() {
|
|||
const close = useClose();
|
||||
|
||||
const debugMode = useSettingsStore(state => state.debugMode);
|
||||
const delayClassName = useMemo(() => debugMode ? "" : "hidden", [debugMode]);
|
||||
const delayClassName = useMemo(() => (debugMode ? "" : "hidden"), [debugMode]);
|
||||
|
||||
const { setKeyboardLayout } = useSettingsStore();
|
||||
const { selectedKeyboard } = useKeyboardLayout();
|
||||
|
|
@ -66,7 +66,7 @@ export default function PasteModal() {
|
|||
const macroSteps: MacroStep[] = [];
|
||||
|
||||
for (const char of text) {
|
||||
const normalizedChar = char.normalize('NFC');
|
||||
const normalizedChar = char.normalize("NFC");
|
||||
const keyprops = selectedKeyboard.chars[normalizedChar];
|
||||
if (!keyprops) continue;
|
||||
|
||||
|
|
@ -94,7 +94,7 @@ export default function PasteModal() {
|
|||
macroSteps.push({
|
||||
keys: [String(key)],
|
||||
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
|
||||
|
|
@ -123,10 +123,7 @@ export default function PasteModal() {
|
|||
<div className="grid h-full grid-rows-(--grid-headerBody)">
|
||||
<div className="h-full space-y-4">
|
||||
<div className="space-y-4">
|
||||
<SettingsPageHeader
|
||||
title={m.paste_text()}
|
||||
description={m.paste_text_description()}
|
||||
/>
|
||||
<SettingsPageHeader title={m.paste_text()} description={m.paste_text_description()} />
|
||||
|
||||
<div
|
||||
className="animate-fadeIn space-y-2 opacity-0"
|
||||
|
|
@ -165,7 +162,7 @@ export default function PasteModal() {
|
|||
...new Set(
|
||||
// @ts-expect-error TS doesn't recognize Intl.Segmenter in some environments
|
||||
[...new Intl.Segmenter().segment(value)]
|
||||
.map(x => x.segment.normalize('NFC'))
|
||||
.map(x => x.segment.normalize("NFC"))
|
||||
.filter(char => !selectedKeyboard.chars[char]),
|
||||
),
|
||||
];
|
||||
|
|
@ -178,8 +175,7 @@ export default function PasteModal() {
|
|||
<div className="mt-2 flex items-center gap-x-2">
|
||||
<ExclamationCircleIcon className="h-4 w-4 text-red-500 dark:text-red-400" />
|
||||
<span className="text-xs text-red-500 dark:text-red-400">
|
||||
{m.paste_modal_invalid_chars_intro()}{" "}
|
||||
{invalidChars.join(", ")}
|
||||
{m.paste_modal_invalid_chars_intro()} {invalidChars.join(", ")}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
|
|
@ -197,18 +193,22 @@ export default function PasteModal() {
|
|||
setDelayValue(parseInt(e.target.value, 10));
|
||||
}}
|
||||
/>
|
||||
{delayValue < 50 || delayValue > 65534 && (
|
||||
{delayValue < 50 ||
|
||||
(delayValue > 65534 && (
|
||||
<div className="mt-2 flex items-center gap-x-2">
|
||||
<ExclamationCircleIcon className="h-4 w-4 text-red-500 dark:text-red-400" />
|
||||
<span className="text-xs text-red-500 dark:text-red-400">
|
||||
{m.paste_modal_delay_out_of_range({ min: 50, max: 65534 })}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
))}
|
||||
</div>
|
||||
<div className="space-y-4">
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ export default function AddDeviceForm({
|
|||
return (
|
||||
<div className="space-y-4">
|
||||
<div
|
||||
className="animate-fadeIn opacity-0 space-y-4"
|
||||
className="animate-fadeIn space-y-4 opacity-0"
|
||||
style={{
|
||||
animationDuration: "0.5s",
|
||||
animationFillMode: "forwards",
|
||||
|
|
@ -74,7 +74,7 @@ export default function AddDeviceForm({
|
|||
/>
|
||||
</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={{
|
||||
animationDuration: "0.7s",
|
||||
animationDelay: "0.2s",
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ export default function DeviceList({
|
|||
{storedDevices.map((device, index) => (
|
||||
<div key={index} className="flex items-center justify-between gap-x-2 p-3">
|
||||
<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}
|
||||
</p>
|
||||
<p className="text-sm text-slate-600 dark:text-slate-400">
|
||||
|
|
@ -64,7 +64,7 @@ export default function DeviceList({
|
|||
</div>
|
||||
</Card>
|
||||
<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={{
|
||||
animationDuration: "0.7s",
|
||||
animationDelay: "0.2s",
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ export default function EmptyStateCard({
|
|||
setShowAddForm: (show: boolean) => void;
|
||||
}) {
|
||||
return (
|
||||
<div className="select-none space-y-4">
|
||||
<div className="space-y-4 select-none">
|
||||
<Card className="animate-fadeIn opacity-0">
|
||||
<div className="flex items-center justify-center py-8 text-center">
|
||||
<div className="space-y-3">
|
||||
|
|
@ -25,7 +25,7 @@ export default function EmptyStateCard({
|
|||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
<h3 className="text-sm font-semibold leading-none text-black dark:text-white">
|
||||
<h3 className="text-sm leading-none font-semibold text-black dark:text-white">
|
||||
{m.wake_on_lan_empty_no_devices_added()}
|
||||
</h3>
|
||||
<p className="text-xs leading-none text-slate-700 dark:text-slate-300">
|
||||
|
|
@ -36,7 +36,7 @@ export default function EmptyStateCard({
|
|||
</div>
|
||||
</Card>
|
||||
<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={{
|
||||
animationDuration: "0.7s",
|
||||
animationDelay: "0.2s",
|
||||
|
|
|
|||
|
|
@ -69,13 +69,17 @@ export default function WakeOnLanModal() {
|
|||
(index: number) => {
|
||||
const updatedDevices = storedDevices.filter((_, i) => i !== index);
|
||||
|
||||
send("setWakeOnLanDevices", { params: { devices: updatedDevices } }, (resp: JsonRpcResponse) => {
|
||||
send(
|
||||
"setWakeOnLanDevices",
|
||||
{ params: { devices: updatedDevices } },
|
||||
(resp: JsonRpcResponse) => {
|
||||
if ("error" in resp) {
|
||||
console.error("Failed to update Wake-on-LAN devices:", resp.error);
|
||||
} else {
|
||||
syncStoredDevices();
|
||||
}
|
||||
});
|
||||
},
|
||||
);
|
||||
},
|
||||
[send, storedDevices, syncStoredDevices],
|
||||
);
|
||||
|
|
@ -85,7 +89,10 @@ export default function WakeOnLanModal() {
|
|||
if (!name || !macAddress) return;
|
||||
const updatedDevices = [...storedDevices, { name, macAddress }];
|
||||
console.log("updatedDevices", updatedDevices);
|
||||
send("setWakeOnLanDevices", { params: { devices: updatedDevices } }, (resp: JsonRpcResponse) => {
|
||||
send(
|
||||
"setWakeOnLanDevices",
|
||||
{ params: { devices: updatedDevices } },
|
||||
(resp: JsonRpcResponse) => {
|
||||
if ("error" in resp) {
|
||||
console.error("Failed to add Wake-on-LAN device:", resp.error);
|
||||
setAddDeviceErrorMessage(m.wake_on_lan_failed_add_device());
|
||||
|
|
@ -93,7 +100,8 @@ export default function WakeOnLanModal() {
|
|||
setShowAddForm(false);
|
||||
syncStoredDevices();
|
||||
}
|
||||
});
|
||||
},
|
||||
);
|
||||
},
|
||||
[send, storedDevices, syncStoredDevices],
|
||||
);
|
||||
|
|
@ -103,10 +111,7 @@ export default function WakeOnLanModal() {
|
|||
<div className="space-y-4 p-4 py-3">
|
||||
<div className="grid h-full grid-rows-(--grid-headerBody)">
|
||||
<div className="space-y-4">
|
||||
<SettingsPageHeader
|
||||
title={m.wake_on_lan()}
|
||||
description={m.wake_on_lan_description()}
|
||||
/>
|
||||
<SettingsPageHeader title={m.wake_on_lan()} description={m.wake_on_lan_description()} />
|
||||
|
||||
{showAddForm ? (
|
||||
<AddDeviceForm
|
||||
|
|
|
|||
|
|
@ -124,17 +124,28 @@ export default function ConnectionStatsSidebar() {
|
|||
</div>
|
||||
<div className="flex items-center">
|
||||
<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}
|
||||
</div>
|
||||
</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
|
||||
className="rounded-l-none border-l-slate-800/30 dark:border-slate-300/20"
|
||||
size="SM"
|
||||
type="button"
|
||||
theme="light"
|
||||
LeadingIcon={LuCopy}
|
||||
onClick={async () => {
|
||||
if (await copy(remoteIPAddress)) {
|
||||
notifications.success((m.connection_stats_remote_ip_address_copy_success({ ip: 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>
|
||||
)}
|
||||
|
|
@ -184,10 +195,7 @@ export default function ConnectionStatsSidebar() {
|
|||
data={jitterBufferAvgDelayData}
|
||||
gate={inboundVideoRtpStats}
|
||||
supported={
|
||||
someIterable(
|
||||
inboundVideoRtpStats,
|
||||
([, x]) => x.jitterBufferDelay != null,
|
||||
) &&
|
||||
someIterable(inboundVideoRtpStats, ([, x]) => x.jitterBufferDelay != null) &&
|
||||
someIterable(
|
||||
inboundVideoRtpStats,
|
||||
([, x]) => x.jitterBufferEmittedCount != null,
|
||||
|
|
|
|||
|
|
@ -3,7 +3,8 @@ import { useCallback, useState } from "react";
|
|||
export function useCopyToClipboard(resetInterval = 2000) {
|
||||
const [isCopied, setIsCopied] = useState(false);
|
||||
|
||||
const copy = useCallback(async (text: string) => {
|
||||
const copy = useCallback(
|
||||
async (text: string) => {
|
||||
if (!text) return false;
|
||||
|
||||
let success = false;
|
||||
|
|
@ -43,7 +44,9 @@ export function useCopyToClipboard(resetInterval = 2000) {
|
|||
}
|
||||
|
||||
return success;
|
||||
}, [resetInterval]);
|
||||
},
|
||||
[resetInterval],
|
||||
);
|
||||
|
||||
return { copy, isCopied };
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,9 +13,9 @@ export const HID_RPC_MESSAGE_TYPES = {
|
|||
KeyboardLedState: 0x32,
|
||||
KeysDownState: 0x33,
|
||||
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;
|
||||
|
||||
|
|
@ -28,12 +28,7 @@ const fromInt32toUint8 = (n: number) => {
|
|||
throw new Error(`Number ${n} is not within the int32 range`);
|
||||
}
|
||||
|
||||
return new Uint8Array([
|
||||
(n >> 24) & 0xFF,
|
||||
(n >> 16) & 0xFF,
|
||||
(n >> 8) & 0xFF,
|
||||
n & 0xFF,
|
||||
]);
|
||||
return new Uint8Array([(n >> 24) & 0xff, (n >> 16) & 0xff, (n >> 8) & 0xff, n & 0xff]);
|
||||
};
|
||||
|
||||
const fromUint16toUint8 = (n: number) => {
|
||||
|
|
@ -41,10 +36,7 @@ const fromUint16toUint8 = (n: number) => {
|
|||
throw new Error(`Number ${n} is not within the uint16 range`);
|
||||
}
|
||||
|
||||
return new Uint8Array([
|
||||
(n >> 8) & 0xFF,
|
||||
n & 0xFF,
|
||||
]);
|
||||
return new Uint8Array([(n >> 8) & 0xff, n & 0xff]);
|
||||
};
|
||||
|
||||
const fromUint32toUint8 = (n: number) => {
|
||||
|
|
@ -52,12 +44,7 @@ const fromUint32toUint8 = (n: number) => {
|
|||
throw new Error(`Number ${n} is not within the uint32 range`);
|
||||
}
|
||||
|
||||
return new Uint8Array([
|
||||
(n >> 24) & 0xFF,
|
||||
(n >> 16) & 0xFF,
|
||||
(n >> 8) & 0xFF,
|
||||
n & 0xFF,
|
||||
]);
|
||||
return new Uint8Array([(n >> 24) & 0xff, (n >> 16) & 0xff, (n >> 8) & 0xff, n & 0xff]);
|
||||
};
|
||||
|
||||
const fromInt8ToUint8 = (n: number) => {
|
||||
|
|
@ -65,7 +52,7 @@ const fromInt8ToUint8 = (n: number) => {
|
|||
throw new Error(`Number ${n} is not within the int8 range`);
|
||||
}
|
||||
|
||||
return n & 0xFF;
|
||||
return n & 0xff;
|
||||
};
|
||||
|
||||
const keyboardLedStateMasks = {
|
||||
|
|
@ -75,7 +62,7 @@ const keyboardLedStateMasks = {
|
|||
compose: 1 << 3,
|
||||
kana: 1 << 4,
|
||||
shift: 1 << 6,
|
||||
}
|
||||
};
|
||||
|
||||
export class RpcMessage {
|
||||
messageType: HidRpcMessageType;
|
||||
|
|
@ -145,11 +132,7 @@ export class KeypressReportMessage extends RpcMessage {
|
|||
}
|
||||
|
||||
marshal(): Uint8Array {
|
||||
return new Uint8Array([
|
||||
this.messageType,
|
||||
this.key,
|
||||
this.press ? 1 : 0,
|
||||
]);
|
||||
return new Uint8Array([this.messageType, this.key, this.press ? 1 : 0]);
|
||||
}
|
||||
|
||||
public static unmarshal(data: Uint8Array): KeypressReportMessage | undefined {
|
||||
|
|
@ -170,7 +153,7 @@ export class KeyboardReportMessage extends RpcMessage {
|
|||
}
|
||||
|
||||
set keys(value: number[]) {
|
||||
value.forEach((k) => {
|
||||
value.forEach(k => {
|
||||
if (!withinUint8Range(k)) {
|
||||
throw new Error(`Key ${k} is not within the uint8 range`);
|
||||
}
|
||||
|
|
@ -198,11 +181,7 @@ export class KeyboardReportMessage extends RpcMessage {
|
|||
}
|
||||
|
||||
marshal(): Uint8Array {
|
||||
return new Uint8Array([
|
||||
this.messageType,
|
||||
this.modifier,
|
||||
...this.keys,
|
||||
]);
|
||||
return new Uint8Array([this.messageType, this.modifier, ...this.keys]);
|
||||
}
|
||||
|
||||
public static unmarshal(data: Uint8Array): KeyboardReportMessage | undefined {
|
||||
|
|
@ -235,15 +214,20 @@ export class KeyboardMacroReportMessage extends RpcMessage {
|
|||
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}`);
|
||||
throw new Error(
|
||||
`Length ${this.stepCount} is not equal to the number of steps ${this.steps.length}`,
|
||||
);
|
||||
}
|
||||
|
||||
const data = new Uint8Array(this.stepCount * 9 + 6);
|
||||
data.set(new Uint8Array([
|
||||
data.set(
|
||||
new Uint8Array([
|
||||
this.messageType,
|
||||
this.isPaste ? 1 : 0,
|
||||
...fromUint32toUint8(this.stepCount),
|
||||
]), 0);
|
||||
]),
|
||||
0,
|
||||
);
|
||||
|
||||
let offset = 6;
|
||||
|
||||
|
|
@ -292,11 +276,7 @@ export class KeyboardMacroStateMessage extends RpcMessage {
|
|||
}
|
||||
|
||||
marshal(): Uint8Array {
|
||||
return new Uint8Array([
|
||||
this.messageType,
|
||||
this.state ? 1 : 0,
|
||||
this.isPaste ? 1 : 0,
|
||||
]);
|
||||
return new Uint8Array([this.messageType, this.state ? 1 : 0, this.isPaste ? 1 : 0]);
|
||||
}
|
||||
|
||||
public static unmarshal(data: Uint8Array): KeyboardMacroStateMessage | undefined {
|
||||
|
|
@ -351,7 +331,7 @@ export class KeysDownStateMessage extends RpcMessage {
|
|||
|
||||
return new KeysDownStateMessage({
|
||||
modifier: data[0],
|
||||
keys: Array.from(data.slice(1))
|
||||
keys: Array.from(data.slice(1)),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -379,7 +359,6 @@ export class PointerReportMessage extends RpcMessage {
|
|||
}
|
||||
|
||||
export class CancelKeyboardMacroReportMessage extends RpcMessage {
|
||||
|
||||
constructor() {
|
||||
super(HID_RPC_MESSAGE_TYPES.CancelKeyboardMacroReport);
|
||||
}
|
||||
|
|
@ -431,7 +410,7 @@ export const messageRegistry = {
|
|||
[HID_RPC_MESSAGE_TYPES.CancelKeyboardMacroReport]: CancelKeyboardMacroReportMessage,
|
||||
[HID_RPC_MESSAGE_TYPES.KeyboardMacroState]: KeyboardMacroStateMessage,
|
||||
[HID_RPC_MESSAGE_TYPES.KeypressKeepAliveReport]: KeypressKeepAliveMessage,
|
||||
}
|
||||
};
|
||||
|
||||
export const unmarshalHidRpcMessage = (data: Uint8Array): RpcMessage | undefined => {
|
||||
if (data.length < 1) {
|
||||
|
|
|
|||
|
|
@ -1,11 +1,7 @@
|
|||
import { create } from "zustand";
|
||||
import { createJSONStorage, persist } from "zustand/middleware";
|
||||
|
||||
import {
|
||||
MAX_STEPS_PER_MACRO,
|
||||
MAX_TOTAL_MACROS,
|
||||
MAX_KEYS_PER_STEP,
|
||||
} from "@/constants/macros";
|
||||
import { MAX_STEPS_PER_MACRO, MAX_TOTAL_MACROS, MAX_KEYS_PER_STEP } from "@/constants/macros";
|
||||
|
||||
// Define the JsonRpc types for better type checking
|
||||
interface JsonRpcResponse {
|
||||
|
|
@ -92,8 +88,7 @@ export const useUiStore = create<UIState>(set => ({
|
|||
setDisableVideoFocusTrap: (enabled: boolean) => set({ disableVideoFocusTrap: enabled }),
|
||||
|
||||
isWakeOnLanModalVisible: false,
|
||||
setWakeOnLanModalVisibility: (enabled: boolean) =>
|
||||
set({ isWakeOnLanModalVisible: enabled }),
|
||||
setWakeOnLanModalVisibility: (enabled: boolean) => set({ isWakeOnLanModalVisible: enabled }),
|
||||
|
||||
toggleSidebarView: view =>
|
||||
set(state => {
|
||||
|
|
@ -275,12 +270,7 @@ export const useMouseStore = create<MouseState>(set => ({
|
|||
setMousePosition: (x, y) => set({ mouseX: x, mouseY: y }),
|
||||
}));
|
||||
|
||||
export type HdmiStates =
|
||||
| "ready"
|
||||
| "no_signal"
|
||||
| "no_lock"
|
||||
| "out_of_range"
|
||||
| "connecting";
|
||||
export type HdmiStates = "ready" | "no_signal" | "no_lock" | "out_of_range" | "connecting";
|
||||
export type HdmiErrorStates = Extract<
|
||||
VideoState["hdmiState"],
|
||||
"no_signal" | "no_lock" | "out_of_range"
|
||||
|
|
@ -310,8 +300,7 @@ export const useVideoStore = create<VideoState>(set => ({
|
|||
clientHeight: 0,
|
||||
|
||||
// The video element's client size
|
||||
setClientSize: (clientWidth: number, clientHeight: number) =>
|
||||
set({ clientWidth, clientHeight }),
|
||||
setClientSize: (clientWidth: number, clientHeight: number) => set({ clientWidth, clientHeight }),
|
||||
|
||||
// Resolution
|
||||
setSize: (width: number, height: number) => set({ width, height }),
|
||||
|
|
@ -401,8 +390,7 @@ export const useSettingsStore = create(
|
|||
dim_after: 10000,
|
||||
off_after: 50000,
|
||||
},
|
||||
setBacklightSettings: (settings: BacklightSettings) =>
|
||||
set({ backlightSettings: settings }),
|
||||
setBacklightSettings: (settings: BacklightSettings) => set({ backlightSettings: settings }),
|
||||
|
||||
keyboardLayout: "en-US",
|
||||
setKeyboardLayout: (layout: string) => set({ keyboardLayout: layout }),
|
||||
|
|
@ -493,12 +481,7 @@ export interface KeysDownState {
|
|||
keys: number[];
|
||||
}
|
||||
|
||||
export type USBStates =
|
||||
| "configured"
|
||||
| "attached"
|
||||
| "not attached"
|
||||
| "suspended"
|
||||
| "addressed";
|
||||
export type USBStates = "configured" | "attached" | "not attached" | "suspended" | "addressed";
|
||||
|
||||
export interface HidState {
|
||||
keyboardLedState: KeyboardLedState;
|
||||
|
|
@ -526,15 +509,13 @@ export const useHidStore = create<HidState>(set => ({
|
|||
kana: false,
|
||||
shift: false,
|
||||
} as KeyboardLedState,
|
||||
setKeyboardLedState: (ledState: KeyboardLedState): void =>
|
||||
set({ keyboardLedState: ledState }),
|
||||
setKeyboardLedState: (ledState: KeyboardLedState): void => set({ keyboardLedState: ledState }),
|
||||
|
||||
keysDownState: { modifier: 0, keys: [0, 0, 0, 0, 0, 0] } as KeysDownState,
|
||||
setKeysDownState: (state: KeysDownState): void => set({ keysDownState: state }),
|
||||
|
||||
isVirtualKeyboardEnabled: false,
|
||||
setVirtualKeyboardEnabled: (enabled: boolean): void =>
|
||||
set({ isVirtualKeyboardEnabled: enabled }),
|
||||
setVirtualKeyboardEnabled: (enabled: boolean): void => set({ isVirtualKeyboardEnabled: enabled }),
|
||||
|
||||
isPasteInProgress: false,
|
||||
setPasteModeEnabled: (enabled: boolean): void => set({ isPasteInProgress: enabled }),
|
||||
|
|
@ -641,8 +622,7 @@ export const useUpdateStore = create<UpdateState>(set => ({
|
|||
setModalView: (view: UpdateModalViews) => set({ modalView: view }),
|
||||
|
||||
updateErrorMessage: null,
|
||||
setUpdateErrorMessage: (errorMessage: string) =>
|
||||
set({ updateErrorMessage: errorMessage }),
|
||||
setUpdateErrorMessage: (errorMessage: string) => set({ updateErrorMessage: errorMessage }),
|
||||
|
||||
shouldReload: false,
|
||||
setShouldReload: (reloadRequired: boolean) => set({ shouldReload: reloadRequired }),
|
||||
|
|
@ -791,12 +771,7 @@ export type IPv6Mode =
|
|||
export type IPv4Mode = "disabled" | "static" | "dhcp" | "unknown";
|
||||
export type LLDPMode = "disabled" | "basic" | "all" | "unknown";
|
||||
export type mDNSMode = "disabled" | "auto" | "ipv4_only" | "ipv6_only" | "unknown";
|
||||
export type TimeSyncMode =
|
||||
| "ntp_only"
|
||||
| "ntp_and_http"
|
||||
| "http_only"
|
||||
| "custom"
|
||||
| "unknown";
|
||||
export type TimeSyncMode = "ntp_only" | "ntp_and_http" | "http_only" | "custom" | "unknown";
|
||||
|
||||
export interface IPv4StaticConfig {
|
||||
address: string;
|
||||
|
|
|
|||
|
|
@ -31,7 +31,8 @@ export function useHidRpc(onHidRpcMessage?: (payload: RpcMessage) => void) {
|
|||
rpcHidUnreliableChannel,
|
||||
rpcHidUnreliableNonOrderedChannel,
|
||||
setRpcHidProtocolVersion,
|
||||
rpcHidProtocolVersion, hidRpcDisabled,
|
||||
rpcHidProtocolVersion,
|
||||
hidRpcDisabled,
|
||||
} = useRTCStore();
|
||||
|
||||
const rpcHidReady = useMemo(() => {
|
||||
|
|
@ -40,15 +41,12 @@ export function useHidRpc(onHidRpcMessage?: (payload: RpcMessage) => void) {
|
|||
}, [rpcHidChannel, rpcHidProtocolVersion, hidRpcDisabled]);
|
||||
|
||||
const rpcHidUnreliableReady = useMemo(() => {
|
||||
return (
|
||||
rpcHidUnreliableChannel?.readyState === "open" && rpcHidProtocolVersion !== null
|
||||
);
|
||||
return rpcHidUnreliableChannel?.readyState === "open" && rpcHidProtocolVersion !== null;
|
||||
}, [rpcHidProtocolVersion, rpcHidUnreliableChannel?.readyState]);
|
||||
|
||||
const rpcHidUnreliableNonOrderedReady = useMemo(() => {
|
||||
return (
|
||||
rpcHidUnreliableNonOrderedChannel?.readyState === "open" &&
|
||||
rpcHidProtocolVersion !== null
|
||||
rpcHidUnreliableNonOrderedChannel?.readyState === "open" && rpcHidProtocolVersion !== null
|
||||
);
|
||||
}, [rpcHidProtocolVersion, rpcHidUnreliableNonOrderedChannel?.readyState]);
|
||||
|
||||
|
|
@ -64,11 +62,7 @@ export function useHidRpc(onHidRpcMessage?: (payload: RpcMessage) => void) {
|
|||
const sendMessage = useCallback(
|
||||
(
|
||||
message: RpcMessage,
|
||||
{
|
||||
ignoreHandshakeState,
|
||||
useUnreliableChannel,
|
||||
requireOrdered = true,
|
||||
}: sendMessageParams = {},
|
||||
{ ignoreHandshakeState, useUnreliableChannel, requireOrdered = true }: sendMessageParams = {},
|
||||
) => {
|
||||
if (hidRpcDisabled) return;
|
||||
if (rpcHidChannel?.readyState !== "open") return;
|
||||
|
|
@ -96,7 +90,8 @@ export function useHidRpc(onHidRpcMessage?: (payload: RpcMessage) => void) {
|
|||
[
|
||||
rpcHidChannel,
|
||||
rpcHidUnreliableChannel,
|
||||
hidRpcDisabled, rpcHidUnreliableNonOrderedChannel,
|
||||
hidRpcDisabled,
|
||||
rpcHidUnreliableNonOrderedChannel,
|
||||
rpcHidReady,
|
||||
rpcHidUnreliableReady,
|
||||
rpcHidUnreliableNonOrderedReady,
|
||||
|
|
@ -140,12 +135,9 @@ export function useHidRpc(onHidRpcMessage?: (payload: RpcMessage) => void) {
|
|||
[sendMessage],
|
||||
);
|
||||
|
||||
const cancelOngoingKeyboardMacro = useCallback(
|
||||
() => {
|
||||
const cancelOngoingKeyboardMacro = useCallback(() => {
|
||||
sendMessage(new CancelKeyboardMacroReportMessage());
|
||||
},
|
||||
[sendMessage],
|
||||
);
|
||||
}, [sendMessage]);
|
||||
|
||||
const reportKeypressKeepAlive = useCallback(() => {
|
||||
sendMessage(KEEPALIVE_MESSAGE);
|
||||
|
|
@ -224,7 +216,7 @@ export function useHidRpc(onHidRpcMessage?: (payload: RpcMessage) => void) {
|
|||
};
|
||||
|
||||
const errorHandler = (e: Event) => {
|
||||
console.error(`Error on rpcHidChannel '${rpcHidChannel.label}': ${e}`)
|
||||
console.error(`Error on rpcHidChannel '${rpcHidChannel.label}': ${e}`);
|
||||
};
|
||||
|
||||
rpcHidChannel.addEventListener("message", messageHandler);
|
||||
|
|
|
|||
|
|
@ -37,14 +37,14 @@ let requestCounter = 0;
|
|||
// Map of blocked RPC methods by failsafe reason
|
||||
const blockedMethodsByReason: Record<string, string[]> = {
|
||||
video: [
|
||||
'setStreamQualityFactor',
|
||||
'getEDID',
|
||||
'setEDID',
|
||||
'getVideoLogStatus',
|
||||
'setDisplayRotation',
|
||||
'getVideoSleepMode',
|
||||
'setVideoSleepMode',
|
||||
'getVideoState',
|
||||
"setStreamQualityFactor",
|
||||
"getEDID",
|
||||
"setEDID",
|
||||
"getVideoLogStatus",
|
||||
"setDisplayRotation",
|
||||
"getVideoSleepMode",
|
||||
"setVideoSleepMode",
|
||||
"getVideoState",
|
||||
],
|
||||
};
|
||||
|
||||
|
|
@ -86,7 +86,7 @@ export function useJsonRpc(onRequest?: (payload: JsonRpcRequest) => void) {
|
|||
|
||||
rpcDataChannel.send(JSON.stringify(payload));
|
||||
},
|
||||
[rpcDataChannel, isFailsafeMode, reason]
|
||||
[rpcDataChannel, isFailsafeMode, reason],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
|
|
@ -117,8 +117,7 @@ export function useJsonRpc(onRequest?: (payload: JsonRpcRequest) => void) {
|
|||
return () => {
|
||||
rpcDataChannel.removeEventListener("message", messageHandler);
|
||||
};
|
||||
},
|
||||
[rpcDataChannel, onRequest]);
|
||||
}, [rpcDataChannel, onRequest]);
|
||||
|
||||
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 (overrun) {
|
||||
if (press) {
|
||||
console.warn(
|
||||
`keyboard buffer overflow current keys ${keys}, key: ${key} not added`,
|
||||
);
|
||||
console.warn(`keyboard buffer overflow current keys ${keys}, key: ${key} not added`);
|
||||
// Fill all key slots with ErrorRollOver (0x01) to indicate overflow
|
||||
keys.length = hidKeyBufferSize;
|
||||
keys.fill(hidErrorRollOver);
|
||||
|
|
@ -248,11 +246,7 @@ export default function useKeyboard() {
|
|||
// Older backends don't support the hidRpc API, so we need:
|
||||
// 1. Calculate the state
|
||||
// 2. Send the newly calculated state to the device
|
||||
const downState = simulateDeviceSideKeyHandlingForLegacyDevices(
|
||||
keysDownState,
|
||||
key,
|
||||
press,
|
||||
);
|
||||
const downState = simulateDeviceSideKeyHandlingForLegacyDevices(keysDownState, key, press);
|
||||
|
||||
handleLegacyKeyboardReport(downState.keys, downState.modifier);
|
||||
|
||||
|
|
|
|||
|
|
@ -7,8 +7,8 @@ export default function useKeyboardLayout() {
|
|||
const { keyboardLayout } = useSettingsStore();
|
||||
|
||||
const keyboardOptions = useMemo(() => {
|
||||
return keyboards.map((keyboard) => {
|
||||
return { label: keyboard.name, value: keyboard.isoCode }
|
||||
return keyboards.map(keyboard => {
|
||||
return { label: keyboard.name, value: keyboard.isoCode };
|
||||
});
|
||||
}, []);
|
||||
|
||||
|
|
@ -27,8 +27,10 @@ export default function useKeyboardLayout() {
|
|||
|
||||
const selectedKeyboard = useMemo(() => {
|
||||
// fallback to original behaviour of en-US if no isoCode given or matching layout not found
|
||||
return keyboards.find(keyboard => keyboard.isoCode === isoCode)
|
||||
?? keyboards.find(keyboard => keyboard.isoCode === "en-US")!;
|
||||
return (
|
||||
keyboards.find(keyboard => keyboard.isoCode === isoCode) ??
|
||||
keyboards.find(keyboard => keyboard.isoCode === "en-US")!
|
||||
);
|
||||
}, [isoCode]);
|
||||
|
||||
return { keyboardOptions, isoCode, selectedKeyboard };
|
||||
|
|
|
|||
|
|
@ -40,13 +40,7 @@ export default function useMouse() {
|
|||
}
|
||||
setMouseMove({ x, y, buttons });
|
||||
},
|
||||
[
|
||||
send,
|
||||
reportRelMouseEvent,
|
||||
setMouseMove,
|
||||
mouseMode,
|
||||
rpcHidReady,
|
||||
],
|
||||
[send, reportRelMouseEvent, setMouseMove, mouseMode, rpcHidReady],
|
||||
);
|
||||
|
||||
const getRelMouseMoveHandler = useCallback(
|
||||
|
|
@ -72,17 +66,12 @@ export default function useMouse() {
|
|||
// We set that for the debug info bar
|
||||
setMousePosition(x, y);
|
||||
},
|
||||
[
|
||||
send,
|
||||
reportAbsMouseEvent,
|
||||
setMousePosition,
|
||||
mouseMode,
|
||||
rpcHidReady,
|
||||
],
|
||||
[send, reportAbsMouseEvent, setMousePosition, mouseMode, rpcHidReady],
|
||||
);
|
||||
|
||||
const getAbsMouseMoveHandler = useCallback(
|
||||
({ videoClientWidth, videoClientHeight, videoWidth, videoHeight }: AbsMouseMoveHandlerProps) => (e: MouseEvent) => {
|
||||
({ videoClientWidth, videoClientHeight, videoWidth, videoHeight }: AbsMouseMoveHandlerProps) =>
|
||||
(e: MouseEvent) => {
|
||||
if (!videoClientWidth || !videoClientHeight) return;
|
||||
if (mouseMode !== "absolute") return;
|
||||
|
||||
|
|
@ -121,7 +110,8 @@ export default function useMouse() {
|
|||
// Send mouse movement
|
||||
const { buttons } = e;
|
||||
sendAbsMouseMovement(x, y, buttons);
|
||||
}, [mouseMode, sendAbsMouseMovement],
|
||||
},
|
||||
[mouseMode, sendAbsMouseMovement],
|
||||
);
|
||||
|
||||
const getMouseWheelHandler = useCallback(
|
||||
|
|
|
|||
|
|
@ -20,12 +20,7 @@ export interface SystemVersionInfo {
|
|||
}
|
||||
|
||||
export function useVersion() {
|
||||
const {
|
||||
appVersion,
|
||||
systemVersion,
|
||||
setAppVersion,
|
||||
setSystemVersion,
|
||||
} = useDeviceStore();
|
||||
const { appVersion, systemVersion, setAppVersion, setSystemVersion } = useDeviceStore();
|
||||
|
||||
const getVersionInfo = useCallback(async () => {
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,16 @@
|
|||
export interface KeyStroke { modifier: number; keys: number[]; }
|
||||
export interface KeyInfo { key: string | number; shift?: boolean, altRight?: boolean }
|
||||
export interface KeyCombo extends KeyInfo { deadKey?: boolean, accentKey?: KeyInfo }
|
||||
export interface KeyStroke {
|
||||
modifier: number;
|
||||
keys: number[];
|
||||
}
|
||||
export interface KeyInfo {
|
||||
key: string | number;
|
||||
shift?: boolean;
|
||||
altRight?: boolean;
|
||||
}
|
||||
export interface KeyCombo extends KeyInfo {
|
||||
deadKey?: boolean;
|
||||
accentKey?: KeyInfo;
|
||||
}
|
||||
export interface KeyboardLayout {
|
||||
isoCode: string;
|
||||
name: string;
|
||||
|
|
@ -8,25 +18,39 @@ export interface KeyboardLayout {
|
|||
modifierDisplayMap: Record<string, string>;
|
||||
keyDisplayMap: Record<string, string>;
|
||||
virtualKeyboard: {
|
||||
main: { default: string[], shift: string[] },
|
||||
control?: { default: string[], shift?: string[] },
|
||||
arrows?: { default: string[] }
|
||||
main: { default: string[]; shift: string[] };
|
||||
control?: { default: string[]; shift?: string[] };
|
||||
arrows?: { default: string[] };
|
||||
};
|
||||
}
|
||||
|
||||
// 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 { de_CH } from "@/keyboardLayouts/de_CH"
|
||||
import { de_DE } from "@/keyboardLayouts/de_DE"
|
||||
import { en_US } from "@/keyboardLayouts/en_US"
|
||||
import { en_UK } from "@/keyboardLayouts/en_UK"
|
||||
import { es_ES } from "@/keyboardLayouts/es_ES"
|
||||
import { fr_BE } from "@/keyboardLayouts/fr_BE"
|
||||
import { fr_CH } from "@/keyboardLayouts/fr_CH"
|
||||
import { fr_FR } from "@/keyboardLayouts/fr_FR"
|
||||
import { it_IT } from "@/keyboardLayouts/it_IT"
|
||||
import { nb_NO } from "@/keyboardLayouts/nb_NO"
|
||||
import { sv_SE } from "@/keyboardLayouts/sv_SE"
|
||||
import { da_DK } from "@/keyboardLayouts/da_DK"
|
||||
import { cs_CZ } from "@/keyboardLayouts/cs_CZ";
|
||||
import { de_CH } from "@/keyboardLayouts/de_CH";
|
||||
import { de_DE } from "@/keyboardLayouts/de_DE";
|
||||
import { en_US } from "@/keyboardLayouts/en_US";
|
||||
import { en_UK } from "@/keyboardLayouts/en_UK";
|
||||
import { es_ES } from "@/keyboardLayouts/es_ES";
|
||||
import { fr_BE } from "@/keyboardLayouts/fr_BE";
|
||||
import { fr_CH } from "@/keyboardLayouts/fr_CH";
|
||||
import { fr_FR } from "@/keyboardLayouts/fr_FR";
|
||||
import { it_IT } from "@/keyboardLayouts/it_IT";
|
||||
import { nb_NO } from "@/keyboardLayouts/nb_NO";
|
||||
import { sv_SE } from "@/keyboardLayouts/sv_SE";
|
||||
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 isoCode = "cs-CZ";
|
||||
|
||||
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 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 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 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 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 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 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 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 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 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 chars = {
|
||||
A: { key: "KeyA", shift: true },
|
||||
"Ä": { key: "KeyA", shift: true, accentKey: keyTrema },
|
||||
"Á": { key: "KeyA", shift: true, accentKey: keyAcute },
|
||||
"Â": { key: "KeyA", shift: true, accentKey: keyHat },
|
||||
"À": { key: "KeyA", shift: true, accentKey: keyGrave },
|
||||
"Ã": { key: "KeyA", shift: true, accentKey: keyTilde },
|
||||
"Ȧ": { key: "KeyA", shift: true, accentKey: keyOverdot },
|
||||
"Ą": { key: "KeyA", shift: true, accentKey: keyHook },
|
||||
Ä: { key: "KeyA", shift: true, accentKey: keyTrema },
|
||||
Á: { key: "KeyA", shift: true, accentKey: keyAcute },
|
||||
Â: { key: "KeyA", shift: true, accentKey: keyHat },
|
||||
À: { key: "KeyA", shift: true, accentKey: keyGrave },
|
||||
Ã: { key: "KeyA", shift: true, accentKey: keyTilde },
|
||||
Ȧ: { key: "KeyA", shift: true, accentKey: keyOverdot },
|
||||
Ą: { key: "KeyA", shift: true, accentKey: keyHook },
|
||||
B: { key: "KeyB", shift: true },
|
||||
"Ḃ": { key: "KeyB", shift: true, accentKEy: keyOverdot },
|
||||
Ḃ: { key: "KeyB", shift: true, accentKEy: keyOverdot },
|
||||
C: { key: "KeyC", shift: true },
|
||||
"Č": { key: "KeyC", shift: true, accentKey: keyCaron },
|
||||
"Ċ": { key: "KeyC", shift: true, accentKey: keyOverdot },
|
||||
"Ç": { key: "KeyC", shift: true, accentKey: keyCedille },
|
||||
Č: { key: "KeyC", shift: true, accentKey: keyCaron },
|
||||
Ċ: { key: "KeyC", shift: true, accentKey: keyOverdot },
|
||||
Ç: { key: "KeyC", shift: true, accentKey: keyCedille },
|
||||
D: { key: "KeyD", shift: true },
|
||||
"Ď": { key: "KeyD", shift: true, accentKey: keyCaron },
|
||||
"Ḋ": { key: "KeyD", shift: true, accentKey: keyOverdot },
|
||||
Ď: { key: "KeyD", shift: true, accentKey: keyCaron },
|
||||
Ḋ: { key: "KeyD", shift: true, accentKey: keyOverdot },
|
||||
E: { key: "KeyE", shift: true },
|
||||
"Ë": { key: "KeyE", shift: true, accentKey: keyTrema },
|
||||
"É": { key: "KeyE", shift: true, accentKey: keyAcute },
|
||||
"Ê": { key: "KeyE", shift: true, accentKey: keyHat },
|
||||
"Ě": { key: "KeyE", shift: true, accentKey: keyCaron },
|
||||
"È": { key: "KeyE", shift: true, accentKey: keyGrave },
|
||||
"Ẽ": { key: "KeyE", shift: true, accentKey: keyTilde },
|
||||
"Ė": { key: "KeyE", shift: true, accentKEy: keyOverdot },
|
||||
"Ę": { key: "KeyE", shift: true, accentKey: keyHook },
|
||||
Ë: { key: "KeyE", shift: true, accentKey: keyTrema },
|
||||
É: { key: "KeyE", shift: true, accentKey: keyAcute },
|
||||
Ê: { key: "KeyE", shift: true, accentKey: keyHat },
|
||||
Ě: { key: "KeyE", shift: true, accentKey: keyCaron },
|
||||
È: { key: "KeyE", shift: true, accentKey: keyGrave },
|
||||
Ẽ: { key: "KeyE", shift: true, accentKey: keyTilde },
|
||||
Ė: { key: "KeyE", shift: true, accentKEy: keyOverdot },
|
||||
Ę: { key: "KeyE", shift: true, accentKey: keyHook },
|
||||
F: { key: "KeyF", shift: true },
|
||||
"Ḟ": { key: "KeyF", shift: true, accentKey: keyOverdot },
|
||||
Ḟ: { key: "KeyF", shift: true, accentKey: keyOverdot },
|
||||
G: { key: "KeyG", shift: true },
|
||||
"Ġ": { key: "KeyG", shift: true, accentKey: keyOverdot },
|
||||
Ġ: { key: "KeyG", shift: true, accentKey: keyOverdot },
|
||||
H: { key: "KeyH", shift: true },
|
||||
"Ḣ": { key: "KeyH", shift: true, accentKey: keyOverdot },
|
||||
Ḣ: { key: "KeyH", shift: true, accentKey: keyOverdot },
|
||||
I: { key: "KeyI", shift: true },
|
||||
"Ï": { key: "KeyI", shift: true, accentKey: keyTrema },
|
||||
"Í": { key: "KeyI", shift: true, accentKey: keyAcute },
|
||||
"Î": { key: "KeyI", shift: true, accentKey: keyHat },
|
||||
"Ì": { key: "KeyI", shift: true, accentKey: keyGrave },
|
||||
"Ĩ": { key: "KeyI", shift: true, accentKey: keyTilde },
|
||||
"İ": { key: "KeyI", shift: true, accentKey: keyOverdot },
|
||||
"Į": { key: "KeyI", shift: true, accentKey: keyHook },
|
||||
Ï: { key: "KeyI", shift: true, accentKey: keyTrema },
|
||||
Í: { key: "KeyI", shift: true, accentKey: keyAcute },
|
||||
Î: { key: "KeyI", shift: true, accentKey: keyHat },
|
||||
Ì: { key: "KeyI", shift: true, accentKey: keyGrave },
|
||||
Ĩ: { key: "KeyI", shift: true, accentKey: keyTilde },
|
||||
İ: { key: "KeyI", shift: true, accentKey: keyOverdot },
|
||||
Į: { key: "KeyI", shift: true, accentKey: keyHook },
|
||||
J: { key: "KeyJ", shift: true },
|
||||
K: { key: "KeyK", shift: true },
|
||||
L: { key: "KeyL", shift: true },
|
||||
"Ŀ": { key: "KeyL", shift: true },
|
||||
Ŀ: { key: "KeyL", shift: true },
|
||||
M: { key: "KeyM", shift: true },
|
||||
"Ṁ": { key: "KeyM", shift: true },
|
||||
Ṁ: { key: "KeyM", shift: true },
|
||||
N: { key: "KeyN", shift: true },
|
||||
"Ň": { key: "KeyN", shift: true, accentKey: keyCaron },
|
||||
"Ñ": { key: "KeyN", shift: true, accentKey: keyTilde },
|
||||
"Ṅ": { key: "KeyN", shift: true, accentKEy: keyOverdot },
|
||||
Ň: { key: "KeyN", shift: true, accentKey: keyCaron },
|
||||
Ñ: { key: "KeyN", shift: true, accentKey: keyTilde },
|
||||
Ṅ: { key: "KeyN", shift: true, accentKEy: keyOverdot },
|
||||
O: { key: "KeyO", shift: true },
|
||||
"Ö": { key: "KeyO", shift: true, accentKey: keyTrema },
|
||||
"Ó": { key: "KeyO", shift: true, accentKey: keyAcute },
|
||||
"Ô": { key: "KeyO", shift: true, accentKey: keyHat },
|
||||
"Ò": { key: "KeyO", shift: true, accentKey: keyGrave },
|
||||
"Õ": { key: "KeyO", shift: true, accentKey: keyTilde },
|
||||
"Ȯ": { key: "KeyO", shift: true, accentKey: keyOverdot },
|
||||
"Ǫ": { key: "KeyO", shift: true, accentKey: keyHook },
|
||||
Ö: { key: "KeyO", shift: true, accentKey: keyTrema },
|
||||
Ó: { key: "KeyO", shift: true, accentKey: keyAcute },
|
||||
Ô: { key: "KeyO", shift: true, accentKey: keyHat },
|
||||
Ò: { key: "KeyO", shift: true, accentKey: keyGrave },
|
||||
Õ: { key: "KeyO", shift: true, accentKey: keyTilde },
|
||||
Ȯ: { key: "KeyO", shift: true, accentKey: keyOverdot },
|
||||
Ǫ: { key: "KeyO", shift: true, accentKey: keyHook },
|
||||
P: { key: "KeyP", shift: true },
|
||||
"Ṗ": { key: "KeyP", shift: true, accentKey: keyOverdot },
|
||||
Ṗ: { key: "KeyP", shift: true, accentKey: keyOverdot },
|
||||
Q: { key: "KeyQ", shift: true },
|
||||
R: { key: "KeyR", shift: true },
|
||||
"Ř": { key: "KeyR", shift: true, accentKey: keyCaron },
|
||||
"Ṙ": { key: "KeyR", shift: true, accentKey: keyOverdot },
|
||||
Ř: { key: "KeyR", shift: true, accentKey: keyCaron },
|
||||
Ṙ: { key: "KeyR", shift: true, accentKey: keyOverdot },
|
||||
S: { key: "KeyS", shift: true },
|
||||
"Š": { key: "KeyS", shift: true, accentKey: keyCaron },
|
||||
"Ṡ": { key: "KeyS", shift: true, accentKey: keyOverdot },
|
||||
Š: { key: "KeyS", shift: true, accentKey: keyCaron },
|
||||
Ṡ: { key: "KeyS", shift: true, accentKey: keyOverdot },
|
||||
T: { key: "KeyT", shift: true },
|
||||
"Ť": { key: "KeyT", shift: true, accentKey: keyCaron },
|
||||
"Ṫ": { key: "KeyT", shift: true, accentKey: keyOverdot },
|
||||
Ť: { key: "KeyT", shift: true, accentKey: keyCaron },
|
||||
Ṫ: { key: "KeyT", shift: true, accentKey: keyOverdot },
|
||||
U: { key: "KeyU", shift: true },
|
||||
"Ü": { key: "KeyU", shift: true, accentKey: keyTrema },
|
||||
"Ú": { key: "KeyU", shift: true, accentKey: keyAcute },
|
||||
"Û": { key: "KeyU", shift: true, accentKey: keyHat },
|
||||
"Ù": { key: "KeyU", shift: true, accentKey: keyGrave },
|
||||
"Ũ": { key: "KeyU", shift: true, accentKey: keyTilde },
|
||||
"Ů": { key: "KeyU", shift: true, accentKey: keyRing },
|
||||
"Ų": { key: "KeyU", shift: true, accentKey: keyHook },
|
||||
Ü: { key: "KeyU", shift: true, accentKey: keyTrema },
|
||||
Ú: { key: "KeyU", shift: true, accentKey: keyAcute },
|
||||
Û: { key: "KeyU", shift: true, accentKey: keyHat },
|
||||
Ù: { key: "KeyU", shift: true, accentKey: keyGrave },
|
||||
Ũ: { key: "KeyU", shift: true, accentKey: keyTilde },
|
||||
Ů: { key: "KeyU", shift: true, accentKey: keyRing },
|
||||
Ų: { key: "KeyU", shift: true, accentKey: keyHook },
|
||||
V: { key: "KeyV", shift: true },
|
||||
W: { key: "KeyW", shift: true },
|
||||
"Ẇ": { key: "KeyW", shift: true, accentKey: keyOverdot },
|
||||
Ẇ: { key: "KeyW", shift: true, accentKey: keyOverdot },
|
||||
X: { key: "KeyX", shift: true },
|
||||
"Ẋ": { key: "KeyX", shift: true, accentKey: keyOverdot },
|
||||
Ẋ: { key: "KeyX", shift: true, accentKey: keyOverdot },
|
||||
Y: { key: "KeyY", shift: true },
|
||||
"Ý": { key: "KeyY", shift: true, accentKey: keyAcute },
|
||||
"Ẏ": { key: "KeyY", shift: true, accentKey: keyOverdot },
|
||||
Ý: { key: "KeyY", shift: true, accentKey: keyAcute },
|
||||
Ẏ: { key: "KeyY", shift: true, accentKey: keyOverdot },
|
||||
Z: { key: "KeyZ", shift: true },
|
||||
"Ż": { key: "KeyZ", shift: true, accentKey: keyOverdot },
|
||||
Ż: { key: "KeyZ", shift: true, accentKey: keyOverdot },
|
||||
a: { key: "KeyA" },
|
||||
"ä": { key: "KeyA", accentKey: keyTrema },
|
||||
"â": { key: "KeyA", accentKey: keyHat },
|
||||
"à": { key: "KeyA", accentKey: keyGrave },
|
||||
"ã": { key: "KeyA", accentKey: keyTilde },
|
||||
"ȧ": { key: "KeyA", accentKey: keyOverdot },
|
||||
"ą": { key: "KeyA", accentKey: keyHook },
|
||||
ä: { key: "KeyA", accentKey: keyTrema },
|
||||
â: { key: "KeyA", accentKey: keyHat },
|
||||
à: { key: "KeyA", accentKey: keyGrave },
|
||||
ã: { key: "KeyA", accentKey: keyTilde },
|
||||
ȧ: { key: "KeyA", accentKey: keyOverdot },
|
||||
ą: { key: "KeyA", accentKey: keyHook },
|
||||
b: { key: "KeyB" },
|
||||
"{": { key: "KeyB", altRight: true },
|
||||
"ḃ": { key: "KeyB", accentKey: keyOverdot },
|
||||
ḃ: { key: "KeyB", accentKey: keyOverdot },
|
||||
c: { key: "KeyC" },
|
||||
"&": { key: "KeyC", altRight: true },
|
||||
"ç": { key: "KeyC", accentKey: keyCedille },
|
||||
"ċ": { key: "KeyC", accentKey: keyOverdot },
|
||||
ç: { key: "KeyC", accentKey: keyCedille },
|
||||
ċ: { key: "KeyC", accentKey: keyOverdot },
|
||||
d: { key: "KeyD" },
|
||||
"ď": { key: "KeyD", accentKey: keyCaron },
|
||||
"ḋ": { key: "KeyD", accentKey: keyOverdot },
|
||||
"Đ": { key: "KeyD", altRight: true },
|
||||
ď: { key: "KeyD", accentKey: keyCaron },
|
||||
ḋ: { key: "KeyD", accentKey: keyOverdot },
|
||||
Đ: { key: "KeyD", altRight: true },
|
||||
e: { key: "KeyE" },
|
||||
"ë": { key: "KeyE", accentKey: keyTrema },
|
||||
"ê": { key: "KeyE", accentKey: keyHat },
|
||||
"ẽ": { key: "KeyE", accentKey: keyTilde },
|
||||
"è": { key: "KeyE", accentKey: keyGrave },
|
||||
"ė": { key: "KeyE", accentKey: keyOverdot },
|
||||
"ę": { key: "KeyE", accentKey: keyHook },
|
||||
ë: { key: "KeyE", accentKey: keyTrema },
|
||||
ê: { key: "KeyE", accentKey: keyHat },
|
||||
ẽ: { key: "KeyE", accentKey: keyTilde },
|
||||
è: { key: "KeyE", accentKey: keyGrave },
|
||||
ė: { key: "KeyE", accentKey: keyOverdot },
|
||||
ę: { key: "KeyE", accentKey: keyHook },
|
||||
"€": { key: "KeyE", altRight: true },
|
||||
f: { key: "KeyF" },
|
||||
"ḟ": { key: "KeyF", accentKey: keyOverdot },
|
||||
ḟ: { key: "KeyF", accentKey: keyOverdot },
|
||||
"[": { key: "KeyF", altRight: true },
|
||||
g: { key: "KeyG" },
|
||||
"ġ": { key: "KeyG", accentKey: keyOverdot },
|
||||
ġ: { key: "KeyG", accentKey: keyOverdot },
|
||||
"]": { key: "KeyF", altRight: true },
|
||||
h: { key: "KeyH" },
|
||||
"ḣ": { key: "KeyH", accentKey: keyOverdot },
|
||||
ḣ: { key: "KeyH", accentKey: keyOverdot },
|
||||
i: { key: "KeyI" },
|
||||
"ï": { key: "KeyI", accentKey: keyTrema },
|
||||
"î": { key: "KeyI", accentKey: keyHat },
|
||||
"ì": { key: "KeyI", accentKey: keyGrave },
|
||||
"ĩ": { key: "KeyI", accentKey: keyTilde },
|
||||
"ı": { key: "KeyI", accentKey: keyOverdot },
|
||||
"į": { key: "KeyI", accentKey: keyHook },
|
||||
ï: { key: "KeyI", accentKey: keyTrema },
|
||||
î: { key: "KeyI", accentKey: keyHat },
|
||||
ì: { key: "KeyI", accentKey: keyGrave },
|
||||
ĩ: { key: "KeyI", accentKey: keyTilde },
|
||||
ı: { key: "KeyI", accentKey: keyOverdot },
|
||||
į: { key: "KeyI", accentKey: keyHook },
|
||||
j: { key: "KeyJ" },
|
||||
"ȷ": { key: "KeyJ", accentKey: keyOverdot },
|
||||
ȷ: { key: "KeyJ", accentKey: keyOverdot },
|
||||
k: { key: "KeyK" },
|
||||
"ł": { key: "KeyK", altRight: true },
|
||||
ł: { key: "KeyK", altRight: true },
|
||||
l: { key: "KeyL" },
|
||||
"ŀ": { key: "KeyL", accentKey: keyOverdot },
|
||||
"Ł": { key: "KeyL", altRight: true },
|
||||
ŀ: { key: "KeyL", accentKey: keyOverdot },
|
||||
Ł: { key: "KeyL", altRight: true },
|
||||
m: { key: "KeyM" },
|
||||
"ṁ": { key: "KeyM", accentKey: keyOverdot },
|
||||
ṁ: { key: "KeyM", accentKey: keyOverdot },
|
||||
n: { key: "KeyN" },
|
||||
"}": { key: "KeyN", altRight: true },
|
||||
"ň": { key: "KeyN", accentKey: keyCaron },
|
||||
"ñ": { key: "KeyN", accentKey: keyTilde },
|
||||
"ṅ": { key: "KeyN", accentKey: keyOverdot },
|
||||
ň: { key: "KeyN", accentKey: keyCaron },
|
||||
ñ: { key: "KeyN", accentKey: keyTilde },
|
||||
ṅ: { key: "KeyN", accentKey: keyOverdot },
|
||||
o: { key: "KeyO" },
|
||||
"ö": { key: "Key0", accentKey: keyTrema },
|
||||
"ó": { key: "KeyO", accentKey: keyAcute },
|
||||
"ô": { key: "KeyO", accentKey: keyHat },
|
||||
"ò": { key: "KeyO", accentKey: keyGrave },
|
||||
"õ": { key: "KeyO", accentKey: keyTilde },
|
||||
"ȯ": { key: "KeyO", accentKey: keyOverdot },
|
||||
"ǫ": { key: "KeyO", accentKey: keyHook },
|
||||
ö: { key: "Key0", accentKey: keyTrema },
|
||||
ó: { key: "KeyO", accentKey: keyAcute },
|
||||
ô: { key: "KeyO", accentKey: keyHat },
|
||||
ò: { key: "KeyO", accentKey: keyGrave },
|
||||
õ: { key: "KeyO", accentKey: keyTilde },
|
||||
ȯ: { key: "KeyO", accentKey: keyOverdot },
|
||||
ǫ: { key: "KeyO", accentKey: keyHook },
|
||||
p: { key: "KeyP" },
|
||||
"ṗ": { key: "KeyP", accentKey: keyOverdot },
|
||||
ṗ: { key: "KeyP", accentKey: keyOverdot },
|
||||
q: { key: "KeyQ" },
|
||||
r: { key: "KeyR" },
|
||||
"ṙ": { key: "KeyR", accentKey: keyOverdot },
|
||||
ṙ: { key: "KeyR", accentKey: keyOverdot },
|
||||
s: { key: "KeyS" },
|
||||
"ṡ": { key: "KeyS", accentKey: keyOverdot },
|
||||
"đ": { key: "KeyS", altRight: true },
|
||||
ṡ: { key: "KeyS", accentKey: keyOverdot },
|
||||
đ: { key: "KeyS", altRight: true },
|
||||
t: { key: "KeyT" },
|
||||
"ť": { key: "KeyT", accentKey: keyCaron },
|
||||
"ṫ": { key: "KeyT", accentKey: keyOverdot },
|
||||
ť: { key: "KeyT", accentKey: keyCaron },
|
||||
ṫ: { key: "KeyT", accentKey: keyOverdot },
|
||||
u: { key: "KeyU" },
|
||||
"ü": { key: "KeyU", accentKey: keyTrema },
|
||||
"û": { key: "KeyU", accentKey: keyHat },
|
||||
"ù": { key: "KeyU", accentKey: keyGrave },
|
||||
"ũ": { key: "KeyU", accentKey: keyTilde },
|
||||
"ų": { key: "KeyU", accentKey: keyHook },
|
||||
ü: { key: "KeyU", accentKey: keyTrema },
|
||||
û: { key: "KeyU", accentKey: keyHat },
|
||||
ù: { key: "KeyU", accentKey: keyGrave },
|
||||
ũ: { key: "KeyU", accentKey: keyTilde },
|
||||
ų: { key: "KeyU", accentKey: keyHook },
|
||||
v: { key: "KeyV" },
|
||||
"@": { key: "KeyV", altRight: true },
|
||||
w: { key: "KeyW" },
|
||||
"ẇ": { key: "KeyW", accentKey: keyOverdot },
|
||||
ẇ: { key: "KeyW", accentKey: keyOverdot },
|
||||
x: { key: "KeyX" },
|
||||
"#": { key: "KeyX", altRight: true },
|
||||
"ẋ": { key: "KeyX", accentKey: keyOverdot },
|
||||
ẋ: { key: "KeyX", accentKey: keyOverdot },
|
||||
y: { key: "KeyY" },
|
||||
"ẏ": { key: "KeyY", accentKey: keyOverdot },
|
||||
ẏ: { key: "KeyY", accentKey: keyOverdot },
|
||||
z: { key: "KeyZ" },
|
||||
"ż": { key: "KeyZ", accentKey: keyOverdot },
|
||||
ż: { key: "KeyZ", accentKey: keyOverdot },
|
||||
";": { key: "Backquote" },
|
||||
"°": { key: "Backquote", shift: true, deadKey: true },
|
||||
"+": { key: "Digit1" },
|
||||
1: { key: "Digit1", shift: true },
|
||||
"ě": { key: "Digit2" },
|
||||
ě: { key: "Digit2" },
|
||||
2: { key: "Digit2", shift: true },
|
||||
"š": { key: "Digit3" },
|
||||
š: { key: "Digit3" },
|
||||
3: { key: "Digit3", shift: true },
|
||||
"č": { key: "Digit4" },
|
||||
č: { key: "Digit4" },
|
||||
4: { key: "Digit4", shift: true },
|
||||
"ř": { key: "Digit5" },
|
||||
ř: { key: "Digit5" },
|
||||
5: { key: "Digit5", shift: true },
|
||||
"ž": { key: "Digit6" },
|
||||
ž: { key: "Digit6" },
|
||||
6: { key: "Digit6", shift: true },
|
||||
"ý": { key: "Digit7" },
|
||||
ý: { key: "Digit7" },
|
||||
7: { key: "Digit7", shift: true },
|
||||
"á": { key: "Digit8" },
|
||||
á: { key: "Digit8" },
|
||||
8: { key: "Digit8", shift: true },
|
||||
"í": { key: "Digit9" },
|
||||
í: { key: "Digit9" },
|
||||
9: { key: "Digit9", shift: true },
|
||||
"é": { key: "Digit0" },
|
||||
é: { key: "Digit0" },
|
||||
0: { key: "Digit0", shift: true },
|
||||
"=": { key: "Minus" },
|
||||
"%": { key: "Minus", shift: true },
|
||||
"ú": { key: "BracketLeft" },
|
||||
ú: { key: "BracketLeft" },
|
||||
"/": { key: "BracketLeft", shift: true },
|
||||
")": { key: "BracketRight" },
|
||||
"(": { key: "BracketRight", shift: true },
|
||||
"ů": { key: "Semicolon" },
|
||||
"\"": { key: "Semicolon", shift: true },
|
||||
ů: { key: "Semicolon" },
|
||||
'"': { key: "Semicolon", shift: true },
|
||||
"§": { key: "Quote" },
|
||||
"!": { key: "Quote", shift: true },
|
||||
"'": { key: "Backslash", shift: true },
|
||||
|
|
@ -236,7 +236,7 @@ const chars = {
|
|||
":": { key: "Period", shift: true },
|
||||
">": { key: "Period", altRight: true },
|
||||
"-": { key: "Slash" },
|
||||
"_": { key: "Slash", shift: true },
|
||||
_: { key: "Slash", shift: true },
|
||||
"*": { key: "Slash", altRight: true },
|
||||
"\\": { key: "IntlBackslash" },
|
||||
"|": { key: "IntlBackslash", shift: true },
|
||||
|
|
@ -253,5 +253,5 @@ export const cs_CZ: KeyboardLayout = {
|
|||
// TODO need to localize these maps and layouts
|
||||
keyDisplayMap: en_US.keyDisplayMap,
|
||||
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
|
||||
|
||||
export const name = "Dansk";
|
||||
const isoCode = "da-DK";
|
||||
|
||||
const keyTrema = { key: "BracketRight" }
|
||||
const keyAcute = { key: "Equal", altRight: true }
|
||||
const keyHat = { key: "BracketRight", shift: true }
|
||||
const keyGrave = { key: "Equal", shift: true }
|
||||
const keyTilde = { key: "BracketRight", altRight: true }
|
||||
const keyTrema = { key: "BracketRight" };
|
||||
const keyAcute = { key: "Equal", altRight: true };
|
||||
const keyHat = { key: "BracketRight", shift: true };
|
||||
const keyGrave = { key: "Equal", shift: true };
|
||||
const keyTilde = { key: "BracketRight", altRight: true };
|
||||
|
||||
export const chars = {
|
||||
A: { key: "KeyA", shift: true },
|
||||
"Ä": { key: "KeyA", shift: true, accentKey: keyTrema },
|
||||
"Á": { key: "KeyA", shift: true, accentKey: keyAcute },
|
||||
"Â": { key: "KeyA", shift: true, accentKey: keyHat },
|
||||
"À": { key: "KeyA", shift: true, accentKey: keyGrave },
|
||||
"Ã": { key: "KeyA", shift: true, accentKey: keyTilde },
|
||||
Ä: { key: "KeyA", shift: true, accentKey: keyTrema },
|
||||
Á: { key: "KeyA", shift: true, accentKey: keyAcute },
|
||||
Â: { key: "KeyA", shift: true, accentKey: keyHat },
|
||||
À: { key: "KeyA", shift: true, accentKey: keyGrave },
|
||||
Ã: { key: "KeyA", shift: true, accentKey: keyTilde },
|
||||
B: { key: "KeyB", shift: true },
|
||||
C: { key: "KeyC", shift: true },
|
||||
D: { key: "KeyD", shift: true },
|
||||
E: { key: "KeyE", shift: true },
|
||||
"Ë": { key: "KeyE", shift: true, accentKey: keyTrema },
|
||||
"É": { key: "KeyE", shift: true, accentKey: keyAcute },
|
||||
"Ê": { key: "KeyE", shift: true, accentKey: keyHat },
|
||||
"È": { key: "KeyE", shift: true, accentKey: keyGrave },
|
||||
"Ẽ": { key: "KeyE", shift: true, accentKey: keyTilde },
|
||||
Ë: { key: "KeyE", shift: true, accentKey: keyTrema },
|
||||
É: { key: "KeyE", shift: true, accentKey: keyAcute },
|
||||
Ê: { key: "KeyE", shift: true, accentKey: keyHat },
|
||||
È: { key: "KeyE", shift: true, accentKey: keyGrave },
|
||||
Ẽ: { key: "KeyE", shift: true, accentKey: keyTilde },
|
||||
F: { key: "KeyF", shift: true },
|
||||
G: { key: "KeyG", shift: true },
|
||||
H: { key: "KeyH", shift: true },
|
||||
I: { key: "KeyI", shift: true },
|
||||
"Ï": { key: "KeyI", shift: true, accentKey: keyTrema },
|
||||
"Í": { key: "KeyI", shift: true, accentKey: keyAcute },
|
||||
"Î": { key: "KeyI", shift: true, accentKey: keyHat },
|
||||
"Ì": { key: "KeyI", shift: true, accentKey: keyGrave },
|
||||
"Ĩ": { key: "KeyI", shift: true, accentKey: keyTilde },
|
||||
Ï: { key: "KeyI", shift: true, accentKey: keyTrema },
|
||||
Í: { key: "KeyI", shift: true, accentKey: keyAcute },
|
||||
Î: { key: "KeyI", shift: true, accentKey: keyHat },
|
||||
Ì: { key: "KeyI", shift: true, accentKey: keyGrave },
|
||||
Ĩ: { key: "KeyI", shift: true, accentKey: keyTilde },
|
||||
J: { key: "KeyJ", shift: true },
|
||||
K: { key: "KeyK", shift: true },
|
||||
L: { key: "KeyL", shift: true },
|
||||
M: { key: "KeyM", shift: true },
|
||||
N: { key: "KeyN", shift: true },
|
||||
O: { key: "KeyO", shift: true },
|
||||
"Ö": { key: "KeyO", shift: true, accentKey: keyTrema },
|
||||
"Ó": { key: "KeyO", shift: true, accentKey: keyAcute },
|
||||
"Ô": { key: "KeyO", shift: true, accentKey: keyHat },
|
||||
"Ò": { key: "KeyO", shift: true, accentKey: keyGrave },
|
||||
"Õ": { key: "KeyO", shift: true, accentKey: keyTilde },
|
||||
Ö: { key: "KeyO", shift: true, accentKey: keyTrema },
|
||||
Ó: { key: "KeyO", shift: true, accentKey: keyAcute },
|
||||
Ô: { key: "KeyO", shift: true, accentKey: keyHat },
|
||||
Ò: { key: "KeyO", shift: true, accentKey: keyGrave },
|
||||
Õ: { key: "KeyO", shift: true, accentKey: keyTilde },
|
||||
P: { key: "KeyP", shift: true },
|
||||
Q: { key: "KeyQ", shift: true },
|
||||
R: { key: "KeyR", shift: true },
|
||||
S: { key: "KeyS", shift: true },
|
||||
T: { key: "KeyT", shift: true },
|
||||
U: { key: "KeyU", shift: true },
|
||||
"Ü": { key: "KeyU", shift: true, accentKey: keyTrema },
|
||||
"Ú": { key: "KeyU", shift: true, accentKey: keyAcute },
|
||||
"Û": { key: "KeyU", shift: true, accentKey: keyHat },
|
||||
"Ù": { key: "KeyU", shift: true, accentKey: keyGrave },
|
||||
"Ũ": { key: "KeyU", shift: true, accentKey: keyTilde },
|
||||
Ü: { key: "KeyU", shift: true, accentKey: keyTrema },
|
||||
Ú: { key: "KeyU", shift: true, accentKey: keyAcute },
|
||||
Û: { key: "KeyU", shift: true, accentKey: keyHat },
|
||||
Ù: { key: "KeyU", shift: true, accentKey: keyGrave },
|
||||
Ũ: { key: "KeyU", shift: true, accentKey: keyTilde },
|
||||
V: { key: "KeyV", shift: true },
|
||||
W: { key: "KeyW", shift: true },
|
||||
X: { key: "KeyX", shift: true },
|
||||
Y: { key: "KeyY", shift: true },
|
||||
Z: { key: "KeyZ", shift: true },
|
||||
a: { key: "KeyA" },
|
||||
"ä": { key: "KeyA", accentKey: keyTrema },
|
||||
"á": { key: "KeyA", accentKey: keyAcute },
|
||||
"â": { key: "KeyA", accentKey: keyHat },
|
||||
"à": { key: "KeyA", accentKey: keyGrave },
|
||||
"ã": { key: "KeyA", accentKey: keyTilde },
|
||||
ä: { key: "KeyA", accentKey: keyTrema },
|
||||
á: { key: "KeyA", accentKey: keyAcute },
|
||||
â: { key: "KeyA", accentKey: keyHat },
|
||||
à: { key: "KeyA", accentKey: keyGrave },
|
||||
ã: { key: "KeyA", accentKey: keyTilde },
|
||||
b: { key: "KeyB" },
|
||||
c: { key: "KeyC" },
|
||||
d: { key: "KeyD" },
|
||||
e: { key: "KeyE" },
|
||||
"ë": { key: "KeyE", accentKey: keyTrema },
|
||||
"é": { key: "KeyE", accentKey: keyAcute },
|
||||
"ê": { key: "KeyE", accentKey: keyHat },
|
||||
"è": { key: "KeyE", accentKey: keyGrave },
|
||||
"ẽ": { key: "KeyE", accentKey: keyTilde },
|
||||
ë: { key: "KeyE", accentKey: keyTrema },
|
||||
é: { key: "KeyE", accentKey: keyAcute },
|
||||
ê: { key: "KeyE", accentKey: keyHat },
|
||||
è: { key: "KeyE", accentKey: keyGrave },
|
||||
ẽ: { key: "KeyE", accentKey: keyTilde },
|
||||
"€": { key: "KeyE", altRight: true },
|
||||
f: { key: "KeyF" },
|
||||
g: { key: "KeyG" },
|
||||
h: { key: "KeyH" },
|
||||
i: { key: "KeyI" },
|
||||
"ï": { key: "KeyI", accentKey: keyTrema },
|
||||
"í": { key: "KeyI", accentKey: keyAcute },
|
||||
"î": { key: "KeyI", accentKey: keyHat },
|
||||
"ì": { key: "KeyI", accentKey: keyGrave },
|
||||
"ĩ": { key: "KeyI", accentKey: keyTilde },
|
||||
ï: { key: "KeyI", accentKey: keyTrema },
|
||||
í: { key: "KeyI", accentKey: keyAcute },
|
||||
î: { key: "KeyI", accentKey: keyHat },
|
||||
ì: { key: "KeyI", accentKey: keyGrave },
|
||||
ĩ: { key: "KeyI", accentKey: keyTilde },
|
||||
j: { key: "KeyJ" },
|
||||
k: { key: "KeyK" },
|
||||
l: { key: "KeyL" },
|
||||
m: { key: "KeyM" },
|
||||
n: { key: "KeyN" },
|
||||
o: { key: "KeyO" },
|
||||
"ö": { key: "KeyO", accentKey: keyTrema },
|
||||
"ó": { key: "KeyO", accentKey: keyAcute },
|
||||
"ô": { key: "KeyO", accentKey: keyHat },
|
||||
"ò": { key: "KeyO", accentKey: keyGrave },
|
||||
"õ": { key: "KeyO", accentKey: keyTilde },
|
||||
ö: { key: "KeyO", accentKey: keyTrema },
|
||||
ó: { key: "KeyO", accentKey: keyAcute },
|
||||
ô: { key: "KeyO", accentKey: keyHat },
|
||||
ò: { key: "KeyO", accentKey: keyGrave },
|
||||
õ: { key: "KeyO", accentKey: keyTilde },
|
||||
p: { key: "KeyP" },
|
||||
q: { key: "KeyQ" },
|
||||
r: { key: "KeyR" },
|
||||
s: { key: "KeyS" },
|
||||
t: { key: "KeyT" },
|
||||
u: { key: "KeyU" },
|
||||
"ü": { key: "KeyU", accentKey: keyTrema },
|
||||
"ú": { key: "KeyU", accentKey: keyAcute },
|
||||
"û": { key: "KeyU", accentKey: keyHat },
|
||||
"ù": { key: "KeyU", accentKey: keyGrave },
|
||||
"ũ": { key: "KeyU", accentKey: keyTilde },
|
||||
ü: { key: "KeyU", accentKey: keyTrema },
|
||||
ú: { key: "KeyU", accentKey: keyAcute },
|
||||
û: { key: "KeyU", accentKey: keyHat },
|
||||
ù: { key: "KeyU", accentKey: keyGrave },
|
||||
ũ: { key: "KeyU", accentKey: keyTilde },
|
||||
v: { key: "KeyV" },
|
||||
w: { key: "KeyW" },
|
||||
x: { key: "KeyX" },
|
||||
|
|
@ -120,14 +120,14 @@ export const chars = {
|
|||
1: { key: "Digit1" },
|
||||
"!": { key: "Digit1", shift: true },
|
||||
2: { key: "Digit2" },
|
||||
"\"": { key: "Digit2", shift: true },
|
||||
'"': { key: "Digit2", shift: true },
|
||||
"@": { key: "Digit2", altRight: true },
|
||||
3: { key: "Digit3" },
|
||||
"#": { key: "Digit3", shift: true },
|
||||
"£": { key: "Digit3", altRight: true },
|
||||
4: { key: "Digit4" },
|
||||
"¤": { key: "Digit4", shift: true },
|
||||
"$": { key: "Digit4", altRight: true },
|
||||
$: { key: "Digit4", altRight: true },
|
||||
5: { key: "Digit5" },
|
||||
"%": { key: "Digit5", shift: true },
|
||||
6: { key: "Digit6" },
|
||||
|
|
@ -147,12 +147,12 @@ export const chars = {
|
|||
"+": { key: "Minus" },
|
||||
"?": { key: "Minus", shift: true },
|
||||
"\\": { key: "Equal" },
|
||||
"å": { key: "BracketLeft" },
|
||||
"Å": { key: "BracketLeft", shift: true },
|
||||
"ø": { key: "Semicolon" },
|
||||
"Ø": { key: "Semicolon", shift: true },
|
||||
"æ": { key: "Quote" },
|
||||
"Æ": { key: "Quote", shift: true },
|
||||
å: { key: "BracketLeft" },
|
||||
Å: { key: "BracketLeft", shift: true },
|
||||
ø: { key: "Semicolon" },
|
||||
Ø: { key: "Semicolon", shift: true },
|
||||
æ: { key: "Quote" },
|
||||
Æ: { key: "Quote", shift: true },
|
||||
"'": { key: "Backslash" },
|
||||
"*": { key: "Backslash", shift: true },
|
||||
",": { key: "Comma" },
|
||||
|
|
@ -160,20 +160,19 @@ export const chars = {
|
|||
".": { key: "Period" },
|
||||
":": { key: "Period", shift: true },
|
||||
"-": { key: "Slash" },
|
||||
"_": { key: "Slash", shift: true },
|
||||
_: { key: "Slash", shift: true },
|
||||
"<": { key: "IntlBackslash" },
|
||||
">": { key: "IntlBackslash", shift: true },
|
||||
"~": { key: "BracketRight", deadKey: true, altRight: 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, shift: true, },
|
||||
"´": { key: "Equal", deadKey: true, },
|
||||
"`": { key: "Equal", deadKey: true, shift: true },
|
||||
"´": { key: "Equal", deadKey: true },
|
||||
" ": { key: "Space" },
|
||||
"\n": { key: "Enter" },
|
||||
Enter: { key: "Enter" },
|
||||
Tab: { key: "Tab" },
|
||||
|
||||
} as Record<string, KeyCombo>;
|
||||
|
||||
export const da_DK: KeyboardLayout = {
|
||||
|
|
@ -183,5 +182,5 @@ export const da_DK: KeyboardLayout = {
|
|||
// TODO need to localize these maps and layouts
|
||||
keyDisplayMap: en_US.keyDisplayMap,
|
||||
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 isoCode = "de-CH";
|
||||
|
||||
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 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 keyTilde: KeyCombo = { key: "Equal", altRight: true } // tilde, mark ~ placed above the letter
|
||||
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 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 keyTilde: KeyCombo = { key: "Equal", altRight: true }; // tilde, mark ~ placed above the letter
|
||||
|
||||
const chars = {
|
||||
A: { key: "KeyA", shift: true },
|
||||
"Ä": { key: "KeyA", shift: true, accentKey: keyTrema },
|
||||
"Á": { key: "KeyA", shift: true, accentKey: keyAcute },
|
||||
"Â": { key: "KeyA", shift: true, accentKey: keyHat },
|
||||
"À": { key: "KeyA", shift: true, accentKey: keyGrave },
|
||||
"Ã": { key: "KeyA", shift: true, accentKey: keyTilde },
|
||||
Ä: { key: "KeyA", shift: true, accentKey: keyTrema },
|
||||
Á: { key: "KeyA", shift: true, accentKey: keyAcute },
|
||||
Â: { key: "KeyA", shift: true, accentKey: keyHat },
|
||||
À: { key: "KeyA", shift: true, accentKey: keyGrave },
|
||||
Ã: { key: "KeyA", shift: true, accentKey: keyTilde },
|
||||
B: { key: "KeyB", shift: true },
|
||||
C: { key: "KeyC", shift: true },
|
||||
D: { key: "KeyD", shift: true },
|
||||
E: { key: "KeyE", shift: true },
|
||||
"Ë": { key: "KeyE", shift: true, accentKey: keyTrema },
|
||||
"É": { key: "KeyE", shift: true, accentKey: keyAcute },
|
||||
"Ê": { key: "KeyE", shift: true, accentKey: keyHat },
|
||||
"È": { key: "KeyE", shift: true, accentKey: keyGrave },
|
||||
"Ẽ": { key: "KeyE", shift: true, accentKey: keyTilde },
|
||||
Ë: { key: "KeyE", shift: true, accentKey: keyTrema },
|
||||
É: { key: "KeyE", shift: true, accentKey: keyAcute },
|
||||
Ê: { key: "KeyE", shift: true, accentKey: keyHat },
|
||||
È: { key: "KeyE", shift: true, accentKey: keyGrave },
|
||||
Ẽ: { key: "KeyE", shift: true, accentKey: keyTilde },
|
||||
F: { key: "KeyF", shift: true },
|
||||
G: { key: "KeyG", shift: true },
|
||||
H: { key: "KeyH", shift: true },
|
||||
I: { key: "KeyI", shift: true },
|
||||
"Ï": { key: "KeyI", shift: true, accentKey: keyTrema },
|
||||
"Í": { key: "KeyI", shift: true, accentKey: keyAcute },
|
||||
"Î": { key: "KeyI", shift: true, accentKey: keyHat },
|
||||
"Ì": { key: "KeyI", shift: true, accentKey: keyGrave },
|
||||
"Ĩ": { key: "KeyI", shift: true, accentKey: keyTilde },
|
||||
Ï: { key: "KeyI", shift: true, accentKey: keyTrema },
|
||||
Í: { key: "KeyI", shift: true, accentKey: keyAcute },
|
||||
Î: { key: "KeyI", shift: true, accentKey: keyHat },
|
||||
Ì: { key: "KeyI", shift: true, accentKey: keyGrave },
|
||||
Ĩ: { key: "KeyI", shift: true, accentKey: keyTilde },
|
||||
J: { key: "KeyJ", shift: true },
|
||||
K: { key: "KeyK", shift: true },
|
||||
L: { key: "KeyL", shift: true },
|
||||
M: { key: "KeyM", shift: true },
|
||||
N: { key: "KeyN", shift: true },
|
||||
O: { key: "KeyO", shift: true },
|
||||
"Ö": { key: "KeyO", shift: true, accentKey: keyTrema },
|
||||
"Ó": { key: "KeyO", shift: true, accentKey: keyAcute },
|
||||
"Ô": { key: "KeyO", shift: true, accentKey: keyHat },
|
||||
"Ò": { key: "KeyO", shift: true, accentKey: keyGrave },
|
||||
"Õ": { key: "KeyO", shift: true, accentKey: keyTilde },
|
||||
Ö: { key: "KeyO", shift: true, accentKey: keyTrema },
|
||||
Ó: { key: "KeyO", shift: true, accentKey: keyAcute },
|
||||
Ô: { key: "KeyO", shift: true, accentKey: keyHat },
|
||||
Ò: { key: "KeyO", shift: true, accentKey: keyGrave },
|
||||
Õ: { key: "KeyO", shift: true, accentKey: keyTilde },
|
||||
P: { key: "KeyP", shift: true },
|
||||
Q: { key: "KeyQ", shift: true },
|
||||
R: { key: "KeyR", shift: true },
|
||||
S: { key: "KeyS", shift: true },
|
||||
T: { key: "KeyT", shift: true },
|
||||
U: { key: "KeyU", shift: true },
|
||||
"Ü": { key: "KeyU", shift: true, accentKey: keyTrema },
|
||||
"Ú": { key: "KeyU", shift: true, accentKey: keyAcute },
|
||||
"Û": { key: "KeyU", shift: true, accentKey: keyHat },
|
||||
"Ù": { key: "KeyU", shift: true, accentKey: keyGrave },
|
||||
"Ũ": { key: "KeyU", shift: true, accentKey: keyTilde },
|
||||
Ü: { key: "KeyU", shift: true, accentKey: keyTrema },
|
||||
Ú: { key: "KeyU", shift: true, accentKey: keyAcute },
|
||||
Û: { key: "KeyU", shift: true, accentKey: keyHat },
|
||||
Ù: { key: "KeyU", shift: true, accentKey: keyGrave },
|
||||
Ũ: { key: "KeyU", shift: true, accentKey: keyTilde },
|
||||
V: { key: "KeyV", shift: true },
|
||||
W: { key: "KeyW", shift: true },
|
||||
X: { key: "KeyX", shift: true },
|
||||
Y: { key: "KeyZ", shift: true },
|
||||
Z: { key: "KeyY", shift: true },
|
||||
a: { key: "KeyA" },
|
||||
"á": { key: "KeyA", accentKey: keyAcute },
|
||||
"â": { key: "KeyA", accentKey: keyHat },
|
||||
"ã": { key: "KeyA", accentKey: keyTilde },
|
||||
á: { key: "KeyA", accentKey: keyAcute },
|
||||
â: { key: "KeyA", accentKey: keyHat },
|
||||
ã: { key: "KeyA", accentKey: keyTilde },
|
||||
b: { key: "KeyB" },
|
||||
c: { key: "KeyC" },
|
||||
d: { key: "KeyD" },
|
||||
e: { key: "KeyE" },
|
||||
"ë": { key: "KeyE", accentKey: keyTrema },
|
||||
"ê": { key: "KeyE", accentKey: keyHat },
|
||||
"ẽ": { key: "KeyE", accentKey: keyTilde },
|
||||
ë: { key: "KeyE", accentKey: keyTrema },
|
||||
ê: { key: "KeyE", accentKey: keyHat },
|
||||
ẽ: { key: "KeyE", accentKey: keyTilde },
|
||||
"€": { key: "KeyE", altRight: true },
|
||||
f: { key: "KeyF" },
|
||||
g: { key: "KeyG" },
|
||||
h: { key: "KeyH" },
|
||||
i: { key: "KeyI" },
|
||||
"ï": { key: "KeyI", accentKey: keyTrema },
|
||||
"í": { key: "KeyI", accentKey: keyAcute },
|
||||
"î": { key: "KeyI", accentKey: keyHat },
|
||||
"ì": { key: "KeyI", accentKey: keyGrave },
|
||||
"ĩ": { key: "KeyI", accentKey: keyTilde },
|
||||
ï: { key: "KeyI", accentKey: keyTrema },
|
||||
í: { key: "KeyI", accentKey: keyAcute },
|
||||
î: { key: "KeyI", accentKey: keyHat },
|
||||
ì: { key: "KeyI", accentKey: keyGrave },
|
||||
ĩ: { key: "KeyI", accentKey: keyTilde },
|
||||
j: { key: "KeyJ" },
|
||||
k: { key: "KeyK" },
|
||||
l: { key: "KeyL" },
|
||||
m: { key: "KeyM" },
|
||||
n: { key: "KeyN" },
|
||||
o: { key: "KeyO" },
|
||||
"ó": { key: "KeyO", accentKey: keyAcute },
|
||||
"ô": { key: "KeyO", accentKey: keyHat },
|
||||
"ò": { key: "KeyO", accentKey: keyGrave },
|
||||
"õ": { key: "KeyO", accentKey: keyTilde },
|
||||
ó: { key: "KeyO", accentKey: keyAcute },
|
||||
ô: { key: "KeyO", accentKey: keyHat },
|
||||
ò: { key: "KeyO", accentKey: keyGrave },
|
||||
õ: { key: "KeyO", accentKey: keyTilde },
|
||||
p: { key: "KeyP" },
|
||||
q: { key: "KeyQ" },
|
||||
r: { key: "KeyR" },
|
||||
s: { key: "KeyS" },
|
||||
t: { key: "KeyT" },
|
||||
u: { key: "KeyU" },
|
||||
"ú": { key: "KeyU", accentKey: keyAcute },
|
||||
"û": { key: "KeyU", accentKey: keyHat },
|
||||
"ù": { key: "KeyU", accentKey: keyGrave },
|
||||
"ũ": { key: "KeyU", accentKey: keyTilde },
|
||||
ú: { key: "KeyU", accentKey: keyAcute },
|
||||
û: { key: "KeyU", accentKey: keyHat },
|
||||
ù: { key: "KeyU", accentKey: keyGrave },
|
||||
ũ: { key: "KeyU", accentKey: keyTilde },
|
||||
v: { key: "KeyV" },
|
||||
w: { key: "KeyW" },
|
||||
x: { key: "KeyX" },
|
||||
|
|
@ -115,13 +115,13 @@ const chars = {
|
|||
"+": { key: "Digit1", shift: true },
|
||||
"|": { key: "Digit1", altRight: true },
|
||||
2: { key: "Digit2" },
|
||||
"\"": { key: "Digit2", shift: true },
|
||||
'"': { key: "Digit2", shift: true },
|
||||
"@": { key: "Digit2", altRight: true },
|
||||
3: { key: "Digit3" },
|
||||
"*": { key: "Digit3", shift: true },
|
||||
"#": { key: "Digit3", altRight: true },
|
||||
4: { key: "Digit4" },
|
||||
"ç": { key: "Digit4", shift: true },
|
||||
ç: { key: "Digit4", shift: true },
|
||||
5: { key: "Digit5" },
|
||||
"%": { key: "Digit5", shift: true },
|
||||
6: { key: "Digit6" },
|
||||
|
|
@ -139,17 +139,17 @@ const chars = {
|
|||
"^": { key: "Equal", deadKey: true },
|
||||
"`": { key: "Equal", shift: true },
|
||||
"~": { key: "Equal", altRight: true, deadKey: true },
|
||||
"ü": { key: "BracketLeft" },
|
||||
"è": { key: "BracketLeft", shift: true },
|
||||
ü: { key: "BracketLeft" },
|
||||
è: { key: "BracketLeft", shift: true },
|
||||
"[": { key: "BracketLeft", altRight: true },
|
||||
"!": { key: "BracketRight", shift: true },
|
||||
"]": { key: "BracketRight", altRight: true },
|
||||
"ö": { key: "Semicolon" },
|
||||
"é": { key: "Semicolon", shift: true },
|
||||
"ä": { key: "Quote" },
|
||||
"à": { key: "Quote", shift: true },
|
||||
ö: { key: "Semicolon" },
|
||||
é: { key: "Semicolon", shift: true },
|
||||
ä: { key: "Quote" },
|
||||
à: { key: "Quote", shift: true },
|
||||
"{": { key: "Quote", altRight: true },
|
||||
"$": { key: "Backslash" },
|
||||
$: { key: "Backslash" },
|
||||
"£": { key: "Backslash", shift: true },
|
||||
"}": { key: "Backslash", altRight: true },
|
||||
",": { key: "Comma" },
|
||||
|
|
@ -157,7 +157,7 @@ const chars = {
|
|||
".": { key: "Period" },
|
||||
":": { key: "Period", shift: true },
|
||||
"-": { key: "Slash" },
|
||||
"_": { key: "Slash", shift: true },
|
||||
_: { key: "Slash", shift: true },
|
||||
"<": { key: "IntlBackslash" },
|
||||
">": { key: "IntlBackslash", shift: true },
|
||||
"\\": { key: "IntlBackslash", altRight: true },
|
||||
|
|
@ -184,5 +184,5 @@ export const de_CH: KeyboardLayout = {
|
|||
keyDisplayMap: keyDisplayMap,
|
||||
// TODO need to localize these maps and layouts
|
||||
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 isoCode = "de-DE";
|
||||
|
||||
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 keyGrave: KeyCombo = { key: "Equal", shift: true } // accent grave, 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 keyGrave: KeyCombo = { key: "Equal", shift: true }; // accent grave, mark ` placed above the letter
|
||||
|
||||
const chars = {
|
||||
a: { key: "KeyA" },
|
||||
"á": { key: "KeyA", accentKey: keyAcute },
|
||||
"â": { key: "KeyA", accentKey: keyHat },
|
||||
"à": { key: "KeyA", accentKey: keyGrave },
|
||||
á: { key: "KeyA", accentKey: keyAcute },
|
||||
â: { key: "KeyA", accentKey: keyHat },
|
||||
à: { key: "KeyA", accentKey: keyGrave },
|
||||
A: { key: "KeyA", shift: true },
|
||||
"Á": { key: "KeyA", shift: true, accentKey: keyAcute },
|
||||
"Â": { key: "KeyA", shift: true, accentKey: keyHat },
|
||||
"À": { key: "KeyA", shift: true, accentKey: keyGrave },
|
||||
Á: { key: "KeyA", shift: true, accentKey: keyAcute },
|
||||
Â: { key: "KeyA", shift: true, accentKey: keyHat },
|
||||
À: { key: "KeyA", shift: true, accentKey: keyGrave },
|
||||
"☺": { key: "KeyA", altRight: true }, // white smiling face ☺
|
||||
b: { key: "KeyB" },
|
||||
B: { key: "KeyB", shift: true },
|
||||
|
|
@ -29,31 +29,31 @@ const chars = {
|
|||
D: { key: "KeyD", shift: true },
|
||||
"′": { key: "KeyD", altRight: true }, // prime, mark ′ placed above the letter
|
||||
e: { key: "KeyE" },
|
||||
"é": { key: "KeyE", accentKey: keyAcute },
|
||||
"ê": { key: "KeyE", accentKey: keyHat },
|
||||
"è": { key: "KeyE", accentKey: keyGrave },
|
||||
é: { key: "KeyE", accentKey: keyAcute },
|
||||
ê: { key: "KeyE", accentKey: keyHat },
|
||||
è: { key: "KeyE", accentKey: keyGrave },
|
||||
"€": { key: "KeyE", altRight: true },
|
||||
E: { key: "KeyE", shift: true },
|
||||
"É": { key: "KeyE", shift: true, accentKey: keyAcute },
|
||||
"Ê": { key: "KeyE", shift: true, accentKey: keyHat },
|
||||
"È": { key: "KeyE", shift: true, accentKey: keyGrave },
|
||||
É: { key: "KeyE", shift: true, accentKey: keyAcute },
|
||||
Ê: { key: "KeyE", shift: true, accentKey: keyHat },
|
||||
È: { key: "KeyE", shift: true, accentKey: keyGrave },
|
||||
f: { key: "KeyF" },
|
||||
F: { key: "KeyF", shift: true },
|
||||
"˟": { key: "KeyF", deadKey: true, altRight: true }, // modifier letter cross accent, ˟
|
||||
G: { key: "KeyG", shift: true },
|
||||
g: { key: "KeyG" },
|
||||
"ẞ": { key: "KeyG", altRight: true }, // capital sharp S, ẞ
|
||||
ẞ: { key: "KeyG", altRight: true }, // capital sharp S, ẞ
|
||||
h: { key: "KeyH" },
|
||||
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" },
|
||||
"í": { key: "KeyI", accentKey: keyAcute },
|
||||
"î": { key: "KeyI", accentKey: keyHat },
|
||||
"ì": { key: "KeyI", accentKey: keyGrave },
|
||||
í: { key: "KeyI", accentKey: keyAcute },
|
||||
î: { key: "KeyI", accentKey: keyHat },
|
||||
ì: { key: "KeyI", accentKey: keyGrave },
|
||||
I: { key: "KeyI", shift: true },
|
||||
"Í": { key: "KeyI", shift: true, accentKey: keyAcute },
|
||||
"Î": { key: "KeyI", shift: true, accentKey: keyHat },
|
||||
"Ì": { key: "KeyI", shift: true, accentKey: keyGrave },
|
||||
Í: { key: "KeyI", shift: true, accentKey: keyAcute },
|
||||
Î: { key: "KeyI", shift: true, accentKey: keyHat },
|
||||
Ì: { key: "KeyI", shift: true, accentKey: keyGrave },
|
||||
"˜": { key: "KeyI", deadKey: true, altRight: true }, // tilde accent, mark ˜ placed above the letter
|
||||
j: { key: "KeyJ" },
|
||||
J: { key: "KeyJ", shift: true },
|
||||
|
|
@ -62,25 +62,25 @@ const chars = {
|
|||
K: { key: "KeyK", shift: true },
|
||||
l: { key: "KeyL" },
|
||||
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", shift: true },
|
||||
"µ": { key: "KeyM", altRight: true },
|
||||
µ: { key: "KeyM", altRight: true },
|
||||
n: { key: "KeyN" },
|
||||
N: { key: "KeyN", shift: true },
|
||||
"–": { key: "KeyN", altRight: true }, // en dash, –
|
||||
o: { key: "KeyO" },
|
||||
"ó": { key: "KeyO", accentKey: keyAcute },
|
||||
"ô": { key: "KeyO", accentKey: keyHat },
|
||||
"ò": { key: "KeyO", accentKey: keyGrave },
|
||||
ó: { key: "KeyO", accentKey: keyAcute },
|
||||
ô: { key: "KeyO", accentKey: keyHat },
|
||||
ò: { key: "KeyO", accentKey: keyGrave },
|
||||
O: { key: "KeyO", shift: true },
|
||||
"Ó": { key: "KeyO", shift: true, accentKey: keyAcute },
|
||||
"Ô": { key: "KeyO", shift: true, accentKey: keyHat },
|
||||
"Ò": { key: "KeyO", shift: true, accentKey: keyGrave },
|
||||
Ó: { key: "KeyO", shift: true, accentKey: keyAcute },
|
||||
Ô: { key: "KeyO", shift: true, accentKey: keyHat },
|
||||
Ò: { key: "KeyO", shift: true, accentKey: keyGrave },
|
||||
"˚": { key: "KeyO", deadKey: true, altRight: true }, // ring above, ˚
|
||||
p: { key: "KeyP" },
|
||||
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", shift: true },
|
||||
"@": { key: "KeyQ", altRight: true },
|
||||
|
|
@ -92,15 +92,15 @@ const chars = {
|
|||
"″": { key: "KeyS", altRight: true }, // double prime, mark ″ placed above the letter
|
||||
T: { key: "KeyT", shift: true },
|
||||
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" },
|
||||
"ú": { key: "KeyU", accentKey: keyAcute },
|
||||
"û": { key: "KeyU", accentKey: keyHat },
|
||||
"ù": { key: "KeyU", accentKey: keyGrave },
|
||||
ú: { key: "KeyU", accentKey: keyAcute },
|
||||
û: { key: "KeyU", accentKey: keyHat },
|
||||
ù: { key: "KeyU", accentKey: keyGrave },
|
||||
U: { key: "KeyU", shift: true },
|
||||
"Ú": { key: "KeyU", shift: true, accentKey: keyAcute },
|
||||
"Û": { key: "KeyU", shift: true, accentKey: keyHat },
|
||||
"Ù": { key: "KeyU", shift: true, accentKey: keyGrave },
|
||||
Ú: { key: "KeyU", shift: true, accentKey: keyAcute },
|
||||
Û: { key: "KeyU", shift: true, accentKey: keyHat },
|
||||
Ù: { key: "KeyU", shift: true, accentKey: keyGrave },
|
||||
"˘": { key: "KeyU", deadKey: true, altRight: true }, // breve accent, ˘ placed above the letter
|
||||
v: { key: "KeyV" },
|
||||
V: { key: "KeyV", shift: true },
|
||||
|
|
@ -125,7 +125,7 @@ const chars = {
|
|||
"!": { key: "Digit1", shift: true },
|
||||
"’": { key: "Digit1", altRight: true }, // single quote, mark ’ placed above the letter
|
||||
2: { key: "Digit2" },
|
||||
"\"": { key: "Digit2", shift: true },
|
||||
'"': { key: "Digit2", shift: true },
|
||||
"²": { key: "Digit2", altRight: true },
|
||||
"<": { key: "Digit2", altRight: true }, // non-US < and >
|
||||
3: { key: "Digit3" },
|
||||
|
|
@ -133,7 +133,7 @@ const chars = {
|
|||
"³": { key: "Digit3", altRight: true },
|
||||
">": { key: "Digit3", altRight: true }, // non-US < and >
|
||||
4: { key: "Digit4" },
|
||||
"$": { key: "Digit4", shift: true },
|
||||
$: { key: "Digit4", shift: true },
|
||||
"—": { key: "Digit4", altRight: true }, // em dash, —
|
||||
5: { key: "Digit5" },
|
||||
"%": { key: "Digit5", shift: true },
|
||||
|
|
@ -153,25 +153,25 @@ const chars = {
|
|||
0: { key: "Digit0" },
|
||||
"=": { key: "Digit0", shift: true },
|
||||
"}": { key: "Digit0", altRight: true },
|
||||
"ß": { key: "Minus" },
|
||||
ß: { key: "Minus" },
|
||||
"?": { key: "Minus", shift: true },
|
||||
"\\": { key: "Minus", altRight: true },
|
||||
"´": { 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", control: true, altRight: true, deadKey: true }, // acute accent, mark ˙ placed above the letter
|
||||
"ü": { key: "BracketLeft" },
|
||||
"Ü": { key: "BracketLeft", shift: true },
|
||||
ü: { key: "BracketLeft" },
|
||||
Ü: { key: "BracketLeft", shift: true },
|
||||
Escape: { key: "BracketLeft", control: true },
|
||||
"ʼ": { key: "BracketLeft", altRight: true }, // modifier letter apostrophe, ʼ
|
||||
ʼ: { key: "BracketLeft", altRight: true }, // modifier letter apostrophe, ʼ
|
||||
"+": { key: "BracketRight" },
|
||||
"*": { key: "BracketRight", shift: true },
|
||||
Control: { key: "BracketRight", control: true },
|
||||
"~": { key: "BracketRight", altRight: true },
|
||||
"ö": { key: "Semicolon" },
|
||||
"Ö": { key: "Semicolon", shift: true },
|
||||
"ˌ": { key: "Semicolon", deadkey: true, altRight: true }, // modifier letter low vertical line, ˌ
|
||||
"ä": { key: "Quote" },
|
||||
"Ä": { key: "Quote", shift: true },
|
||||
ö: { key: "Semicolon" },
|
||||
Ö: { key: "Semicolon", shift: true },
|
||||
ˌ: { key: "Semicolon", deadkey: true, altRight: true }, // modifier letter low vertical line, ˌ
|
||||
ä: { key: "Quote" },
|
||||
Ä: { key: "Quote", shift: true },
|
||||
"˗": { key: "Quote", deadKey: true, altRight: true }, // modifier letter minus sign, ˗
|
||||
"#": { key: "Backslash" },
|
||||
"'": { key: "Backslash", shift: true },
|
||||
|
|
@ -183,7 +183,7 @@ const chars = {
|
|||
":": { key: "Period", shift: true },
|
||||
"·": { key: "Period", altRight: true }, // middle dot, ·
|
||||
"-": { key: "Slash" },
|
||||
"_": { key: "Slash", shift: true },
|
||||
_: { key: "Slash", shift: true },
|
||||
"\u00ad": { key: "Slash", altRight: true }, // soft hyphen,
|
||||
" ": { key: "Space" },
|
||||
"\n": { key: "Enter" },
|
||||
|
|
@ -237,7 +237,7 @@ export const keyDisplayMap: Record<string, string> = {
|
|||
"(Backslash)": "'",
|
||||
|
||||
// Shifted Numbers
|
||||
"(Digit2)": "\"",
|
||||
"(Digit2)": '"',
|
||||
"(Digit3)": "§",
|
||||
"(Digit6)": "&",
|
||||
"(Digit7)": "/",
|
||||
|
|
@ -265,7 +265,7 @@ export const keyDisplayMap: Record<string, string> = {
|
|||
PrintScreen: "Druck",
|
||||
ScrollLock: "Rollen",
|
||||
"(Pause)": "Unterbr",
|
||||
}
|
||||
};
|
||||
|
||||
export const modifierDisplayMap: Record<string, string> = {
|
||||
ShiftLeft: "Umschalt (links)",
|
||||
|
|
@ -298,25 +298,15 @@ export const virtualKeyboard = {
|
|||
"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",
|
||||
"ControlLeft MetaLeft AltLeft Space AltGr MetaRight Menu ControlRight",
|
||||
]
|
||||
],
|
||||
},
|
||||
control: {
|
||||
default: [
|
||||
"PrintScreen ScrollLock Pause",
|
||||
"Insert Home PageUp",
|
||||
"Delete End PageDown"
|
||||
],
|
||||
shift: [
|
||||
"(PrintScreen) ScrollLock (Pause)",
|
||||
"Insert Home PageUp",
|
||||
"Delete End PageDown"
|
||||
],
|
||||
default: ["PrintScreen ScrollLock Pause", "Insert Home PageUp", "Delete End PageDown"],
|
||||
shift: ["(PrintScreen) ScrollLock (Pause)", "Insert Home PageUp", "Delete End PageDown"],
|
||||
},
|
||||
|
||||
arrows: {
|
||||
default: [
|
||||
" ArrowUp ",
|
||||
"ArrowLeft ArrowDown ArrowRight"],
|
||||
default: [" ArrowUp ", "ArrowLeft ArrowDown ArrowRight"],
|
||||
},
|
||||
|
||||
numpad: {
|
||||
|
|
@ -334,8 +324,8 @@ export const virtualKeyboard = {
|
|||
"End ArrowDown PageDown NumpadEnter",
|
||||
"NumpadInsert NumpadDelete",
|
||||
],
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
export const de_DE: KeyboardLayout = {
|
||||
isoCode: isoCode,
|
||||
|
|
@ -343,5 +333,5 @@ export const de_DE: KeyboardLayout = {
|
|||
chars: chars,
|
||||
keyDisplayMap: keyDisplayMap,
|
||||
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 isoCode = "en-UK";
|
||||
|
|
@ -61,7 +61,7 @@ const chars = {
|
|||
1: { key: "Digit1" },
|
||||
"!": { key: "Digit1", shift: true },
|
||||
2: { key: "Digit2" },
|
||||
"\"": { key: "Digit2", shift: true },
|
||||
'"': { key: "Digit2", shift: true },
|
||||
3: { key: "Digit3" },
|
||||
"£": { key: "Digit3", shift: true },
|
||||
4: { key: "Digit4" },
|
||||
|
|
@ -84,7 +84,7 @@ const chars = {
|
|||
"=": { key: "Equal" },
|
||||
"+": { key: "Equal", shift: true },
|
||||
"'": { key: "Quote" },
|
||||
'@': { key: "Quote", shift: true },
|
||||
"@": { key: "Quote", shift: true },
|
||||
",": { key: "Comma" },
|
||||
"<": { key: "Comma", shift: true },
|
||||
"/": { key: "Slash" },
|
||||
|
|
@ -107,7 +107,7 @@ const chars = {
|
|||
"\n": { key: "Enter" },
|
||||
Enter: { key: "Enter" },
|
||||
Tab: { key: "Tab" },
|
||||
} as Record<string, KeyCombo>
|
||||
} as Record<string, KeyCombo>;
|
||||
|
||||
export const en_UK: KeyboardLayout = {
|
||||
isoCode: isoCode,
|
||||
|
|
@ -116,5 +116,5 @@ export const en_UK: KeyboardLayout = {
|
|||
// TODO need to localize these maps and layouts
|
||||
keyDisplayMap: en_US.keyDisplayMap,
|
||||
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 isoCode = "en-US";
|
||||
|
|
@ -125,7 +125,7 @@ export const chars = {
|
|||
Break: { key: "Pause", shift: true },
|
||||
Insert: { key: "Insert" },
|
||||
Delete: { key: "Delete" },
|
||||
} as Record<string, KeyCombo>
|
||||
} as Record<string, KeyCombo>;
|
||||
|
||||
export const modifierDisplayMap: Record<string, string> = {
|
||||
ControlLeft: "Left Ctrl",
|
||||
|
|
@ -173,28 +173,84 @@ export const keyDisplayMap: Record<string, string> = {
|
|||
Tab: "Tab ⇥",
|
||||
|
||||
// Letters
|
||||
KeyA: "a", KeyB: "b", KeyC: "c", KeyD: "d", 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",
|
||||
KeyA: "a",
|
||||
KeyB: "b",
|
||||
KeyC: "c",
|
||||
KeyD: "d",
|
||||
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",
|
||||
|
||||
// Capital letters
|
||||
"(KeyA)": "A", "(KeyB)": "B", "(KeyC)": "C", "(KeyD)": "D", "(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",
|
||||
"(KeyA)": "A",
|
||||
"(KeyB)": "B",
|
||||
"(KeyC)": "C",
|
||||
"(KeyD)": "D",
|
||||
"(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",
|
||||
|
||||
// Numbers
|
||||
Digit1: "1", Digit2: "2", Digit3: "3", Digit4: "4", Digit5: "5",
|
||||
Digit6: "6", Digit7: "7", Digit8: "8", Digit9: "9", Digit0: "0",
|
||||
Digit1: "1",
|
||||
Digit2: "2",
|
||||
Digit3: "3",
|
||||
Digit4: "4",
|
||||
Digit5: "5",
|
||||
Digit6: "6",
|
||||
Digit7: "7",
|
||||
Digit8: "8",
|
||||
Digit9: "9",
|
||||
Digit0: "0",
|
||||
|
||||
// Shifted Numbers
|
||||
"(Digit1)": "!", "(Digit2)": "@", "(Digit3)": "#", "(Digit4)": "$", "(Digit5)": "%",
|
||||
"(Digit6)": "^", "(Digit7)": "&", "(Digit8)": "*", "(Digit9)": "(", "(Digit0)": ")",
|
||||
"(Digit1)": "!",
|
||||
"(Digit2)": "@",
|
||||
"(Digit3)": "#",
|
||||
"(Digit4)": "$",
|
||||
"(Digit5)": "%",
|
||||
"(Digit6)": "^",
|
||||
"(Digit7)": "&",
|
||||
"(Digit8)": "*",
|
||||
"(Digit9)": "(",
|
||||
"(Digit0)": ")",
|
||||
|
||||
// Symbols
|
||||
Minus: "-",
|
||||
|
|
@ -210,7 +266,7 @@ export const keyDisplayMap: Record<string, string> = {
|
|||
Semicolon: ";",
|
||||
"(Semicolon)": ":",
|
||||
Quote: "'",
|
||||
"(Quote)": "\"",
|
||||
"(Quote)": '"',
|
||||
Comma: ",",
|
||||
"(Comma)": "<",
|
||||
Period: ".",
|
||||
|
|
@ -222,23 +278,49 @@ export const keyDisplayMap: Record<string, string> = {
|
|||
IntlBackslash: "\\",
|
||||
|
||||
// Function keys
|
||||
F1: "F1", F2: "F2", F3: "F3", F4: "F4",
|
||||
F5: "F5", F6: "F6", F7: "F7", F8: "F8",
|
||||
F9: "F9", F10: "F10", F11: "F11", F12: "F12",
|
||||
F1: "F1",
|
||||
F2: "F2",
|
||||
F3: "F3",
|
||||
F4: "F4",
|
||||
F5: "F5",
|
||||
F6: "F6",
|
||||
F7: "F7",
|
||||
F8: "F8",
|
||||
F9: "F9",
|
||||
F10: "F10",
|
||||
F11: "F11",
|
||||
F12: "F12",
|
||||
|
||||
// Numpad
|
||||
Numpad0: "Num 0", Numpad1: "Num 1", Numpad2: "Num 2",
|
||||
Numpad3: "Num 3", Numpad4: "Num 4", Numpad5: "Num 5",
|
||||
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",
|
||||
Numpad0: "Num 0",
|
||||
Numpad1: "Num 1",
|
||||
Numpad2: "Num 2",
|
||||
Numpad3: "Num 3",
|
||||
Numpad4: "Num 4",
|
||||
Numpad5: "Num 5",
|
||||
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
|
||||
PrintScreen: "Prt Sc", ScrollLock: "Scr Lk", Pause: "Pause",
|
||||
"(PrintScreen)": "Sys Rq", "(Pause)": "Break",
|
||||
SystemRequest: "Sys Rq", Break: "Break"
|
||||
PrintScreen: "Prt Sc",
|
||||
ScrollLock: "Scr Lk",
|
||||
Pause: "Pause",
|
||||
"(PrintScreen)": "Sys Rq",
|
||||
"(Pause)": "Break",
|
||||
SystemRequest: "Sys Rq",
|
||||
Break: "Break",
|
||||
};
|
||||
|
||||
export const virtualKeyboard = {
|
||||
|
|
@ -260,25 +342,15 @@ export const virtualKeyboard = {
|
|||
"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",
|
||||
"ControlLeft MetaLeft AltLeft Space AltGr MetaRight Menu ControlRight",
|
||||
]
|
||||
],
|
||||
},
|
||||
control: {
|
||||
default: [
|
||||
"PrintScreen ScrollLock Pause",
|
||||
"Insert Home PageUp",
|
||||
"Delete End PageDown"
|
||||
],
|
||||
shift: [
|
||||
"(PrintScreen) ScrollLock (Pause)",
|
||||
"Insert Home PageUp",
|
||||
"Delete End PageDown"
|
||||
],
|
||||
default: ["PrintScreen ScrollLock Pause", "Insert Home PageUp", "Delete End PageDown"],
|
||||
shift: ["(PrintScreen) ScrollLock (Pause)", "Insert Home PageUp", "Delete End PageDown"],
|
||||
},
|
||||
|
||||
arrows: {
|
||||
default: [
|
||||
"ArrowUp",
|
||||
"ArrowLeft ArrowDown ArrowRight"],
|
||||
default: ["ArrowUp", "ArrowLeft ArrowDown ArrowRight"],
|
||||
},
|
||||
|
||||
numpad: {
|
||||
|
|
@ -296,8 +368,8 @@ export const virtualKeyboard = {
|
|||
"End ArrowDown PageDown NumpadEnter",
|
||||
"NumpadInsert NumpadDelete",
|
||||
],
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
export const en_US: KeyboardLayout = {
|
||||
isoCode,
|
||||
|
|
@ -305,7 +377,5 @@ export const en_US: KeyboardLayout = {
|
|||
chars,
|
||||
keyDisplayMap,
|
||||
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 isoCode = "es-ES";
|
||||
|
||||
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 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 keyTilde: KeyCombo = { key: "Key4", altRight: true } // tilde, mark ~ placed above the letter
|
||||
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 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 keyTilde: KeyCombo = { key: "Key4", altRight: true }; // tilde, mark ~ placed above the letter
|
||||
|
||||
const chars = {
|
||||
A: { key: "KeyA", shift: true },
|
||||
"Ä": { key: "KeyA", shift: true, accentKey: keyTrema },
|
||||
"Á": { key: "KeyA", shift: true, accentKey: keyAcute },
|
||||
"Â": { key: "KeyA", shift: true, accentKey: keyHat },
|
||||
"À": { key: "KeyA", shift: true, accentKey: keyGrave },
|
||||
"Ã": { key: "KeyA", shift: true, accentKey: keyTilde },
|
||||
Ä: { key: "KeyA", shift: true, accentKey: keyTrema },
|
||||
Á: { key: "KeyA", shift: true, accentKey: keyAcute },
|
||||
Â: { key: "KeyA", shift: true, accentKey: keyHat },
|
||||
À: { key: "KeyA", shift: true, accentKey: keyGrave },
|
||||
Ã: { key: "KeyA", shift: true, accentKey: keyTilde },
|
||||
B: { key: "KeyB", shift: true },
|
||||
C: { key: "KeyC", shift: true },
|
||||
D: { key: "KeyD", shift: true },
|
||||
E: { key: "KeyE", shift: true },
|
||||
"Ë": { key: "KeyE", shift: true, accentKey: keyTrema },
|
||||
"É": { key: "KeyE", shift: true, accentKey: keyAcute },
|
||||
"Ê": { key: "KeyE", shift: true, accentKey: keyHat },
|
||||
"È": { key: "KeyE", shift: true, accentKey: keyGrave },
|
||||
"Ẽ": { key: "KeyE", shift: true, accentKey: keyTilde },
|
||||
Ë: { key: "KeyE", shift: true, accentKey: keyTrema },
|
||||
É: { key: "KeyE", shift: true, accentKey: keyAcute },
|
||||
Ê: { key: "KeyE", shift: true, accentKey: keyHat },
|
||||
È: { key: "KeyE", shift: true, accentKey: keyGrave },
|
||||
Ẽ: { key: "KeyE", shift: true, accentKey: keyTilde },
|
||||
F: { key: "KeyF", shift: true },
|
||||
G: { key: "KeyG", shift: true },
|
||||
H: { key: "KeyH", shift: true },
|
||||
I: { key: "KeyI", shift: true },
|
||||
"Ï": { key: "KeyI", shift: true, accentKey: keyTrema },
|
||||
"Í": { key: "KeyI", shift: true, accentKey: keyAcute },
|
||||
"Î": { key: "KeyI", shift: true, accentKey: keyHat },
|
||||
"Ì": { key: "KeyI", shift: true, accentKey: keyGrave },
|
||||
"Ĩ": { key: "KeyI", shift: true, accentKey: keyTilde },
|
||||
Ï: { key: "KeyI", shift: true, accentKey: keyTrema },
|
||||
Í: { key: "KeyI", shift: true, accentKey: keyAcute },
|
||||
Î: { key: "KeyI", shift: true, accentKey: keyHat },
|
||||
Ì: { key: "KeyI", shift: true, accentKey: keyGrave },
|
||||
Ĩ: { key: "KeyI", shift: true, accentKey: keyTilde },
|
||||
J: { key: "KeyJ", shift: true },
|
||||
K: { key: "KeyK", shift: true },
|
||||
L: { key: "KeyL", shift: true },
|
||||
M: { key: "KeyM", shift: true },
|
||||
N: { key: "KeyN", shift: true },
|
||||
O: { key: "KeyO", shift: true },
|
||||
"Ö": { key: "KeyO", shift: true, accentKey: keyTrema },
|
||||
"Ó": { key: "KeyO", shift: true, accentKey: keyAcute },
|
||||
"Ô": { key: "KeyO", shift: true, accentKey: keyHat },
|
||||
"Ò": { key: "KeyO", shift: true, accentKey: keyGrave },
|
||||
"Õ": { key: "KeyO", shift: true, accentKey: keyTilde },
|
||||
Ö: { key: "KeyO", shift: true, accentKey: keyTrema },
|
||||
Ó: { key: "KeyO", shift: true, accentKey: keyAcute },
|
||||
Ô: { key: "KeyO", shift: true, accentKey: keyHat },
|
||||
Ò: { key: "KeyO", shift: true, accentKey: keyGrave },
|
||||
Õ: { key: "KeyO", shift: true, accentKey: keyTilde },
|
||||
P: { key: "KeyP", shift: true },
|
||||
Q: { key: "KeyQ", shift: true },
|
||||
R: { key: "KeyR", shift: true },
|
||||
S: { key: "KeyS", shift: true },
|
||||
T: { key: "KeyT", shift: true },
|
||||
U: { key: "KeyU", shift: true },
|
||||
"Ü": { key: "KeyU", shift: true, accentKey: keyTrema },
|
||||
"Ú": { key: "KeyU", shift: true, accentKey: keyAcute },
|
||||
"Û": { key: "KeyU", shift: true, accentKey: keyHat },
|
||||
"Ù": { key: "KeyU", shift: true, accentKey: keyGrave },
|
||||
"Ũ": { key: "KeyU", shift: true, accentKey: keyTilde },
|
||||
Ü: { key: "KeyU", shift: true, accentKey: keyTrema },
|
||||
Ú: { key: "KeyU", shift: true, accentKey: keyAcute },
|
||||
Û: { key: "KeyU", shift: true, accentKey: keyHat },
|
||||
Ù: { key: "KeyU", shift: true, accentKey: keyGrave },
|
||||
Ũ: { key: "KeyU", shift: true, accentKey: keyTilde },
|
||||
V: { key: "KeyV", shift: true },
|
||||
W: { key: "KeyW", shift: true },
|
||||
X: { key: "KeyX", shift: true },
|
||||
Y: { key: "KeyY", shift: true },
|
||||
Z: { key: "KeyZ", shift: true },
|
||||
a: { key: "KeyA" },
|
||||
"ä": { key: "KeyA", accentKey: keyTrema },
|
||||
"á": { key: "KeyA", accentKey: keyAcute },
|
||||
"â": { key: "KeyA", accentKey: keyHat },
|
||||
"à": { key: "KeyA", accentKey: keyGrave },
|
||||
"ã": { key: "KeyA", accentKey: keyTilde },
|
||||
ä: { key: "KeyA", accentKey: keyTrema },
|
||||
á: { key: "KeyA", accentKey: keyAcute },
|
||||
â: { key: "KeyA", accentKey: keyHat },
|
||||
à: { key: "KeyA", accentKey: keyGrave },
|
||||
ã: { key: "KeyA", accentKey: keyTilde },
|
||||
b: { key: "KeyB" },
|
||||
c: { key: "KeyC" },
|
||||
d: { key: "KeyD" },
|
||||
e: { key: "KeyE" },
|
||||
"ë": { key: "KeyE", accentKey: keyTrema },
|
||||
"é": { key: "KeyE", accentKey: keyAcute },
|
||||
"ê": { key: "KeyE", accentKey: keyHat },
|
||||
"è": { key: "KeyE", accentKey: keyGrave },
|
||||
"ẽ": { key: "KeyE", accentKey: keyTilde },
|
||||
ë: { key: "KeyE", accentKey: keyTrema },
|
||||
é: { key: "KeyE", accentKey: keyAcute },
|
||||
ê: { key: "KeyE", accentKey: keyHat },
|
||||
è: { key: "KeyE", accentKey: keyGrave },
|
||||
ẽ: { key: "KeyE", accentKey: keyTilde },
|
||||
"€": { key: "KeyE", altRight: true },
|
||||
f: { key: "KeyF" },
|
||||
g: { key: "KeyG" },
|
||||
h: { key: "KeyH" },
|
||||
i: { key: "KeyI" },
|
||||
"ï": { key: "KeyI", accentKey: keyTrema },
|
||||
"í": { key: "KeyI", accentKey: keyAcute },
|
||||
"î": { key: "KeyI", accentKey: keyHat },
|
||||
"ì": { key: "KeyI", accentKey: keyGrave },
|
||||
"ĩ": { key: "KeyI", accentKey: keyTilde },
|
||||
ï: { key: "KeyI", accentKey: keyTrema },
|
||||
í: { key: "KeyI", accentKey: keyAcute },
|
||||
î: { key: "KeyI", accentKey: keyHat },
|
||||
ì: { key: "KeyI", accentKey: keyGrave },
|
||||
ĩ: { key: "KeyI", accentKey: keyTilde },
|
||||
j: { key: "KeyJ" },
|
||||
k: { key: "KeyK" },
|
||||
l: { key: "KeyL" },
|
||||
m: { key: "KeyM" },
|
||||
n: { key: "KeyN" },
|
||||
o: { key: "KeyO" },
|
||||
"ö": { key: "KeyO", accentKey: keyTrema },
|
||||
"ó": { key: "KeyO", accentKey: keyAcute },
|
||||
"ô": { key: "KeyO", accentKey: keyHat },
|
||||
"ò": { key: "KeyO", accentKey: keyGrave },
|
||||
"õ": { key: "KeyO", accentKey: keyTilde },
|
||||
ö: { key: "KeyO", accentKey: keyTrema },
|
||||
ó: { key: "KeyO", accentKey: keyAcute },
|
||||
ô: { key: "KeyO", accentKey: keyHat },
|
||||
ò: { key: "KeyO", accentKey: keyGrave },
|
||||
õ: { key: "KeyO", accentKey: keyTilde },
|
||||
p: { key: "KeyP" },
|
||||
q: { key: "KeyQ" },
|
||||
r: { key: "KeyR" },
|
||||
s: { key: "KeyS" },
|
||||
t: { key: "KeyT" },
|
||||
u: { key: "KeyU" },
|
||||
"ü": { key: "KeyU", accentKey: keyTrema },
|
||||
"ú": { key: "KeyU", accentKey: keyAcute },
|
||||
"û": { key: "KeyU", accentKey: keyHat },
|
||||
"ù": { key: "KeyU", accentKey: keyGrave },
|
||||
"ũ": { key: "KeyU", accentKey: keyTilde },
|
||||
ü: { key: "KeyU", accentKey: keyTrema },
|
||||
ú: { key: "KeyU", accentKey: keyAcute },
|
||||
û: { key: "KeyU", accentKey: keyHat },
|
||||
ù: { key: "KeyU", accentKey: keyGrave },
|
||||
ũ: { key: "KeyU", accentKey: keyTilde },
|
||||
v: { key: "KeyV" },
|
||||
w: { key: "KeyW" },
|
||||
x: { key: "KeyX" },
|
||||
y: { key: "KeyY" },
|
||||
z: { key: "KeyZ" },
|
||||
"º": { key: "Backquote" },
|
||||
"ª": { key: "Backquote", shift: true },
|
||||
º: { key: "Backquote" },
|
||||
ª: { key: "Backquote", shift: true },
|
||||
"\\": { key: "Backquote", altRight: true },
|
||||
1: { key: "Digit1" },
|
||||
"!": { key: "Digit1", shift: true },
|
||||
"|": { key: "Digit1", altRight: true },
|
||||
2: { key: "Digit2" },
|
||||
"\"": { key: "Digit2", shift: true },
|
||||
'"': { key: "Digit2", shift: true },
|
||||
"@": { key: "Digit2", altRight: true },
|
||||
3: { key: "Digit3" },
|
||||
"·": { key: "Digit3", shift: true },
|
||||
"#": { key: "Digit3", altRight: true },
|
||||
4: { key: "Digit4" },
|
||||
"$": { key: "Digit4", shift: true },
|
||||
$: { key: "Digit4", shift: true },
|
||||
5: { key: "Digit5" },
|
||||
"%": { key: "Digit5", shift: true },
|
||||
6: { key: "Digit6" },
|
||||
|
|
@ -150,18 +150,18 @@ const chars = {
|
|||
"+": { key: "BracketRight" },
|
||||
"*": { key: "BracketRight", shift: true },
|
||||
"]": { key: "BracketRight", altRight: true },
|
||||
"ñ": { key: "Semicolon" },
|
||||
"Ñ": { key: "Semicolon", shift: true },
|
||||
ñ: { key: "Semicolon" },
|
||||
Ñ: { key: "Semicolon", shift: true },
|
||||
"{": { key: "Quote", altRight: true },
|
||||
"ç": { key: "Backslash" },
|
||||
"Ç": { key: "Backslash", shift: true },
|
||||
ç: { key: "Backslash" },
|
||||
Ç: { key: "Backslash", shift: true },
|
||||
"}": { key: "Backslash", altRight: true },
|
||||
",": { key: "Comma" },
|
||||
";": { key: "Comma", shift: true },
|
||||
".": { key: "Period" },
|
||||
":": { key: "Period", shift: true },
|
||||
"-": { key: "Slash" },
|
||||
"_": { key: "Slash", shift: true },
|
||||
_: { key: "Slash", shift: true },
|
||||
"<": { key: "IntlBackslash" },
|
||||
">": { key: "IntlBackslash", shift: true },
|
||||
" ": { key: "Space" },
|
||||
|
|
@ -177,5 +177,5 @@ export const es_ES: KeyboardLayout = {
|
|||
// TODO need to localize these maps and layouts
|
||||
keyDisplayMap: en_US.keyDisplayMap,
|
||||
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 isoCode = "nl-BE";
|
||||
|
||||
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 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 keyTilde: KeyCombo = { key: "Slash", altRight: true } // tilde, mark ~ placed above the letter
|
||||
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 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 keyTilde: KeyCombo = { key: "Slash", altRight: true }; // tilde, mark ~ placed above the letter
|
||||
|
||||
const chars = {
|
||||
A: { key: "KeyQ", shift: true },
|
||||
"Ä": { key: "KeyQ", shift: true, accentKey: keyTrema },
|
||||
"Â": { key: "KeyQ", shift: true, accentKey: keyHat },
|
||||
"Á": { key: "KeyQ", shift: true, accentKey: keyAcute },
|
||||
"À": { key: "KeyQ", shift: true, accentKey: keyGrave },
|
||||
"Ã": { key: "KeyQ", shift: true, accentKey: keyTilde },
|
||||
Ä: { key: "KeyQ", shift: true, accentKey: keyTrema },
|
||||
Â: { key: "KeyQ", shift: true, accentKey: keyHat },
|
||||
Á: { key: "KeyQ", shift: true, accentKey: keyAcute },
|
||||
À: { key: "KeyQ", shift: true, accentKey: keyGrave },
|
||||
Ã: { key: "KeyQ", shift: true, accentKey: keyTilde },
|
||||
B: { key: "KeyB", shift: true },
|
||||
C: { key: "KeyC", shift: true },
|
||||
D: { key: "KeyD", shift: true },
|
||||
E: { key: "KeyE", shift: true },
|
||||
"Ë": { key: "KeyE", shift: true, accentKey: keyTrema },
|
||||
"Ê": { key: "KeyE", shift: true, accentKey: keyHat },
|
||||
"É": { key: "KeyE", shift: true, accentKey: keyAcute },
|
||||
"È": { key: "KeyE", shift: true, accentKey: keyGrave },
|
||||
"Ẽ": { key: "KeyE", shift: true, accentKey: keyTilde },
|
||||
Ë: { key: "KeyE", shift: true, accentKey: keyTrema },
|
||||
Ê: { key: "KeyE", shift: true, accentKey: keyHat },
|
||||
É: { key: "KeyE", shift: true, accentKey: keyAcute },
|
||||
È: { key: "KeyE", shift: true, accentKey: keyGrave },
|
||||
Ẽ: { key: "KeyE", shift: true, accentKey: keyTilde },
|
||||
F: { key: "KeyF", shift: true },
|
||||
G: { key: "KeyG", shift: true },
|
||||
H: { key: "KeyH", shift: true },
|
||||
I: { key: "KeyI", shift: true },
|
||||
"Ï": { key: "KeyI", shift: true, accentKey: keyTrema },
|
||||
"Î": { key: "KeyI", shift: true, accentKey: keyHat },
|
||||
"Í": { key: "KeyI", shift: true, accentKey: keyAcute },
|
||||
"Ì": { key: "KeyI", shift: true, accentKey: keyGrave },
|
||||
"Ĩ": { key: "KeyI", shift: true, accentKey: keyTilde },
|
||||
Ï: { key: "KeyI", shift: true, accentKey: keyTrema },
|
||||
Î: { key: "KeyI", shift: true, accentKey: keyHat },
|
||||
Í: { key: "KeyI", shift: true, accentKey: keyAcute },
|
||||
Ì: { key: "KeyI", shift: true, accentKey: keyGrave },
|
||||
Ĩ: { key: "KeyI", shift: true, accentKey: keyTilde },
|
||||
J: { key: "KeyJ", shift: true },
|
||||
K: { key: "KeyK", shift: true },
|
||||
L: { key: "KeyL", shift: true },
|
||||
M: { key: "Semicolon", shift: true },
|
||||
N: { key: "KeyN", shift: true },
|
||||
O: { key: "KeyO", shift: true },
|
||||
"Ö": { key: "KeyO", shift: true, accentKey: keyTrema },
|
||||
"Ô": { key: "KeyO", shift: true, accentKey: keyHat },
|
||||
"Ó": { key: "KeyO", shift: true, accentKey: keyAcute },
|
||||
"Ò": { key: "KeyO", shift: true, accentKey: keyGrave },
|
||||
"Õ": { key: "KeyO", shift: true, accentKey: keyTilde },
|
||||
Ö: { key: "KeyO", shift: true, accentKey: keyTrema },
|
||||
Ô: { key: "KeyO", shift: true, accentKey: keyHat },
|
||||
Ó: { key: "KeyO", shift: true, accentKey: keyAcute },
|
||||
Ò: { key: "KeyO", shift: true, accentKey: keyGrave },
|
||||
Õ: { key: "KeyO", shift: true, accentKey: keyTilde },
|
||||
P: { key: "KeyP", shift: true },
|
||||
Q: { key: "KeyA", shift: true },
|
||||
R: { key: "KeyR", shift: true },
|
||||
S: { key: "KeyS", shift: true },
|
||||
T: { key: "KeyT", shift: true },
|
||||
U: { key: "KeyU", shift: true },
|
||||
"Ü": { key: "KeyU", shift: true, accentKey: keyTrema },
|
||||
"Û": { key: "KeyU", shift: true, accentKey: keyHat },
|
||||
"Ú": { key: "KeyU", shift: true, accentKey: keyAcute },
|
||||
"Ù": { key: "KeyU", shift: true, accentKey: keyGrave },
|
||||
"Ũ": { key: "KeyU", shift: true, accentKey: keyTilde },
|
||||
Ü: { key: "KeyU", shift: true, accentKey: keyTrema },
|
||||
Û: { key: "KeyU", shift: true, accentKey: keyHat },
|
||||
Ú: { key: "KeyU", shift: true, accentKey: keyAcute },
|
||||
Ù: { key: "KeyU", shift: true, accentKey: keyGrave },
|
||||
Ũ: { key: "KeyU", shift: true, accentKey: keyTilde },
|
||||
V: { key: "KeyV", shift: true },
|
||||
W: { key: "KeyW", shift: true },
|
||||
X: { key: "KeyX", shift: true },
|
||||
Y: { key: "KeyZ", shift: true },
|
||||
Z: { key: "KeyY", shift: true },
|
||||
a: { key: "KeyQ" },
|
||||
"ä": { key: "KeyQ", accentKey: keyTrema },
|
||||
"â": { key: "KeyQ", accentKey: keyHat },
|
||||
"á": { key: "KeyQ", accentKey: keyAcute },
|
||||
"ã": { key: "KeyQ", accentKey: keyTilde },
|
||||
ä: { key: "KeyQ", accentKey: keyTrema },
|
||||
â: { key: "KeyQ", accentKey: keyHat },
|
||||
á: { key: "KeyQ", accentKey: keyAcute },
|
||||
ã: { key: "KeyQ", accentKey: keyTilde },
|
||||
b: { key: "KeyB" },
|
||||
c: { key: "KeyC" },
|
||||
d: { key: "KeyD" },
|
||||
e: { key: "KeyE" },
|
||||
"ë": { key: "KeyE", accentKey: keyTrema },
|
||||
"ê": { key: "KeyE", accentKey: keyHat },
|
||||
"ẽ": { key: "KeyE", accentKey: keyTilde },
|
||||
ë: { key: "KeyE", accentKey: keyTrema },
|
||||
ê: { key: "KeyE", accentKey: keyHat },
|
||||
ẽ: { key: "KeyE", accentKey: keyTilde },
|
||||
"€": { key: "KeyE", altRight: true },
|
||||
f: { key: "KeyF" },
|
||||
g: { key: "KeyG" },
|
||||
h: { key: "KeyH" },
|
||||
i: { key: "KeyI" },
|
||||
"ï": { key: "KeyI", accentKey: keyTrema },
|
||||
"î": { key: "KeyI", accentKey: keyHat },
|
||||
"í": { key: "KeyI", accentKey: keyAcute },
|
||||
"ì": { key: "KeyI", accentKey: keyGrave },
|
||||
"ĩ": { key: "KeyI", accentKey: keyTilde },
|
||||
ï: { key: "KeyI", accentKey: keyTrema },
|
||||
î: { key: "KeyI", accentKey: keyHat },
|
||||
í: { key: "KeyI", accentKey: keyAcute },
|
||||
ì: { key: "KeyI", accentKey: keyGrave },
|
||||
ĩ: { key: "KeyI", accentKey: keyTilde },
|
||||
j: { key: "KeyJ" },
|
||||
k: { key: "KeyK" },
|
||||
l: { key: "KeyL" },
|
||||
m: { key: "Semicolon" },
|
||||
n: { key: "KeyN" },
|
||||
o: { key: "KeyO" },
|
||||
"ö": { key: "KeyO", accentKey: keyTrema },
|
||||
"ó": { key: "KeyO", accentKey: keyAcute },
|
||||
"ô": { key: "KeyO", accentKey: keyHat },
|
||||
"ò": { key: "KeyO", accentKey: keyGrave },
|
||||
"õ": { key: "KeyO", accentKey: keyTilde },
|
||||
ö: { key: "KeyO", accentKey: keyTrema },
|
||||
ó: { key: "KeyO", accentKey: keyAcute },
|
||||
ô: { key: "KeyO", accentKey: keyHat },
|
||||
ò: { key: "KeyO", accentKey: keyGrave },
|
||||
õ: { key: "KeyO", accentKey: keyTilde },
|
||||
p: { key: "KeyP" },
|
||||
q: { key: "KeyA" },
|
||||
r: { key: "KeyR" },
|
||||
s: { key: "KeyS" },
|
||||
t: { key: "KeyT" },
|
||||
u: { key: "KeyU" },
|
||||
"ü": { key: "KeyU", accentKey: keyTrema },
|
||||
"û": { key: "KeyU", accentKey: keyHat },
|
||||
"ú": { key: "KeyU", accentKey: keyAcute },
|
||||
"ũ": { key: "KeyU", accentKey: keyTilde },
|
||||
ü: { key: "KeyU", accentKey: keyTrema },
|
||||
û: { key: "KeyU", accentKey: keyHat },
|
||||
ú: { key: "KeyU", accentKey: keyAcute },
|
||||
ũ: { key: "KeyU", accentKey: keyTilde },
|
||||
v: { key: "KeyV" },
|
||||
w: { key: "KeyW" },
|
||||
x: { key: "KeyX" },
|
||||
|
|
@ -116,10 +116,10 @@ const chars = {
|
|||
"&": { key: "Digit1" },
|
||||
1: { key: "Digit1", shift: true },
|
||||
"|": { key: "Digit1", altRight: true },
|
||||
"é": { key: "Digit2" },
|
||||
é: { key: "Digit2" },
|
||||
2: { key: "Digit2", shift: true },
|
||||
"@": { key: "Digit2", altRight: true },
|
||||
"\"": { key: "Digit3" },
|
||||
'"': { key: "Digit3" },
|
||||
3: { key: "Digit3", shift: true },
|
||||
"#": { key: "Digit3", altRight: true },
|
||||
"'": { key: "Digit4" },
|
||||
|
|
@ -129,27 +129,27 @@ const chars = {
|
|||
"§": { key: "Digit6" },
|
||||
6: { key: "Digit6", shift: true },
|
||||
"^": { key: "Digit6", altRight: true },
|
||||
"è": { key: "Digit7" },
|
||||
è: { key: "Digit7" },
|
||||
7: { key: "Digit7", shift: true },
|
||||
"!": { key: "Digit8" },
|
||||
8: { key: "Digit8", shift: true },
|
||||
"ç": { key: "Digit9" },
|
||||
ç: { key: "Digit9" },
|
||||
9: { key: "Digit9", shift: true },
|
||||
"{": { key: "Digit9", altRight: true },
|
||||
"à": { key: "Digit0" },
|
||||
à: { key: "Digit0" },
|
||||
0: { key: "Digit0", shift: true },
|
||||
"}": { key: "Digit0", altRight: true },
|
||||
")": { key: "Minus" },
|
||||
"°": { key: "Minus", shift: true },
|
||||
"-": { key: "Equal", deadKey: true },
|
||||
"_": { key: "Equal", shift: true },
|
||||
_: { key: "Equal", shift: true },
|
||||
"[": { key: "BracketLeft", altRight: true },
|
||||
"$": { key: "BracketRight" },
|
||||
$: { key: "BracketRight" },
|
||||
"*": { key: "BracketRight", altRight: true },
|
||||
"]": { key: "BracketRight", altRight: true },
|
||||
"ù": { key: "Quote" },
|
||||
ù: { key: "Quote" },
|
||||
"%": { key: "Quote", shift: true },
|
||||
"µ": { key: "Backslash" },
|
||||
µ: { key: "Backslash" },
|
||||
"£": { key: "Backslash", shift: true },
|
||||
",": { key: "KeyM" },
|
||||
"?": { key: "KeyM", shift: true },
|
||||
|
|
@ -176,5 +176,5 @@ export const fr_BE: KeyboardLayout = {
|
|||
// TODO need to localize these maps and layouts
|
||||
keyDisplayMap: en_US.keyDisplayMap,
|
||||
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 isoCode = "fr-CH";
|
||||
|
||||
const chars = {
|
||||
...de_CH.chars,
|
||||
"è": { key: "BracketLeft" },
|
||||
"ü": { key: "BracketLeft", shift: true },
|
||||
"é": { key: "Semicolon" },
|
||||
"ö": { key: "Semicolon", shift: true },
|
||||
"à": { key: "Quote" },
|
||||
"ä": { key: "Quote", shift: true },
|
||||
è: { key: "BracketLeft" },
|
||||
ü: { key: "BracketLeft", shift: true },
|
||||
é: { key: "Semicolon" },
|
||||
ö: { key: "Semicolon", shift: true },
|
||||
à: { key: "Quote" },
|
||||
ä: { key: "Quote", shift: true },
|
||||
} as Record<string, KeyCombo>;
|
||||
|
||||
const keyDisplayMap = {
|
||||
...de_CH.keyDisplayMap,
|
||||
"BracketLeft": "è",
|
||||
"BracketLeftShift": "ü",
|
||||
"Semicolon": "é",
|
||||
"SemicolonShift": "ö",
|
||||
"Quote": "à",
|
||||
"QuoteShift": "ä",
|
||||
BracketLeft: "è",
|
||||
BracketLeftShift: "ü",
|
||||
Semicolon: "é",
|
||||
SemicolonShift: "ö",
|
||||
Quote: "à",
|
||||
QuoteShift: "ä",
|
||||
} as Record<string, string>;
|
||||
|
||||
export const fr_CH: KeyboardLayout = {
|
||||
|
|
@ -32,5 +32,5 @@ export const fr_CH: KeyboardLayout = {
|
|||
keyDisplayMap: keyDisplayMap,
|
||||
// TODO need to localize these maps and layouts
|
||||
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 isoCode = "fr-FR";
|
||||
|
||||
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 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 chars = {
|
||||
A: { key: "KeyQ", shift: true },
|
||||
"Ä": { key: "KeyQ", shift: true, accentKey: keyTrema },
|
||||
"Â": { key: "KeyQ", shift: true, accentKey: keyHat },
|
||||
Ä: { key: "KeyQ", shift: true, accentKey: keyTrema },
|
||||
Â: { key: "KeyQ", shift: true, accentKey: keyHat },
|
||||
B: { key: "KeyB", shift: true },
|
||||
C: { key: "KeyC", shift: true },
|
||||
D: { key: "KeyD", shift: true },
|
||||
E: { key: "KeyE", shift: true },
|
||||
"Ë": { key: "KeyE", shift: true, accentKey: keyTrema },
|
||||
"Ê": { key: "KeyE", shift: true, accentKey: keyHat },
|
||||
Ë: { key: "KeyE", shift: true, accentKey: keyTrema },
|
||||
Ê: { key: "KeyE", shift: true, accentKey: keyHat },
|
||||
F: { key: "KeyF", shift: true },
|
||||
G: { key: "KeyG", shift: true },
|
||||
H: { key: "KeyH", shift: true },
|
||||
I: { key: "KeyI", shift: true },
|
||||
"Ï": { key: "KeyI", shift: true, accentKey: keyTrema },
|
||||
"Î": { key: "KeyI", shift: true, accentKey: keyHat },
|
||||
Ï: { key: "KeyI", shift: true, accentKey: keyTrema },
|
||||
Î: { key: "KeyI", shift: true, accentKey: keyHat },
|
||||
J: { key: "KeyJ", shift: true },
|
||||
K: { key: "KeyK", shift: true },
|
||||
L: { key: "KeyL", shift: true },
|
||||
M: { key: "Semicolon", shift: true },
|
||||
N: { key: "KeyN", shift: true },
|
||||
O: { key: "KeyO", shift: true },
|
||||
"Ö": { key: "KeyO", shift: true, accentKey: keyTrema },
|
||||
"Ô": { key: "KeyO", shift: true, accentKey: keyHat },
|
||||
Ö: { key: "KeyO", shift: true, accentKey: keyTrema },
|
||||
Ô: { key: "KeyO", shift: true, accentKey: keyHat },
|
||||
P: { key: "KeyP", shift: true },
|
||||
Q: { key: "KeyA", shift: true },
|
||||
R: { key: "KeyR", shift: true },
|
||||
S: { key: "KeyS", shift: true },
|
||||
T: { key: "KeyT", shift: true },
|
||||
U: { key: "KeyU", shift: true },
|
||||
"Ü": { key: "KeyU", shift: true, accentKey: keyTrema },
|
||||
"Û": { key: "KeyU", shift: true, accentKey: keyHat },
|
||||
Ü: { key: "KeyU", shift: true, accentKey: keyTrema },
|
||||
Û: { key: "KeyU", shift: true, accentKey: keyHat },
|
||||
V: { key: "KeyV", shift: true },
|
||||
W: { key: "KeyZ", shift: true },
|
||||
X: { key: "KeyX", shift: true },
|
||||
Y: { key: "KeyY", shift: true },
|
||||
Z: { key: "KeyW", shift: true },
|
||||
a: { key: "KeyQ" },
|
||||
"ä": { key: "KeyQ", accentKey: keyTrema },
|
||||
"â": { key: "KeyQ", accentKey: keyHat },
|
||||
ä: { key: "KeyQ", accentKey: keyTrema },
|
||||
â: { key: "KeyQ", accentKey: keyHat },
|
||||
b: { key: "KeyB" },
|
||||
c: { key: "KeyC" },
|
||||
d: { key: "KeyD" },
|
||||
e: { key: "KeyE" },
|
||||
"ë": { key: "KeyE", accentKey: keyTrema },
|
||||
"ê": { key: "KeyE", accentKey: keyHat },
|
||||
ë: { key: "KeyE", accentKey: keyTrema },
|
||||
ê: { key: "KeyE", accentKey: keyHat },
|
||||
"€": { key: "KeyE", altRight: true },
|
||||
f: { key: "KeyF" },
|
||||
g: { key: "KeyG" },
|
||||
h: { key: "KeyH" },
|
||||
i: { key: "KeyI" },
|
||||
"ï": { key: "KeyI", accentKey: keyTrema },
|
||||
"î": { key: "KeyI", accentKey: keyHat },
|
||||
ï: { key: "KeyI", accentKey: keyTrema },
|
||||
î: { key: "KeyI", accentKey: keyHat },
|
||||
j: { key: "KeyJ" },
|
||||
k: { key: "KeyK" },
|
||||
l: { key: "KeyL" },
|
||||
m: { key: "Semicolon" },
|
||||
n: { key: "KeyN" },
|
||||
o: { key: "KeyO" },
|
||||
"ö": { key: "KeyO", accentKey: keyTrema },
|
||||
"ô": { key: "KeyO", accentKey: keyHat },
|
||||
ö: { key: "KeyO", accentKey: keyTrema },
|
||||
ô: { key: "KeyO", accentKey: keyHat },
|
||||
p: { key: "KeyP" },
|
||||
q: { key: "KeyA" },
|
||||
r: { key: "KeyR" },
|
||||
s: { key: "KeyS" },
|
||||
t: { key: "KeyT" },
|
||||
u: { key: "KeyU" },
|
||||
"ü": { key: "KeyU", accentKey: keyTrema },
|
||||
"û": { key: "KeyU", accentKey: keyHat },
|
||||
ü: { key: "KeyU", accentKey: keyTrema },
|
||||
û: { key: "KeyU", accentKey: keyHat },
|
||||
v: { key: "KeyV" },
|
||||
w: { key: "KeyZ" },
|
||||
x: { key: "KeyX" },
|
||||
|
|
@ -85,10 +85,10 @@ const chars = {
|
|||
"²": { key: "Backquote" },
|
||||
"&": { key: "Digit1" },
|
||||
1: { key: "Digit1", shift: true },
|
||||
"é": { key: "Digit2" },
|
||||
é: { key: "Digit2" },
|
||||
2: { key: "Digit2", shift: true },
|
||||
"~": { key: "Digit2", altRight: true },
|
||||
"\"": { key: "Digit3" },
|
||||
'"': { key: "Digit3" },
|
||||
3: { key: "Digit3", shift: true },
|
||||
"#": { key: "Digit3", altRight: true },
|
||||
"'": { key: "Digit4" },
|
||||
|
|
@ -100,16 +100,16 @@ const chars = {
|
|||
"-": { key: "Digit6" },
|
||||
6: { key: "Digit6", shift: true },
|
||||
"|": { key: "Digit6", altRight: true },
|
||||
"è": { key: "Digit7" },
|
||||
è: { key: "Digit7" },
|
||||
7: { key: "Digit7", shift: true },
|
||||
"`": { key: "Digit7", altRight: true },
|
||||
"_": { key: "Digit8" },
|
||||
_: { key: "Digit8" },
|
||||
8: { key: "Digit8", shift: true },
|
||||
"\\": { key: "Digit8", altRight: true },
|
||||
"ç": { key: "Digit9" },
|
||||
ç: { key: "Digit9" },
|
||||
9: { key: "Digit9", shift: true },
|
||||
"^": { key: "Digit9", altRight: true },
|
||||
"à": { key: "Digit0" },
|
||||
à: { key: "Digit0" },
|
||||
0: { key: "Digit0", shift: true },
|
||||
"@": { key: "Digit0", altRight: true },
|
||||
")": { key: "Minus" },
|
||||
|
|
@ -118,13 +118,13 @@ const chars = {
|
|||
"=": { key: "Equal" },
|
||||
"+": { key: "Equal", shift: true },
|
||||
"}": { key: "Equal", altRight: true },
|
||||
"$": { key: "BracketRight" },
|
||||
$: { key: "BracketRight" },
|
||||
"£": { key: "BracketRight", shift: true },
|
||||
"¤": { key: "BracketRight", altRight: true },
|
||||
"ù": { key: "Quote" },
|
||||
ù: { key: "Quote" },
|
||||
"%": { key: "Quote", shift: true },
|
||||
"*": { key: "Backslash" },
|
||||
"µ": { key: "Backslash", shift: true },
|
||||
µ: { key: "Backslash", shift: true },
|
||||
",": { key: "KeyM" },
|
||||
"?": { key: "KeyM", shift: true },
|
||||
";": { key: "Comma" },
|
||||
|
|
@ -148,5 +148,5 @@ export const fr_FR: KeyboardLayout = {
|
|||
// TODO need to localize these maps and layouts
|
||||
keyDisplayMap: en_US.keyDisplayMap,
|
||||
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 isoCode = "it-IT";
|
||||
|
|
@ -64,11 +64,11 @@ const chars = {
|
|||
1: { key: "Digit1" },
|
||||
"!": { key: "Digit1", shift: true },
|
||||
2: { key: "Digit2" },
|
||||
"\"": { key: "Digit2", shift: true },
|
||||
'"': { key: "Digit2", shift: true },
|
||||
3: { key: "Digit3" },
|
||||
"£": { key: "Digit3", shift: true },
|
||||
4: { key: "Digit4" },
|
||||
"$": { key: "Digit4", shift: true },
|
||||
$: { key: "Digit4", shift: true },
|
||||
5: { key: "Digit5" },
|
||||
"%": { key: "Digit5", shift: true },
|
||||
6: { key: "Digit6" },
|
||||
|
|
@ -83,30 +83,30 @@ const chars = {
|
|||
"=": { key: "Digit0", shift: true },
|
||||
"'": { key: "Minus" },
|
||||
"?": { key: "Minus", shift: true },
|
||||
"ì": { key: "Equal" },
|
||||
ì: { key: "Equal" },
|
||||
"^": { key: "Equal", shift: true },
|
||||
"è": { key: "BracketLeft" },
|
||||
"é": { key: "BracketLeft", shift: true },
|
||||
è: { key: "BracketLeft" },
|
||||
é: { key: "BracketLeft", shift: true },
|
||||
"[": { key: "BracketLeft", altRight: true },
|
||||
"{": { key: "BracketLeft", shift: true, altRight: true },
|
||||
"+": { key: "BracketRight" },
|
||||
"*": { key: "BracketRight", shift: true },
|
||||
"]": { key: "BracketRight", altRight: true },
|
||||
"}": { key: "BracketRight", shift: true, altRight: true },
|
||||
"ò": { key: "Semicolon" },
|
||||
"ç": { key: "Semicolon", shift: true },
|
||||
ò: { key: "Semicolon" },
|
||||
ç: { key: "Semicolon", shift: true },
|
||||
"@": { key: "Semicolon", altRight: true },
|
||||
"à": { key: "Quote" },
|
||||
à: { key: "Quote" },
|
||||
"°": { key: "Quote", shift: true },
|
||||
"#": { key: "Quote", altRight: true },
|
||||
"ù": { key: "Backslash" },
|
||||
ù: { key: "Backslash" },
|
||||
"§": { key: "Backslash", shift: true },
|
||||
",": { key: "Comma" },
|
||||
";": { key: "Comma", shift: true },
|
||||
".": { key: "Period" },
|
||||
":": { key: "Period", shift: true },
|
||||
"-": { key: "Slash" },
|
||||
"_": { key: "Slash", shift: true },
|
||||
_: { key: "Slash", shift: true },
|
||||
"<": { key: "IntlBackslash" },
|
||||
">": { key: "IntlBackslash", shift: true },
|
||||
" ": { key: "Space" },
|
||||
|
|
@ -122,5 +122,5 @@ export const it_IT: KeyboardLayout = {
|
|||
// TODO need to localize these maps and layouts
|
||||
keyDisplayMap: en_US.keyDisplayMap,
|
||||
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 isoCode = "nb-NO";
|
||||
|
||||
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 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 keyTilde: KeyCombo = { key: "BracketRight", altRight: true } // tilde, mark ~ placed above the letter
|
||||
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 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 keyTilde: KeyCombo = { key: "BracketRight", altRight: true }; // tilde, mark ~ placed above the letter
|
||||
|
||||
const chars = {
|
||||
A: { key: "KeyA", shift: true },
|
||||
"Ä": { key: "KeyA", shift: true, accentKey: keyTrema },
|
||||
"Á": { key: "KeyA", shift: true, accentKey: keyAcute },
|
||||
"Â": { key: "KeyA", shift: true, accentKey: keyHat },
|
||||
"À": { key: "KeyA", shift: true, accentKey: keyGrave },
|
||||
"Ã": { key: "KeyA", shift: true, accentKey: keyTilde },
|
||||
Ä: { key: "KeyA", shift: true, accentKey: keyTrema },
|
||||
Á: { key: "KeyA", shift: true, accentKey: keyAcute },
|
||||
Â: { key: "KeyA", shift: true, accentKey: keyHat },
|
||||
À: { key: "KeyA", shift: true, accentKey: keyGrave },
|
||||
Ã: { key: "KeyA", shift: true, accentKey: keyTilde },
|
||||
B: { key: "KeyB", shift: true },
|
||||
C: { key: "KeyC", shift: true },
|
||||
D: { key: "KeyD", shift: true },
|
||||
E: { key: "KeyE", shift: true },
|
||||
"Ë": { key: "KeyE", shift: true, accentKey: keyTrema },
|
||||
"É": { key: "KeyE", shift: true, accentKey: keyAcute },
|
||||
"Ê": { key: "KeyE", shift: true, accentKey: keyHat },
|
||||
"È": { key: "KeyE", shift: true, accentKey: keyGrave },
|
||||
"Ẽ": { key: "KeyE", shift: true, accentKey: keyTilde },
|
||||
Ë: { key: "KeyE", shift: true, accentKey: keyTrema },
|
||||
É: { key: "KeyE", shift: true, accentKey: keyAcute },
|
||||
Ê: { key: "KeyE", shift: true, accentKey: keyHat },
|
||||
È: { key: "KeyE", shift: true, accentKey: keyGrave },
|
||||
Ẽ: { key: "KeyE", shift: true, accentKey: keyTilde },
|
||||
F: { key: "KeyF", shift: true },
|
||||
G: { key: "KeyG", shift: true },
|
||||
H: { key: "KeyH", shift: true },
|
||||
I: { key: "KeyI", shift: true },
|
||||
"Ï": { key: "KeyI", shift: true, accentKey: keyTrema },
|
||||
"Í": { key: "KeyI", shift: true, accentKey: keyAcute },
|
||||
"Î": { key: "KeyI", shift: true, accentKey: keyHat },
|
||||
"Ì": { key: "KeyI", shift: true, accentKey: keyGrave },
|
||||
"Ĩ": { key: "KeyI", shift: true, accentKey: keyTilde },
|
||||
Ï: { key: "KeyI", shift: true, accentKey: keyTrema },
|
||||
Í: { key: "KeyI", shift: true, accentKey: keyAcute },
|
||||
Î: { key: "KeyI", shift: true, accentKey: keyHat },
|
||||
Ì: { key: "KeyI", shift: true, accentKey: keyGrave },
|
||||
Ĩ: { key: "KeyI", shift: true, accentKey: keyTilde },
|
||||
J: { key: "KeyJ", shift: true },
|
||||
K: { key: "KeyK", shift: true },
|
||||
L: { key: "KeyL", shift: true },
|
||||
M: { key: "KeyM", shift: true },
|
||||
N: { key: "KeyN", shift: true },
|
||||
O: { key: "KeyO", shift: true },
|
||||
"Ö": { key: "KeyO", shift: true, accentKey: keyTrema },
|
||||
"Ó": { key: "KeyO", shift: true, accentKey: keyAcute },
|
||||
"Ô": { key: "KeyO", shift: true, accentKey: keyHat },
|
||||
"Ò": { key: "KeyO", shift: true, accentKey: keyGrave },
|
||||
"Õ": { key: "KeyO", shift: true, accentKey: keyTilde },
|
||||
Ö: { key: "KeyO", shift: true, accentKey: keyTrema },
|
||||
Ó: { key: "KeyO", shift: true, accentKey: keyAcute },
|
||||
Ô: { key: "KeyO", shift: true, accentKey: keyHat },
|
||||
Ò: { key: "KeyO", shift: true, accentKey: keyGrave },
|
||||
Õ: { key: "KeyO", shift: true, accentKey: keyTilde },
|
||||
P: { key: "KeyP", shift: true },
|
||||
Q: { key: "KeyQ", shift: true },
|
||||
R: { key: "KeyR", shift: true },
|
||||
S: { key: "KeyS", shift: true },
|
||||
T: { key: "KeyT", shift: true },
|
||||
U: { key: "KeyU", shift: true },
|
||||
"Ü": { key: "KeyU", shift: true, accentKey: keyTrema },
|
||||
"Ú": { key: "KeyU", shift: true, accentKey: keyAcute },
|
||||
"Û": { key: "KeyU", shift: true, accentKey: keyHat },
|
||||
"Ù": { key: "KeyU", shift: true, accentKey: keyGrave },
|
||||
"Ũ": { key: "KeyU", shift: true, accentKey: keyTilde },
|
||||
Ü: { key: "KeyU", shift: true, accentKey: keyTrema },
|
||||
Ú: { key: "KeyU", shift: true, accentKey: keyAcute },
|
||||
Û: { key: "KeyU", shift: true, accentKey: keyHat },
|
||||
Ù: { key: "KeyU", shift: true, accentKey: keyGrave },
|
||||
Ũ: { key: "KeyU", shift: true, accentKey: keyTilde },
|
||||
V: { key: "KeyV", shift: true },
|
||||
W: { key: "KeyW", shift: true },
|
||||
X: { key: "KeyX", shift: true },
|
||||
Y: { key: "KeyZ", shift: true },
|
||||
Z: { key: "KeyY", shift: true },
|
||||
a: { key: "KeyA" },
|
||||
"ä": { key: "KeyA", accentKey: keyTrema },
|
||||
"á": { key: "KeyA", accentKey: keyAcute },
|
||||
"â": { key: "KeyA", accentKey: keyHat },
|
||||
"à": { key: "KeyA", accentKey: keyGrave },
|
||||
"ã": { key: "KeyA", accentKey: keyTilde },
|
||||
ä: { key: "KeyA", accentKey: keyTrema },
|
||||
á: { key: "KeyA", accentKey: keyAcute },
|
||||
â: { key: "KeyA", accentKey: keyHat },
|
||||
à: { key: "KeyA", accentKey: keyGrave },
|
||||
ã: { key: "KeyA", accentKey: keyTilde },
|
||||
b: { key: "KeyB" },
|
||||
c: { key: "KeyC" },
|
||||
d: { key: "KeyD" },
|
||||
e: { key: "KeyE" },
|
||||
"ë": { key: "KeyE", accentKey: keyTrema },
|
||||
"é": { key: "KeyE", accentKey: keyAcute },
|
||||
"ê": { key: "KeyE", accentKey: keyHat },
|
||||
"è": { key: "KeyE", accentKey: keyGrave },
|
||||
"ẽ": { key: "KeyE", accentKey: keyTilde },
|
||||
ë: { key: "KeyE", accentKey: keyTrema },
|
||||
é: { key: "KeyE", accentKey: keyAcute },
|
||||
ê: { key: "KeyE", accentKey: keyHat },
|
||||
è: { key: "KeyE", accentKey: keyGrave },
|
||||
ẽ: { key: "KeyE", accentKey: keyTilde },
|
||||
"€": { key: "KeyE", altRight: true },
|
||||
f: { key: "KeyF" },
|
||||
g: { key: "KeyG" },
|
||||
h: { key: "KeyH" },
|
||||
i: { key: "KeyI" },
|
||||
"ï": { key: "KeyI", accentKey: keyTrema },
|
||||
"í": { key: "KeyI", accentKey: keyAcute },
|
||||
"î": { key: "KeyI", accentKey: keyHat },
|
||||
"ì": { key: "KeyI", accentKey: keyGrave },
|
||||
"ĩ": { key: "KeyI", accentKey: keyTilde },
|
||||
ï: { key: "KeyI", accentKey: keyTrema },
|
||||
í: { key: "KeyI", accentKey: keyAcute },
|
||||
î: { key: "KeyI", accentKey: keyHat },
|
||||
ì: { key: "KeyI", accentKey: keyGrave },
|
||||
ĩ: { key: "KeyI", accentKey: keyTilde },
|
||||
j: { key: "KeyJ" },
|
||||
k: { key: "KeyK" },
|
||||
l: { key: "KeyL" },
|
||||
m: { key: "KeyM" },
|
||||
n: { key: "KeyN" },
|
||||
o: { key: "KeyO" },
|
||||
"ö": { key: "KeyO", accentKey: keyTrema },
|
||||
"ó": { key: "KeyO", accentKey: keyAcute },
|
||||
"ô": { key: "KeyO", accentKey: keyHat },
|
||||
"ò": { key: "KeyO", accentKey: keyGrave },
|
||||
"õ": { key: "KeyO", accentKey: keyTilde },
|
||||
ö: { key: "KeyO", accentKey: keyTrema },
|
||||
ó: { key: "KeyO", accentKey: keyAcute },
|
||||
ô: { key: "KeyO", accentKey: keyHat },
|
||||
ò: { key: "KeyO", accentKey: keyGrave },
|
||||
õ: { key: "KeyO", accentKey: keyTilde },
|
||||
p: { key: "KeyP" },
|
||||
q: { key: "KeyQ" },
|
||||
r: { key: "KeyR" },
|
||||
s: { key: "KeyS" },
|
||||
t: { key: "KeyT" },
|
||||
u: { key: "KeyU" },
|
||||
"ü": { key: "KeyU", accentKey: keyTrema },
|
||||
"ú": { key: "KeyU", accentKey: keyAcute },
|
||||
"û": { key: "KeyU", accentKey: keyHat },
|
||||
"ù": { key: "KeyU", accentKey: keyGrave },
|
||||
"ũ": { key: "KeyU", accentKey: keyTilde },
|
||||
ü: { key: "KeyU", accentKey: keyTrema },
|
||||
ú: { key: "KeyU", accentKey: keyAcute },
|
||||
û: { key: "KeyU", accentKey: keyHat },
|
||||
ù: { key: "KeyU", accentKey: keyGrave },
|
||||
ũ: { key: "KeyU", accentKey: keyTilde },
|
||||
v: { key: "KeyV" },
|
||||
w: { key: "KeyW" },
|
||||
x: { key: "KeyX" },
|
||||
|
|
@ -120,14 +120,14 @@ const chars = {
|
|||
1: { key: "Digit1" },
|
||||
"!": { key: "Digit1", shift: true },
|
||||
2: { key: "Digit2" },
|
||||
"\"": { key: "Digit2", shift: true },
|
||||
'"': { key: "Digit2", shift: true },
|
||||
"@": { key: "Digit2", altRight: true },
|
||||
3: { key: "Digit3" },
|
||||
"#": { key: "Digit3", shift: true },
|
||||
"£": { key: "Digit3", altRight: true },
|
||||
4: { key: "Digit4" },
|
||||
"¤": { key: "Digit4", shift: true },
|
||||
"$": { key: "Digit4", altRight: true },
|
||||
$: { key: "Digit4", altRight: true },
|
||||
5: { key: "Digit5" },
|
||||
"%": { key: "Digit5", shift: true },
|
||||
6: { key: "Digit6" },
|
||||
|
|
@ -147,12 +147,12 @@ const chars = {
|
|||
"+": { key: "Minus" },
|
||||
"?": { key: "Minus", shift: true },
|
||||
"\\": { key: "Equal" },
|
||||
"å": { key: "BracketLeft" },
|
||||
"Å": { key: "BracketLeft", shift: true },
|
||||
"ø": { key: "Semicolon" },
|
||||
"Ø": { key: "Semicolon", shift: true },
|
||||
"æ": { key: "Quote" },
|
||||
"Æ": { key: "Quote", shift: true },
|
||||
å: { key: "BracketLeft" },
|
||||
Å: { key: "BracketLeft", shift: true },
|
||||
ø: { key: "Semicolon" },
|
||||
Ø: { key: "Semicolon", shift: true },
|
||||
æ: { key: "Quote" },
|
||||
Æ: { key: "Quote", shift: true },
|
||||
"'": { key: "Backslash" },
|
||||
"*": { key: "Backslash", shift: true },
|
||||
",": { key: "Comma" },
|
||||
|
|
@ -160,7 +160,7 @@ const chars = {
|
|||
".": { key: "Period" },
|
||||
":": { key: "Period", shift: true },
|
||||
"-": { key: "Slash" },
|
||||
"_": { key: "Slash", shift: true },
|
||||
_: { key: "Slash", shift: true },
|
||||
"<": { key: "IntlBackslash" },
|
||||
">": { key: "IntlBackslash", shift: true },
|
||||
" ": { key: "Space" },
|
||||
|
|
@ -176,5 +176,5 @@ export const nb_NO: KeyboardLayout = {
|
|||
// TODO need to localize these maps and layouts
|
||||
keyDisplayMap: en_US.keyDisplayMap,
|
||||
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 isoCode = "sv-SE";
|
||||
|
||||
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 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 keyTilde: KeyCombo = { key: "BracketRight", altRight: true } // tilde, mark ~ placed above the letter
|
||||
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 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 keyTilde: KeyCombo = { key: "BracketRight", altRight: true }; // tilde, mark ~ placed above the letter
|
||||
|
||||
const chars = {
|
||||
A: { key: "KeyA", shift: true },
|
||||
"Á": { key: "KeyA", shift: true, accentKey: keyAcute },
|
||||
"Â": { key: "KeyA", shift: true, accentKey: keyHat },
|
||||
"À": { key: "KeyA", shift: true, accentKey: keyGrave },
|
||||
"Ã": { key: "KeyA", shift: true, accentKey: keyTilde },
|
||||
Á: { key: "KeyA", shift: true, accentKey: keyAcute },
|
||||
Â: { key: "KeyA", shift: true, accentKey: keyHat },
|
||||
À: { key: "KeyA", shift: true, accentKey: keyGrave },
|
||||
Ã: { key: "KeyA", shift: true, accentKey: keyTilde },
|
||||
B: { key: "KeyB", shift: true },
|
||||
C: { key: "KeyC", shift: true },
|
||||
D: { key: "KeyD", shift: true },
|
||||
E: { key: "KeyE", shift: true },
|
||||
"Ë": { key: "KeyE", shift: true, accentKey: keyTrema },
|
||||
"É": { key: "KeyE", shift: true, accentKey: keyAcute },
|
||||
"Ê": { key: "KeyE", shift: true, accentKey: keyHat },
|
||||
"È": { key: "KeyE", shift: true, accentKey: keyGrave },
|
||||
"Ẽ": { key: "KeyE", shift: true, accentKey: keyTilde },
|
||||
Ë: { key: "KeyE", shift: true, accentKey: keyTrema },
|
||||
É: { key: "KeyE", shift: true, accentKey: keyAcute },
|
||||
Ê: { key: "KeyE", shift: true, accentKey: keyHat },
|
||||
È: { key: "KeyE", shift: true, accentKey: keyGrave },
|
||||
Ẽ: { key: "KeyE", shift: true, accentKey: keyTilde },
|
||||
F: { key: "KeyF", shift: true },
|
||||
G: { key: "KeyG", shift: true },
|
||||
H: { key: "KeyH", shift: true },
|
||||
I: { key: "KeyI", shift: true },
|
||||
"Ï": { key: "KeyI", shift: true, accentKey: keyTrema },
|
||||
"Í": { key: "KeyI", shift: true, accentKey: keyAcute },
|
||||
"Î": { key: "KeyI", shift: true, accentKey: keyHat },
|
||||
"Ì": { key: "KeyI", shift: true, accentKey: keyGrave },
|
||||
"Ĩ": { key: "KeyI", shift: true, accentKey: keyTilde },
|
||||
Ï: { key: "KeyI", shift: true, accentKey: keyTrema },
|
||||
Í: { key: "KeyI", shift: true, accentKey: keyAcute },
|
||||
Î: { key: "KeyI", shift: true, accentKey: keyHat },
|
||||
Ì: { key: "KeyI", shift: true, accentKey: keyGrave },
|
||||
Ĩ: { key: "KeyI", shift: true, accentKey: keyTilde },
|
||||
J: { key: "KeyJ", shift: true },
|
||||
K: { key: "KeyK", shift: true },
|
||||
L: { key: "KeyL", shift: true },
|
||||
M: { key: "KeyM", shift: true },
|
||||
N: { key: "KeyN", shift: true },
|
||||
O: { key: "KeyO", shift: true },
|
||||
"Ó": { key: "KeyO", shift: true, accentKey: keyAcute },
|
||||
"Ô": { key: "KeyO", shift: true, accentKey: keyHat },
|
||||
"Ò": { key: "KeyO", shift: true, accentKey: keyGrave },
|
||||
"Õ": { key: "KeyO", shift: true, accentKey: keyTilde },
|
||||
Ó: { key: "KeyO", shift: true, accentKey: keyAcute },
|
||||
Ô: { key: "KeyO", shift: true, accentKey: keyHat },
|
||||
Ò: { key: "KeyO", shift: true, accentKey: keyGrave },
|
||||
Õ: { key: "KeyO", shift: true, accentKey: keyTilde },
|
||||
P: { key: "KeyP", shift: true },
|
||||
Q: { key: "KeyQ", shift: true },
|
||||
R: { key: "KeyR", shift: true },
|
||||
S: { key: "KeyS", shift: true },
|
||||
T: { key: "KeyT", shift: true },
|
||||
U: { key: "KeyU", shift: true },
|
||||
"Ü": { key: "KeyU", shift: true, accentKey: keyTrema },
|
||||
"Ú": { key: "KeyU", shift: true, accentKey: keyAcute },
|
||||
"Û": { key: "KeyU", shift: true, accentKey: keyHat },
|
||||
"Ù": { key: "KeyU", shift: true, accentKey: keyGrave },
|
||||
"Ũ": { key: "KeyU", shift: true, accentKey: keyTilde },
|
||||
Ü: { key: "KeyU", shift: true, accentKey: keyTrema },
|
||||
Ú: { key: "KeyU", shift: true, accentKey: keyAcute },
|
||||
Û: { key: "KeyU", shift: true, accentKey: keyHat },
|
||||
Ù: { key: "KeyU", shift: true, accentKey: keyGrave },
|
||||
Ũ: { key: "KeyU", shift: true, accentKey: keyTilde },
|
||||
V: { key: "KeyV", shift: true },
|
||||
W: { key: "KeyW", shift: true },
|
||||
X: { key: "KeyX", shift: true },
|
||||
Y: { key: "KeyY", shift: true },
|
||||
Z: { key: "KeyZ", shift: true },
|
||||
a: { key: "KeyA" },
|
||||
"á": { key: "KeyA", accentKey: keyAcute },
|
||||
"â": { key: "KeyA", accentKey: keyHat },
|
||||
"à": { key: "KeyA", accentKey: keyGrave },
|
||||
"ã": { key: "KeyA", accentKey: keyTilde },
|
||||
á: { key: "KeyA", accentKey: keyAcute },
|
||||
â: { key: "KeyA", accentKey: keyHat },
|
||||
à: { key: "KeyA", accentKey: keyGrave },
|
||||
ã: { key: "KeyA", accentKey: keyTilde },
|
||||
b: { key: "KeyB" },
|
||||
c: { key: "KeyC" },
|
||||
d: { key: "KeyD" },
|
||||
e: { key: "KeyE" },
|
||||
"ë": { key: "KeyE", accentKey: keyTrema },
|
||||
"é": { key: "KeyE", accentKey: keyAcute },
|
||||
"ê": { key: "KeyE", accentKey: keyHat },
|
||||
"è": { key: "KeyE", accentKey: keyGrave },
|
||||
"ẽ": { key: "KeyE", accentKey: keyTilde },
|
||||
ë: { key: "KeyE", accentKey: keyTrema },
|
||||
é: { key: "KeyE", accentKey: keyAcute },
|
||||
ê: { key: "KeyE", accentKey: keyHat },
|
||||
è: { key: "KeyE", accentKey: keyGrave },
|
||||
ẽ: { key: "KeyE", accentKey: keyTilde },
|
||||
"€": { key: "KeyE", altRight: true },
|
||||
f: { key: "KeyF" },
|
||||
g: { key: "KeyG" },
|
||||
h: { key: "KeyH" },
|
||||
i: { key: "KeyI" },
|
||||
"ï": { key: "KeyI", accentKey: keyTrema },
|
||||
"í": { key: "KeyI", accentKey: keyAcute },
|
||||
"î": { key: "KeyI", accentKey: keyHat },
|
||||
"ì": { key: "KeyI", accentKey: keyGrave },
|
||||
"ĩ": { key: "KeyI", accentKey: keyTilde },
|
||||
ï: { key: "KeyI", accentKey: keyTrema },
|
||||
í: { key: "KeyI", accentKey: keyAcute },
|
||||
î: { key: "KeyI", accentKey: keyHat },
|
||||
ì: { key: "KeyI", accentKey: keyGrave },
|
||||
ĩ: { key: "KeyI", accentKey: keyTilde },
|
||||
j: { key: "KeyJ" },
|
||||
k: { key: "KeyK" },
|
||||
l: { key: "KeyL" },
|
||||
m: { key: "KeyM" },
|
||||
n: { key: "KeyN" },
|
||||
o: { key: "KeyO" },
|
||||
"ó": { key: "KeyO", accentKey: keyAcute },
|
||||
"ô": { key: "KeyO", accentKey: keyHat },
|
||||
"ò": { key: "KeyO", accentKey: keyGrave },
|
||||
"õ": { key: "KeyO", accentKey: keyTilde },
|
||||
ó: { key: "KeyO", accentKey: keyAcute },
|
||||
ô: { key: "KeyO", accentKey: keyHat },
|
||||
ò: { key: "KeyO", accentKey: keyGrave },
|
||||
õ: { key: "KeyO", accentKey: keyTilde },
|
||||
p: { key: "KeyP" },
|
||||
q: { key: "KeyQ" },
|
||||
r: { key: "KeyR" },
|
||||
s: { key: "KeyS" },
|
||||
t: { key: "KeyT" },
|
||||
u: { key: "KeyU" },
|
||||
"ü": { key: "KeyU", accentKey: keyTrema },
|
||||
"ú": { key: "KeyU", accentKey: keyAcute },
|
||||
"û": { key: "KeyU", accentKey: keyHat },
|
||||
"ù": { key: "KeyU", accentKey: keyGrave },
|
||||
"ũ": { key: "KeyU", accentKey: keyTilde },
|
||||
ü: { key: "KeyU", accentKey: keyTrema },
|
||||
ú: { key: "KeyU", accentKey: keyAcute },
|
||||
û: { key: "KeyU", accentKey: keyHat },
|
||||
ù: { key: "KeyU", accentKey: keyGrave },
|
||||
ũ: { key: "KeyU", accentKey: keyTilde },
|
||||
v: { key: "KeyV" },
|
||||
w: { key: "KeyW" },
|
||||
x: { key: "KeyX" },
|
||||
|
|
@ -116,14 +116,14 @@ const chars = {
|
|||
1: { key: "Digit1" },
|
||||
"!": { key: "Digit1", shift: true },
|
||||
2: { key: "Digit2" },
|
||||
"\"": { key: "Digit2", shift: true },
|
||||
'"': { key: "Digit2", shift: true },
|
||||
"@": { key: "Digit2", altRight: true },
|
||||
3: { key: "Digit3" },
|
||||
"#": { key: "Digit3", shift: true },
|
||||
"£": { key: "Digit3", altRight: true },
|
||||
4: { key: "Digit4" },
|
||||
"¤": { key: "Digit4", shift: true },
|
||||
"$": { key: "Digit4", altRight: true },
|
||||
$: { key: "Digit4", altRight: true },
|
||||
5: { key: "Digit5" },
|
||||
"%": { key: "Digit5", shift: true },
|
||||
6: { key: "Digit6" },
|
||||
|
|
@ -143,12 +143,12 @@ const chars = {
|
|||
"+": { key: "Minus" },
|
||||
"?": { key: "Minus", shift: true },
|
||||
"\\": { key: "Minus", altRight: true },
|
||||
"å": { key: "BracketLeft" },
|
||||
"Å": { key: "BracketLeft", shift: true },
|
||||
"ö": { key: "Semicolon" },
|
||||
"Ö": { key: "Semicolon", shift: true },
|
||||
"ä": { key: "Quote" },
|
||||
"Ä": { key: "Quote", shift: true },
|
||||
å: { key: "BracketLeft" },
|
||||
Å: { key: "BracketLeft", shift: true },
|
||||
ö: { key: "Semicolon" },
|
||||
Ö: { key: "Semicolon", shift: true },
|
||||
ä: { key: "Quote" },
|
||||
Ä: { key: "Quote", shift: true },
|
||||
"'": { key: "Backslash" },
|
||||
"*": { key: "Backslash", shift: true },
|
||||
",": { key: "Comma" },
|
||||
|
|
@ -156,7 +156,7 @@ const chars = {
|
|||
".": { key: "Period" },
|
||||
":": { key: "Period", shift: true },
|
||||
"-": { key: "Slash" },
|
||||
"_": { key: "Slash", shift: true },
|
||||
_: { key: "Slash", shift: true },
|
||||
"<": { key: "IntlBackslash" },
|
||||
">": { key: "IntlBackslash", shift: true },
|
||||
"|": { key: "IntlBackslash", altRight: true },
|
||||
|
|
@ -173,5 +173,5 @@ export const sv_SE: KeyboardLayout = {
|
|||
// TODO need to localize these maps and layouts
|
||||
keyDisplayMap: en_US.keyDisplayMap,
|
||||
modifierDisplayMap: en_US.modifierDisplayMap,
|
||||
virtualKeyboard: en_US.virtualKeyboard
|
||||
virtualKeyboard: en_US.virtualKeyboard,
|
||||
};
|
||||
|
|
@ -251,7 +251,7 @@ export const deadKeys = {
|
|||
Slash: 0x02f8,
|
||||
Tilde: 0x007e,
|
||||
Umlaut: 0x00a8,
|
||||
} as Record<string, number>
|
||||
} as Record<string, number>;
|
||||
|
||||
export const modifiers = {
|
||||
ControlLeft: 0x01,
|
||||
|
|
|
|||
|
|
@ -43,10 +43,16 @@ const SettingsHardwareRoute = lazy(() => import("@routes/devices.$id.settings.ha
|
|||
const SettingsVideoRoute = lazy(() => import("@routes/devices.$id.settings.video"));
|
||||
const SettingsAppearanceRoute = lazy(() => import("@routes/devices.$id.settings.appearance"));
|
||||
const SettingsGeneralIndexRoute = lazy(() => import("@routes/devices.$id.settings.general._index"));
|
||||
const SettingsGeneralRebootRoute = lazy(() => import("@routes/devices.$id.settings.general.reboot"));
|
||||
const SettingsGeneralUpdateRoute = lazy(() => import("@routes/devices.$id.settings.general.update"));
|
||||
const SettingsGeneralRebootRoute = lazy(
|
||||
() => 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 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 SettingsMacrosAddRoute = lazy(() => import("@routes/devices.$id.settings.macros.add"));
|
||||
const SettingsMacrosEditRoute = lazy(() => import("@routes/devices.$id.settings.macros.edit"));
|
||||
|
|
@ -364,7 +370,7 @@ if (isOnDevice) {
|
|||
{
|
||||
path: "devices",
|
||||
element: <DevicesRoute />,
|
||||
loader: DevicesRoute.loader
|
||||
loader: DevicesRoute.loader,
|
||||
},
|
||||
],
|
||||
},
|
||||
|
|
|
|||
|
|
@ -19,7 +19,8 @@ const ToastContent = ({
|
|||
t: Toast;
|
||||
}) => (
|
||||
<Card
|
||||
className={`${t.visible ? "animate-enter" : "animate-leave"
|
||||
className={`${
|
||||
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">
|
||||
|
|
@ -34,7 +35,7 @@ const notifications = {
|
|||
return toast.custom(
|
||||
(t: Toast) => (
|
||||
<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}
|
||||
t={t}
|
||||
/>
|
||||
|
|
@ -47,7 +48,7 @@ const notifications = {
|
|||
return toast.custom(
|
||||
(t: Toast) => (
|
||||
<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}
|
||||
t={t}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -31,7 +31,5 @@ export const FeatureFlagProvider = ({
|
|||
|
||||
const value = { appVersion, isFeatureEnabled };
|
||||
|
||||
return (
|
||||
<FeatureFlagContext.Provider value={value}>{children}</FeatureFlagContext.Provider>
|
||||
);
|
||||
return <FeatureFlagContext.Provider value={value}>{children}</FeatureFlagContext.Provider>;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ const loader: LoaderFunction = async ({ request }: LoaderFunctionArgs) => {
|
|||
};
|
||||
|
||||
export default function AdoptRoute() {
|
||||
return (<></>);
|
||||
return <></>;
|
||||
}
|
||||
|
||||
AdoptRoute.loader = loader;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,10 @@
|
|||
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 { User } from "@hooks/stores";
|
||||
|
|
@ -76,9 +81,9 @@ export default function DevicesIdDeregister() {
|
|||
kvmName={device?.name}
|
||||
/>
|
||||
|
||||
<div className="w-full h-full">
|
||||
<div className="h-full w-full">
|
||||
<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">
|
||||
<LinkButton
|
||||
size="SM"
|
||||
|
|
|
|||
|
|
@ -1,11 +1,6 @@
|
|||
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
import { useNavigate } from "react-router";
|
||||
import {
|
||||
LuLink,
|
||||
LuRadioReceiver,
|
||||
LuCheck,
|
||||
LuUpload,
|
||||
} from "react-icons/lu";
|
||||
import { LuLink, LuRadioReceiver, LuCheck, LuUpload } from "react-icons/lu";
|
||||
import { PlusCircleIcon, ExclamationTriangleIcon } from "@heroicons/react/20/solid";
|
||||
import { TrashIcon } from "@heroicons/react/16/solid";
|
||||
|
||||
|
|
@ -43,13 +38,8 @@ export default function MountRoute() {
|
|||
}
|
||||
|
||||
export function Dialog({ onClose }: Readonly<{ onClose: () => void }>) {
|
||||
const {
|
||||
modalView,
|
||||
setModalView,
|
||||
setRemoteVirtualMediaState,
|
||||
errorMessage,
|
||||
setErrorMessage,
|
||||
} = useMountMediaStore();
|
||||
const { modalView, setModalView, setRemoteVirtualMediaState, errorMessage, setErrorMessage } =
|
||||
useMountMediaStore();
|
||||
const navigate = useNavigate();
|
||||
|
||||
const [incompleteFileName, setIncompleteFileName] = useState<string | null>(null);
|
||||
|
|
@ -65,9 +55,7 @@ export function Dialog({ onClose }: Readonly<{ onClose: () => void }>) {
|
|||
if ("error" in resp) {
|
||||
reject(new Error(resp.error.message));
|
||||
} else {
|
||||
setRemoteVirtualMediaState(
|
||||
resp as unknown as MountMediaState["remoteVirtualMediaState"],
|
||||
);
|
||||
setRemoteVirtualMediaState(resp as unknown as MountMediaState["remoteVirtualMediaState"]);
|
||||
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", {
|
||||
"max-w-4xl": modalView === "mode",
|
||||
"max-w-2xl": modalView === "device",
|
||||
"max-w-xl":
|
||||
modalView === "url" ||
|
||||
modalView === "upload" ||
|
||||
modalView === "error",
|
||||
"max-w-xl": modalView === "url" || modalView === "upload" || modalView === "error",
|
||||
})}
|
||||
>
|
||||
<GridCard cardClassName="relative w-full text-left pointer-events-auto">
|
||||
|
|
@ -281,9 +266,7 @@ function ModeSelectionView({
|
|||
>
|
||||
<div
|
||||
className="relative z-50 flex flex-col items-start p-4 select-none"
|
||||
onClick={() =>
|
||||
disabled ? null : setSelectedMode(mode as "url" | "device")
|
||||
}
|
||||
onClick={() => (disabled ? null : setSelectedMode(mode as "url" | "device"))}
|
||||
>
|
||||
<div>
|
||||
<Card>
|
||||
|
|
@ -293,14 +276,10 @@ function ModeSelectionView({
|
|||
</Card>
|
||||
</div>
|
||||
<div className="mt-2 space-y-0">
|
||||
<p className="block pt-1 text-xs text-red-500">
|
||||
{tag ? tag : <> </>}
|
||||
</p>
|
||||
<p className="block pt-1 text-xs text-red-500">{tag ? tag : <> </>}</p>
|
||||
|
||||
<h3 className="text-sm font-medium dark:text-white">{label}</h3>
|
||||
<p className="text-sm text-gray-700 dark:text-slate-400">
|
||||
{description}
|
||||
</p>
|
||||
<p className="text-sm text-gray-700 dark:text-slate-400">{description}</p>
|
||||
</div>
|
||||
<input
|
||||
type="radio"
|
||||
|
|
@ -414,10 +393,7 @@ function UrlView({
|
|||
|
||||
return (
|
||||
<div className="w-full space-y-4">
|
||||
<ViewHeader
|
||||
title={m.mount_view_url_title()}
|
||||
description={m.mount_view_url_description()}
|
||||
/>
|
||||
<ViewHeader title={m.mount_view_url_title()} description={m.mount_view_url_description()} />
|
||||
|
||||
<div
|
||||
className="animate-fadeIn opacity-0"
|
||||
|
|
@ -452,9 +428,7 @@ function UrlView({
|
|||
loading={mountInProgress}
|
||||
text={m.mount_button_mount_url()}
|
||||
onClick={() => onMount(url, usbMode)}
|
||||
disabled={
|
||||
mountInProgress || !isUrlValid || url.length === 0
|
||||
}
|
||||
disabled={mountInProgress || !isUrlValid || url.length === 0}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -538,10 +512,9 @@ function DeviceFileView({
|
|||
const percentageUsed = useMemo(() => {
|
||||
if (!storageSpace) return 0;
|
||||
return Number(
|
||||
(
|
||||
(storageSpace.bytesUsed / (storageSpace.bytesUsed + storageSpace.bytesFree)) *
|
||||
100
|
||||
).toFixed(1),
|
||||
((storageSpace.bytesUsed / (storageSpace.bytesUsed + storageSpace.bytesFree)) * 100).toFixed(
|
||||
1,
|
||||
),
|
||||
);
|
||||
}, [storageSpace]);
|
||||
|
||||
|
|
@ -681,9 +654,7 @@ function DeviceFileView({
|
|||
onDelete={() => {
|
||||
const selectedFile = onStorageFiles.find(f => f.name === file.name);
|
||||
if (!selectedFile) return;
|
||||
if (
|
||||
window.confirm(m.mount_confirm_delete({ name: selectedFile.name }))
|
||||
) {
|
||||
if (window.confirm(m.mount_confirm_delete({ name: selectedFile.name }))) {
|
||||
handleDeleteFile(selectedFile);
|
||||
}
|
||||
}}
|
||||
|
|
@ -698,7 +669,7 @@ function DeviceFileView({
|
|||
{m.mount_button_showing_results({
|
||||
from: indexOfFirstFile + 1,
|
||||
to: Math.min(indexOfLastFile, onStorageFiles.length),
|
||||
total: onStorageFiles.length
|
||||
total: onStorageFiles.length,
|
||||
})}
|
||||
</p>
|
||||
<div className="flex items-center gap-x-2">
|
||||
|
|
@ -827,9 +798,7 @@ function UploadFileView({
|
|||
onCancelUpload: () => void;
|
||||
incompleteFileName?: string;
|
||||
}) {
|
||||
const [uploadState, setUploadState] = useState<"idle" | "uploading" | "success">(
|
||||
"idle",
|
||||
);
|
||||
const [uploadState, setUploadState] = useState<"idle" | "uploading" | "success">("idle");
|
||||
const [uploadProgress, setUploadProgress] = useState(0);
|
||||
const [uploadedFileName, setUploadedFileName] = useState<string | null>(null);
|
||||
const [uploadedFileSize, setUploadedFileSize] = useState<number | null>(null);
|
||||
|
|
@ -854,14 +823,8 @@ function UploadFileView({
|
|||
};
|
||||
}, []);
|
||||
|
||||
function handleWebRTCUpload(
|
||||
file: File,
|
||||
alreadyUploadedBytes: number,
|
||||
dataChannel: string,
|
||||
) {
|
||||
const rtcDataChannel = useRTCStore
|
||||
.getState()
|
||||
.peerConnection?.createDataChannel(dataChannel);
|
||||
function handleWebRTCUpload(file: File, alreadyUploadedBytes: number, dataChannel: string) {
|
||||
const rtcDataChannel = useRTCStore.getState().peerConnection?.createDataChannel(dataChannel);
|
||||
|
||||
if (!rtcDataChannel) {
|
||||
console.error("Failed to create data channel for file upload");
|
||||
|
|
@ -903,8 +866,7 @@ function UploadFileView({
|
|||
}
|
||||
|
||||
// Calculate average speed
|
||||
const averageSpeed =
|
||||
speedHistory.reduce((a, b) => a + b, 0) / speedHistory.length;
|
||||
const averageSpeed = speedHistory.reduce((a, b) => a + b, 0) / speedHistory.length;
|
||||
|
||||
setUploadSpeed(averageSpeed);
|
||||
setUploadProgress((AlreadyUploadedBytes / Size) * 100);
|
||||
|
|
@ -961,11 +923,7 @@ function UploadFileView({
|
|||
};
|
||||
}
|
||||
|
||||
async function handleHttpUpload(
|
||||
file: File,
|
||||
alreadyUploadedBytes: number,
|
||||
dataChannel: string,
|
||||
) {
|
||||
async function handleHttpUpload(file: File, alreadyUploadedBytes: number, dataChannel: string) {
|
||||
const uploadUrl = `${DEVICE_API}/storage/upload?uploadId=${dataChannel}`;
|
||||
|
||||
const xhr = new XMLHttpRequest();
|
||||
|
|
@ -994,8 +952,7 @@ function UploadFileView({
|
|||
}
|
||||
|
||||
// Calculate average speed
|
||||
const averageSpeed =
|
||||
speedHistory.reduce((a, b) => a + b, 0) / speedHistory.length;
|
||||
const averageSpeed = speedHistory.reduce((a, b) => a + b, 0) / speedHistory.length;
|
||||
|
||||
setUploadSpeed(averageSpeed);
|
||||
setUploadProgress((totalUploaded / totalSize) * 100);
|
||||
|
|
@ -1035,10 +992,7 @@ function UploadFileView({
|
|||
// Reset the upload error when a new file is selected
|
||||
setUploadError(null);
|
||||
|
||||
if (
|
||||
incompleteFileName &&
|
||||
file.name !== incompleteFileName.replace(".incomplete", "")
|
||||
) {
|
||||
if (incompleteFileName && file.name !== incompleteFileName.replace(".incomplete", "")) {
|
||||
setFileError(
|
||||
m.mount_please_select_file({ name: incompleteFileName.replace(".incomplete", "") }),
|
||||
);
|
||||
|
|
@ -1052,7 +1006,10 @@ function UploadFileView({
|
|||
setUploadState("uploading");
|
||||
console.log("Upload state set to 'uploading'");
|
||||
|
||||
send("startStorageFileUpload", { filename: file.name, size: file.size }, (resp: JsonRpcResponse) => {
|
||||
send(
|
||||
"startStorageFileUpload",
|
||||
{ filename: file.name, size: file.size },
|
||||
(resp: JsonRpcResponse) => {
|
||||
console.log("startStorageFileUpload response:", resp);
|
||||
if ("error" in resp) {
|
||||
console.error("Upload error:", resp.error.message);
|
||||
|
|
@ -1076,7 +1033,8 @@ function UploadFileView({
|
|||
} else {
|
||||
handleWebRTCUpload(file, alreadyUploadedBytes, dataChannel);
|
||||
}
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -1086,7 +1044,9 @@ function UploadFileView({
|
|||
title={m.mount_upload_title()}
|
||||
description={
|
||||
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()
|
||||
}
|
||||
/>
|
||||
|
|
@ -1124,7 +1084,9 @@ function UploadFileView({
|
|||
</div>
|
||||
<h3 className="text-sm leading-none font-semibold text-black dark:text-white">
|
||||
{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()}
|
||||
</h3>
|
||||
<p className="text-xs leading-none text-slate-700 dark:text-slate-300">
|
||||
|
|
@ -1143,7 +1105,9 @@ function UploadFileView({
|
|||
</Card>
|
||||
</div>
|
||||
<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>
|
||||
<p className="text-xs leading-none text-slate-700 dark:text-slate-300">
|
||||
{formatters.bytes(uploadedFileSize || 0)}
|
||||
|
|
@ -1180,7 +1144,9 @@ function UploadFileView({
|
|||
{m.mount_upload_successful()}
|
||||
</h3>
|
||||
<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>
|
||||
</div>
|
||||
)}
|
||||
|
|
@ -1196,15 +1162,13 @@ function UploadFileView({
|
|||
className="hidden"
|
||||
accept=".iso, .img"
|
||||
/>
|
||||
{fileError && (
|
||||
<p className="mt-2 text-sm text-red-600 dark:text-red-400">{fileError}</p>
|
||||
)}
|
||||
{fileError && <p className="mt-2 text-sm text-red-600 dark:text-red-400">{fileError}</p>}
|
||||
</div>
|
||||
|
||||
{/* Display upload error if present */}
|
||||
{uploadError && (
|
||||
<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" }}
|
||||
>
|
||||
{m.mount_upload_error({ error: String(uploadError) })}
|
||||
|
|
@ -1263,9 +1227,7 @@ function ErrorView({
|
|||
<ExclamationTriangleIcon className="h-6 w-6" />
|
||||
<h2 className="text-lg leading-tight font-bold">{m.mount_error_title()}</h2>
|
||||
</div>
|
||||
<p className="text-sm leading-snug text-slate-600">
|
||||
{m.mount_error_description()}
|
||||
</p>
|
||||
<p className="text-sm leading-snug text-slate-600">{m.mount_error_description()}</p>
|
||||
</div>
|
||||
{errorMessage && (
|
||||
<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">
|
||||
<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>
|
||||
);
|
||||
|
|
@ -1379,12 +1346,8 @@ function PreUploadedImageItem({
|
|||
function ViewHeader({ title, description }: { title: string; description: string }) {
|
||||
return (
|
||||
<div className="space-y-0">
|
||||
<h2 className="text-lg leading-tight font-bold text-black dark:text-white">
|
||||
{title}
|
||||
</h2>
|
||||
<div className="text-sm leading-snug text-slate-600 dark:text-slate-400">
|
||||
{description}
|
||||
</div>
|
||||
<h2 className="text-lg leading-tight font-bold text-black dark:text-white">{title}</h2>
|
||||
<div className="text-sm leading-snug text-slate-600 dark:text-slate-400">{description}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,14 +30,17 @@ export default function OtherSessionRoute() {
|
|||
</div>
|
||||
|
||||
<div className="text-left">
|
||||
<p className="text-base font-semibold dark:text-white">
|
||||
{m.other_session_detected()}
|
||||
</p>
|
||||
<p className="text-base font-semibold dark:text-white">{m.other_session_detected()}</p>
|
||||
<p className="mb-4 text-sm text-slate-600 dark:text-slate-400">
|
||||
{m.other_session_take_over()}
|
||||
</p>
|
||||
<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>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,10 @@
|
|||
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 { Button, LinkButton } from "@components/Button";
|
||||
|
|
|
|||
|
|
@ -5,10 +5,10 @@ import { getDeviceUiPath } from "@hooks/useAppNavigation";
|
|||
|
||||
const loader: LoaderFunction = ({ params }: LoaderFunctionArgs) => {
|
||||
return redirect(getDeviceUiPath("/settings/general", params.id));
|
||||
}
|
||||
};
|
||||
|
||||
export default function SettingIndexRoute() {
|
||||
return (<></>);
|
||||
return <></>;
|
||||
}
|
||||
|
||||
SettingIndexRoute.loader = loader;
|
||||
|
|
@ -125,10 +125,7 @@ export default function SettingsAccessIndexRoute() {
|
|||
returnTo.search = "";
|
||||
returnTo.hash = "";
|
||||
window.location.href =
|
||||
cloudAppUrl +
|
||||
"/signup?deviceId=" +
|
||||
deviceId +
|
||||
`&returnTo=${returnTo.toString()}`;
|
||||
cloudAppUrl + "/signup?deviceId=" + deviceId + `&returnTo=${returnTo.toString()}`;
|
||||
});
|
||||
},
|
||||
[deviceId, send],
|
||||
|
|
@ -168,7 +165,9 @@ export default function SettingsAccessIndexRoute() {
|
|||
|
||||
notifications.success(m.access_tls_updated());
|
||||
});
|
||||
}, [send]);
|
||||
},
|
||||
[send],
|
||||
);
|
||||
|
||||
// Handle TLS mode change
|
||||
const handleTlsModeChange = (value: string) => {
|
||||
|
|
@ -206,10 +205,7 @@ export default function SettingsAccessIndexRoute() {
|
|||
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<SettingsPageHeader
|
||||
title={m.access_title()}
|
||||
description={m.access_description()}
|
||||
/>
|
||||
<SettingsPageHeader title={m.access_title()} description={m.access_description()} />
|
||||
|
||||
{loaderData?.authMode && (
|
||||
<>
|
||||
|
|
@ -246,9 +242,7 @@ export default function SettingsAccessIndexRoute() {
|
|||
<TextAreaWithLabel
|
||||
label={m.access_certificate_label()}
|
||||
rows={3}
|
||||
placeholder={
|
||||
"-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----"
|
||||
}
|
||||
placeholder={"-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----"}
|
||||
value={tlsCert}
|
||||
onChange={e => handleTlsCertChange(e.target.value)}
|
||||
/>
|
||||
|
|
@ -256,9 +250,7 @@ export default function SettingsAccessIndexRoute() {
|
|||
label={m.access_private_key_label()}
|
||||
description={m.access_private_key_description()}
|
||||
rows={3}
|
||||
placeholder={
|
||||
"-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----"
|
||||
}
|
||||
placeholder={"-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----"}
|
||||
value={tlsKey}
|
||||
onChange={e => handleTlsKeyChange(e.target.value)}
|
||||
/>
|
||||
|
|
@ -275,7 +267,11 @@ export default function SettingsAccessIndexRoute() {
|
|||
|
||||
<SettingsItem
|
||||
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" ? (
|
||||
<Button
|
||||
|
|
@ -320,10 +316,7 @@ export default function SettingsAccessIndexRoute() {
|
|||
)}
|
||||
|
||||
<div className="space-y-4">
|
||||
<SettingsSectionHeader
|
||||
title="Remote"
|
||||
description={m.access_remote_description()}
|
||||
/>
|
||||
<SettingsSectionHeader title="Remote" description={m.access_remote_description()} />
|
||||
|
||||
<div className="space-y-4">
|
||||
{!isAdopted && (
|
||||
|
|
@ -438,11 +431,7 @@ export default function SettingsAccessIndexRoute() {
|
|||
className="text-red-600"
|
||||
onClick={() => {
|
||||
if (deviceId) {
|
||||
if (
|
||||
window.confirm(
|
||||
m.access_confirm_deregister(),
|
||||
)
|
||||
) {
|
||||
if (window.confirm(m.access_confirm_deregister())) {
|
||||
deregisterDevice();
|
||||
}
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -195,9 +195,7 @@ function CreatePasswordModal({
|
|||
}}
|
||||
>
|
||||
<div>
|
||||
<h2 className="text-lg font-semibold dark:text-white">
|
||||
{m.local_auth_create_title()}
|
||||
</h2>
|
||||
<h2 className="text-lg font-semibold dark:text-white">{m.local_auth_create_title()}</h2>
|
||||
<p className="text-sm text-slate-600 dark:text-slate-400">
|
||||
{m.local_auth_create_description()}
|
||||
</p>
|
||||
|
|
@ -225,7 +223,12 @@ function CreatePasswordModal({
|
|||
text={m.local_auth_create_secure_button()}
|
||||
onClick={() => onSetPassword(password, confirmPassword)}
|
||||
/>
|
||||
<Button size="SM" theme="light" text={m.local_auth_create_not_now_button()} onClick={onCancel} />
|
||||
<Button
|
||||
size="SM"
|
||||
theme="light"
|
||||
text={m.local_auth_create_not_now_button()}
|
||||
onClick={onCancel}
|
||||
/>
|
||||
</div>
|
||||
{error && <p className="text-sm text-red-500">{error}</p>}
|
||||
</form>
|
||||
|
|
@ -282,11 +285,7 @@ function UpdatePasswordModal({
|
|||
onCancel,
|
||||
error,
|
||||
}: {
|
||||
onUpdatePassword: (
|
||||
oldPassword: string,
|
||||
newPassword: string,
|
||||
confirmNewPassword: string,
|
||||
) => void;
|
||||
onUpdatePassword: (oldPassword: string, newPassword: string, confirmNewPassword: string) => void;
|
||||
onCancel: () => void;
|
||||
error: string | null;
|
||||
}) {
|
||||
|
|
|
|||
|
|
@ -81,8 +81,12 @@ export default function SettingsAdvancedRoute() {
|
|||
if ("error" in resp) {
|
||||
notifications.error(
|
||||
enabled
|
||||
? m.advanced_error_usb_emulation_enable({ error: resp.error.data || m.unknown_error() })
|
||||
: m.advanced_error_usb_emulation_disable({ error: resp.error.data || m.unknown_error() })
|
||||
? m.advanced_error_usb_emulation_enable({
|
||||
error: resp.error.data || m.unknown_error(),
|
||||
})
|
||||
: m.advanced_error_usb_emulation_disable({
|
||||
error: resp.error.data || m.unknown_error(),
|
||||
}),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
|
@ -97,7 +101,7 @@ export default function SettingsAdvancedRoute() {
|
|||
send("resetConfig", {}, (resp: JsonRpcResponse) => {
|
||||
if ("error" in resp) {
|
||||
notifications.error(
|
||||
m.advanced_error_reset_config({ error: resp.error.data || m.unknown_error() })
|
||||
m.advanced_error_reset_config({ error: resp.error.data || m.unknown_error() }),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
|
@ -109,7 +113,7 @@ export default function SettingsAdvancedRoute() {
|
|||
send("setSSHKeyState", { sshKey }, (resp: JsonRpcResponse) => {
|
||||
if ("error" in resp) {
|
||||
notifications.error(
|
||||
m.advanced_error_update_ssh_key({ error: resp.error.data || m.unknown_error() })
|
||||
m.advanced_error_update_ssh_key({ error: resp.error.data || m.unknown_error() }),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
|
@ -122,7 +126,7 @@ export default function SettingsAdvancedRoute() {
|
|||
send("setDevModeState", { enabled: developerMode }, (resp: JsonRpcResponse) => {
|
||||
if ("error" in resp) {
|
||||
notifications.error(
|
||||
m.advanced_error_set_dev_mode({ error: resp.error.data || m.unknown_error() })
|
||||
m.advanced_error_set_dev_mode({ error: resp.error.data || m.unknown_error() }),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
|
@ -137,7 +141,7 @@ export default function SettingsAdvancedRoute() {
|
|||
send("setDevChannelState", { enabled }, (resp: JsonRpcResponse) => {
|
||||
if ("error" in resp) {
|
||||
notifications.error(
|
||||
m.advanced_error_set_dev_channel({ error: resp.error.data || m.unknown_error() })
|
||||
m.advanced_error_set_dev_channel({ error: resp.error.data || m.unknown_error() }),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
|
@ -154,7 +158,7 @@ export default function SettingsAdvancedRoute() {
|
|||
notifications.error(
|
||||
enabled
|
||||
? m.advanced_error_loopback_enable({ error: resp.error.data || m.unknown_error() })
|
||||
: m.advanced_error_loopback_disable({ error: resp.error.data || m.unknown_error() })
|
||||
: m.advanced_error_loopback_disable({ error: resp.error.data || m.unknown_error() }),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
|
@ -190,9 +194,10 @@ export default function SettingsAdvancedRoute() {
|
|||
const handleVersionUpdateError = useCallback((error?: JsonRpcError | string) => {
|
||||
notifications.error(
|
||||
m.advanced_error_version_update({
|
||||
error: typeof error === "string" ? error : (error?.data ?? error?.message ?? m.unknown_error())
|
||||
error:
|
||||
typeof error === "string" ? error : (error?.data ?? error?.message ?? m.unknown_error()),
|
||||
}),
|
||||
{ duration: 1000 * 15 } // 15 seconds
|
||||
{ duration: 1000 * 15 }, // 15 seconds
|
||||
);
|
||||
setCustomVersionUpdateLoading(false);
|
||||
}, []);
|
||||
|
|
@ -200,16 +205,15 @@ export default function SettingsAdvancedRoute() {
|
|||
const handleCustomVersionUpdate = useCallback(async () => {
|
||||
const components: UpdateComponents = {};
|
||||
if (["app", "both"].includes(updateTarget) && appVersion) components.app = appVersion;
|
||||
if (["system", "both"].includes(updateTarget) && systemVersion) components.system = systemVersion;
|
||||
if (["system", "both"].includes(updateTarget) && systemVersion)
|
||||
components.system = systemVersion;
|
||||
let versionInfo: SystemVersionInfo | undefined;
|
||||
|
||||
try {
|
||||
// we do not need to set it to false if check succeeds,
|
||||
// because it will be redirected to the update page later
|
||||
setCustomVersionUpdateLoading(true);
|
||||
versionInfo = await checkUpdateComponents({
|
||||
components,
|
||||
}, devChannel);
|
||||
versionInfo = await checkUpdateComponents({ components }, devChannel);
|
||||
} catch (error: unknown) {
|
||||
const jsonRpcError = error as JsonRpcError;
|
||||
handleVersionUpdateError(jsonRpcError);
|
||||
|
|
@ -223,7 +227,11 @@ export default function SettingsAdvancedRoute() {
|
|||
hasUpdate = true;
|
||||
pageParams.set("custom_app_version", versionInfo.remote?.appVersion);
|
||||
}
|
||||
if (components.system && versionInfo?.remote?.systemVersion && versionInfo?.systemUpdateAvailable) {
|
||||
if (
|
||||
components.system &&
|
||||
versionInfo?.remote?.systemVersion &&
|
||||
versionInfo?.systemUpdateAvailable
|
||||
) {
|
||||
hasUpdate = true;
|
||||
pageParams.set("custom_system_version", versionInfo.remote?.systemVersion);
|
||||
}
|
||||
|
|
@ -237,17 +245,18 @@ export default function SettingsAdvancedRoute() {
|
|||
// Navigate to update page
|
||||
navigateTo(`/settings/general/update?${pageParams.toString()}`);
|
||||
}, [
|
||||
updateTarget, appVersion, systemVersion, devChannel,
|
||||
navigateTo, resetConfig, handleVersionUpdateError,
|
||||
setCustomVersionUpdateLoading
|
||||
appVersion,
|
||||
devChannel,
|
||||
handleVersionUpdateError,
|
||||
navigateTo,
|
||||
resetConfig,
|
||||
systemVersion,
|
||||
updateTarget,
|
||||
]);
|
||||
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<SettingsPageHeader
|
||||
title={m.advanced_title()}
|
||||
description={m.advanced_description()}
|
||||
/>
|
||||
<SettingsPageHeader title={m.advanced_title()} description={m.advanced_description()} />
|
||||
|
||||
<div className="space-y-4">
|
||||
<SettingsItem
|
||||
|
|
@ -319,7 +328,8 @@ export default function SettingsAdvancedRoute() {
|
|||
placeholder={m.advanced_ssh_public_key_placeholder()}
|
||||
/>
|
||||
<p className="text-xs text-slate-600 dark:text-slate-400">
|
||||
{m.advanced_ssh_default_user()}<strong>root</strong>.
|
||||
{m.advanced_ssh_default_user()}
|
||||
<strong>root</strong>.
|
||||
</p>
|
||||
<div className="flex items-center gap-x-2">
|
||||
<Button
|
||||
|
|
@ -426,8 +436,6 @@ export default function SettingsAdvancedRoute() {
|
|||
/>
|
||||
</SettingsItem>
|
||||
|
||||
|
||||
|
||||
<SettingsItem
|
||||
title={m.advanced_troubleshooting_mode_title()}
|
||||
description={m.advanced_troubleshooting_mode_description()}
|
||||
|
|
@ -450,7 +458,9 @@ export default function SettingsAdvancedRoute() {
|
|||
size="SM"
|
||||
theme="light"
|
||||
text={
|
||||
usbEmulationEnabled ? m.advanced_disable_usb_emulation() : m.advanced_enable_usb_emulation()
|
||||
usbEmulationEnabled
|
||||
? m.advanced_disable_usb_emulation()
|
||||
: m.advanced_enable_usb_emulation()
|
||||
}
|
||||
onClick={() => handleUsbEmulationToggle(!usbEmulationEnabled)}
|
||||
/>
|
||||
|
|
@ -484,12 +494,8 @@ export default function SettingsAdvancedRoute() {
|
|||
title={m.advanced_loopback_warning_title()}
|
||||
description={
|
||||
<>
|
||||
<p>
|
||||
{m.advanced_loopback_warning_description()}
|
||||
</p>
|
||||
<p>
|
||||
{m.advanced_loopback_warning_before()}
|
||||
</p>
|
||||
<p>{m.advanced_loopback_warning_description()}</p>
|
||||
<p>{m.advanced_loopback_warning_before()}</p>
|
||||
<ul className="list-disc space-y-1 pl-5 text-xs text-slate-700 dark:text-slate-300">
|
||||
<li>{m.advanced_loopback_warning_ssh()}</li>
|
||||
<li>{m.advanced_loopback_warning_cloud()}</li>
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import { SelectMenuBasic } from "@components/SelectMenuBasic";
|
|||
import { SettingsItem } from "@components/SettingsItem";
|
||||
import { SettingsPageHeader } from "@components/SettingsPageheader";
|
||||
import notifications from "@/notifications";
|
||||
import { getLocale, setLocale, locales, baseLocale } from '@localizations/runtime.js';
|
||||
import { getLocale, setLocale, locales, baseLocale } from "@localizations/runtime.js";
|
||||
import { m } from "@localizations/messages.js";
|
||||
import { deleteCookie, map_locale_code_to_name } from "@/utils";
|
||||
|
||||
|
|
@ -45,19 +45,21 @@ export default function SettingsGeneralRoute() {
|
|||
const [currentLocale, setCurrentLocale] = useState(getLocale());
|
||||
|
||||
const localeOptions = useMemo(() => {
|
||||
return ["", ...locales]
|
||||
.map((code) => {
|
||||
return ["", ...locales].map(code => {
|
||||
const [localizedName, nativeName] = map_locale_code_to_name(currentLocale, code);
|
||||
// don't repeat the name if it's the same in both locales (or blank)
|
||||
const label = nativeName && nativeName !== localizedName ? `${localizedName} - ${nativeName}` : localizedName;
|
||||
return { value: code, label: label }
|
||||
const label =
|
||||
nativeName && nativeName !== localizedName
|
||||
? `${localizedName} - ${nativeName}`
|
||||
: localizedName;
|
||||
return { value: code, label: label };
|
||||
});
|
||||
}, [currentLocale]);
|
||||
|
||||
const handleLocaleChange = (newLocale: string) => {
|
||||
if (newLocale === currentLocale) return;
|
||||
|
||||
let validLocale = newLocale as typeof locales[number];
|
||||
let validLocale = newLocale as (typeof locales)[number];
|
||||
|
||||
if (newLocale !== "") {
|
||||
if (!locales.includes(validLocale)) {
|
||||
|
|
@ -75,10 +77,7 @@ export default function SettingsGeneralRoute() {
|
|||
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<SettingsPageHeader
|
||||
title={m.general_title()}
|
||||
description={m.general_page_description()}
|
||||
/>
|
||||
<SettingsPageHeader title={m.general_title()} description={m.general_page_description()} />
|
||||
|
||||
<div className="space-y-4">
|
||||
<div className="space-y-4 pb-2">
|
||||
|
|
@ -92,7 +91,9 @@ export default function SettingsGeneralRoute() {
|
|||
label=""
|
||||
value={currentLocale}
|
||||
options={localeOptions}
|
||||
onChange={e => { handleLocaleChange(e.target.value); }}
|
||||
onChange={e => {
|
||||
handleLocaleChange(e.target.value);
|
||||
}}
|
||||
/>
|
||||
</SettingsItem>
|
||||
</div>
|
||||
|
|
@ -101,9 +102,13 @@ export default function SettingsGeneralRoute() {
|
|||
title={m.general_check_for_updates()}
|
||||
description={
|
||||
<>
|
||||
{m.general_app_version({ version: currentVersions ? currentVersions.appVersion : m.loading() })}
|
||||
{m.general_app_version({
|
||||
version: currentVersions ? currentVersions.appVersion : m.loading(),
|
||||
})}
|
||||
<br />
|
||||
{m.general_system_version({ version: currentVersions ? currentVersions.systemVersion : m.loading() })}
|
||||
{m.general_system_version({
|
||||
version: currentVersions ? currentVersions.systemVersion : m.loading(),
|
||||
})}
|
||||
</>
|
||||
}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -1,15 +1,14 @@
|
|||
import { useCallback, useState } from "react";
|
||||
import { useNavigate } from "react-router";
|
||||
|
||||
import { useDeviceUiNavigation } from "@hooks/useAppNavigation";
|
||||
import { useFailsafeModeStore } from "@hooks/stores";
|
||||
import { useJsonRpc } from "@hooks/useJsonRpc";
|
||||
import { Button } from "@components/Button";
|
||||
import { useFailsafeModeStore } from "@/hooks/stores";
|
||||
import LoadingSpinner from "@components/LoadingSpinner";
|
||||
import { sleep } from "@/utils";
|
||||
import { m } from "@localizations/messages.js";
|
||||
|
||||
import LoadingSpinner from "../components/LoadingSpinner";
|
||||
import { useDeviceUiNavigation } from "../hooks/useAppNavigation";
|
||||
|
||||
// Time to wait after initiating reboot before redirecting to home
|
||||
const REBOOT_REDIRECT_DELAY_MS = 7000;
|
||||
|
||||
|
|
@ -27,7 +26,6 @@ export default function SettingsGeneralRebootRoute() {
|
|||
window.location.reload(); // force a full reload to ensure the current device/cloud UI version is loaded
|
||||
}, [navigate]);
|
||||
|
||||
|
||||
const onConfirmUpdate = useCallback(async () => {
|
||||
setIsRebooting(true);
|
||||
send("reboot", { force: true });
|
||||
|
|
@ -49,15 +47,10 @@ export function Dialog({
|
|||
onClose: () => void;
|
||||
onConfirmUpdate: () => void;
|
||||
}>) {
|
||||
|
||||
return (
|
||||
<div className="pointer-events-auto relative mx-auto text-left">
|
||||
<div>
|
||||
<ConfirmationBox
|
||||
isRebooting={isRebooting}
|
||||
onYes={onConfirmUpdate}
|
||||
onNo={onClose}
|
||||
/>
|
||||
<ConfirmationBox isRebooting={isRebooting} onYes={onConfirmUpdate} onNo={onClose} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue