mirror of https://github.com/jetkvm/kvm.git
chore: run eslint --fix on all code with new pretter rules to enforce prettier
This commit is contained in:
parent
cf71508bcd
commit
6d74e51086
|
@ -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: {
|
||||
|
@ -241,7 +241,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,14 @@ 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",
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -84,8 +84,8 @@ export function ConfirmDialog({
|
|||
>
|
||||
<Icon aria-hidden="true" className={cx("size-6", iconClass)} />
|
||||
</div>
|
||||
<div className="mt-3 text-center sm:ml-4 sm:mt-0 sm:text-left">
|
||||
<h2 className="text-lg font-bold leading-tight text-black dark:text-white">
|
||||
<div className="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left">
|
||||
<h2 className="text-lg leading-tight font-bold text-black dark:text-white">
|
||||
{title}
|
||||
</h2>
|
||||
<div className="mt-2 text-sm leading-snug text-slate-600 dark:text-slate-400">
|
||||
|
|
|
@ -4,7 +4,7 @@ import React, { ReactNode } from "react";
|
|||
import { cx } from "@/cva.config";
|
||||
|
||||
function Container({ children, className }: { children: ReactNode; className?: string }) {
|
||||
return <div className={cx("mx-auto h-full w-full px-8 ", className)}>{children}</div>;
|
||||
return <div className={cx("mx-auto h-full w-full px-8", className)}>{children}</div>;
|
||||
}
|
||||
|
||||
function Article({ children }: { children: React.ReactNode }) {
|
||||
|
|
|
@ -18,7 +18,7 @@ export default function CustomTooltip({ payload }: CustomTooltipProps) {
|
|||
<div className="space-y-1">
|
||||
<div className="flex items-center gap-x-1">
|
||||
<div className="h-[2px] w-2 bg-blue-700" />
|
||||
<span >
|
||||
<span>
|
||||
{stat} {toolTipData?.unit}
|
||||
</span>
|
||||
</div>
|
||||
|
|
|
@ -14,7 +14,7 @@ export default function DhcpLeaseCard({
|
|||
}) {
|
||||
return (
|
||||
<GridCard>
|
||||
<div className="animate-fadeIn p-4 opacity-0 animation-duration-500 text-black dark:text-white">
|
||||
<div className="animate-fadeIn p-4 text-black opacity-0 animation-duration-500 dark:text-white">
|
||||
<div className="space-y-3">
|
||||
<h3 className="text-base font-bold text-slate-900 dark:text-white">
|
||||
DHCP Lease Information
|
||||
|
|
|
@ -32,7 +32,7 @@ export default function EmptyCard({
|
|||
{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">
|
||||
<h4 className="text-base leading-none font-bold text-black dark:text-white">
|
||||
{headline}
|
||||
</h4>
|
||||
</div>
|
||||
|
|
|
@ -21,7 +21,7 @@ export default function FieldLabel({
|
|||
<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 +35,8 @@ export default function FieldLabel({
|
|||
);
|
||||
} else if (as === "span") {
|
||||
return (
|
||||
<div className="flex select-none flex-col">
|
||||
<span className="font-display text-[13px] font-medium leading-snug text-black dark:text-white">
|
||||
<div className="flex flex-col select-none">
|
||||
<span className="font-display text-[13px] leading-snug font-medium text-black dark:text-white">
|
||||
{label}
|
||||
</span>
|
||||
{description && (
|
||||
|
@ -49,4 +49,4 @@ export default function FieldLabel({
|
|||
} else {
|
||||
return <></>;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -112,7 +112,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}
|
||||
|
@ -133,7 +133,7 @@ export default function DashboardNavbar({
|
|||
<div className="font-display text-xs">
|
||||
Logged in as
|
||||
</div>
|
||||
<div className="font-display max-w-[200px] truncate text-sm font-semibold">
|
||||
<div className="max-w-[200px] truncate font-display text-sm font-semibold">
|
||||
{userEmail}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -46,10 +46,10 @@ export default function InfoBar() {
|
|||
const hdmiState = useVideoStore(state => state.hdmiState);
|
||||
|
||||
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">
|
||||
{settings.debugMode ? (
|
||||
<div className="flex">
|
||||
<span className="text-xs font-semibold">Resolution:</span>{" "}
|
||||
|
@ -64,7 +64,7 @@ export default function InfoBar() {
|
|||
</div>
|
||||
) : null}
|
||||
|
||||
{(settings.debugMode && settings.mouseMode == "absolute") ? (
|
||||
{settings.debugMode && settings.mouseMode == "absolute" ? (
|
||||
<div className="flex w-[118px] items-center gap-x-1">
|
||||
<span className="text-xs font-semibold">Pointer:</span>
|
||||
<span className="text-xs">
|
||||
|
@ -73,13 +73,13 @@ export default function InfoBar() {
|
|||
</div>
|
||||
) : null}
|
||||
|
||||
{(settings.debugMode && settings.mouseMode == "relative") ? (
|
||||
{settings.debugMode && settings.mouseMode == "relative" ? (
|
||||
<div className="flex w-[118px] items-center gap-x-1">
|
||||
<span className="text-xs font-semibold">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}
|
||||
|
@ -112,7 +112,7 @@ export default function InfoBar() {
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center divide-x first:divide-l divide-slate-800/20 dark:divide-slate-300/20">
|
||||
<div className="first:divide-l flex items-center divide-x divide-slate-800/20 dark:divide-slate-300/20">
|
||||
{isTurnServerInUse && (
|
||||
<div className="shrink-0 p-1 px-1.5 text-xs text-black dark:text-white">
|
||||
Relayed by Cloudflare
|
||||
|
|
|
@ -44,14 +44,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>
|
||||
)}
|
||||
|
@ -61,12 +65,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} />}
|
||||
|
|
|
@ -33,56 +33,54 @@ export default function Ipv6NetworkCard({
|
|||
{networkState?.ipv6_addresses && networkState?.ipv6_addresses.length > 0 && (
|
||||
<div className="space-y-3">
|
||||
<h4 className="text-sm font-semibold">IPv6 Addresses</h4>
|
||||
{networkState.ipv6_addresses.map(
|
||||
addr => (
|
||||
<div
|
||||
key={addr.address}
|
||||
className="rounded-md rounded-l-none border border-slate-500/10 border-l-blue-700/50 bg-white p-4 pl-4 backdrop-blur-sm dark:bg-transparent"
|
||||
>
|
||||
<div className="grid grid-cols-2 gap-x-8 gap-y-4">
|
||||
<div className="col-span-2 flex flex-col justify-between">
|
||||
<span className="text-sm text-slate-600 dark:text-slate-400">
|
||||
Address
|
||||
</span>
|
||||
<span className="text-sm font-medium">{addr.address}</span>
|
||||
</div>
|
||||
|
||||
{addr.valid_lifetime && (
|
||||
<div className="flex flex-col justify-between">
|
||||
<span className="text-sm text-slate-600 dark:text-slate-400">
|
||||
Valid Lifetime
|
||||
</span>
|
||||
<span className="text-sm font-medium">
|
||||
{addr.valid_lifetime === "" ? (
|
||||
<span className="text-slate-400 dark:text-slate-600">
|
||||
N/A
|
||||
</span>
|
||||
) : (
|
||||
<LifeTimeLabel lifetime={`${addr.valid_lifetime}`} />
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
{addr.preferred_lifetime && (
|
||||
<div className="flex flex-col justify-between">
|
||||
<span className="text-sm text-slate-600 dark:text-slate-400">
|
||||
Preferred Lifetime
|
||||
</span>
|
||||
<span className="text-sm font-medium">
|
||||
{addr.preferred_lifetime === "" ? (
|
||||
<span className="text-slate-400 dark:text-slate-600">
|
||||
N/A
|
||||
</span>
|
||||
) : (
|
||||
<LifeTimeLabel lifetime={`${addr.preferred_lifetime}`} />
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
{networkState.ipv6_addresses.map(addr => (
|
||||
<div
|
||||
key={addr.address}
|
||||
className="rounded-md rounded-l-none border border-slate-500/10 border-l-blue-700/50 bg-white p-4 pl-4 backdrop-blur-sm dark:bg-transparent"
|
||||
>
|
||||
<div className="grid grid-cols-2 gap-x-8 gap-y-4">
|
||||
<div className="col-span-2 flex flex-col justify-between">
|
||||
<span className="text-sm text-slate-600 dark:text-slate-400">
|
||||
Address
|
||||
</span>
|
||||
<span className="text-sm font-medium">{addr.address}</span>
|
||||
</div>
|
||||
|
||||
{addr.valid_lifetime && (
|
||||
<div className="flex flex-col justify-between">
|
||||
<span className="text-sm text-slate-600 dark:text-slate-400">
|
||||
Valid Lifetime
|
||||
</span>
|
||||
<span className="text-sm font-medium">
|
||||
{addr.valid_lifetime === "" ? (
|
||||
<span className="text-slate-400 dark:text-slate-600">
|
||||
N/A
|
||||
</span>
|
||||
) : (
|
||||
<LifeTimeLabel lifetime={`${addr.valid_lifetime}`} />
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
{addr.preferred_lifetime && (
|
||||
<div className="flex flex-col justify-between">
|
||||
<span className="text-sm text-slate-600 dark:text-slate-400">
|
||||
Preferred Lifetime
|
||||
</span>
|
||||
<span className="text-sm font-medium">
|
||||
{addr.preferred_lifetime === "" ? (
|
||||
<span className="text-slate-400 dark:text-slate-600">
|
||||
N/A
|
||||
</span>
|
||||
) : (
|
||||
<LifeTimeLabel lifetime={`${addr.preferred_lifetime}`} />
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
),
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
|
|
@ -52,10 +52,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">
|
||||
<div className="text-lg leading-none font-bold text-black dark:text-white">
|
||||
{title}
|
||||
</div>
|
||||
|
||||
|
@ -66,7 +66,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 ? (
|
||||
<>Last online {getRelativeTimeString(lastSeen)}</>
|
||||
|
@ -111,14 +111,14 @@ export default function KvmCard({
|
|||
|
||||
<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`}
|
||||
|
@ -132,7 +132,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,10 +1,6 @@
|
|||
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]")}
|
||||
|
|
|
@ -14,7 +14,7 @@ export default function MacroBar() {
|
|||
|
||||
useEffect(() => {
|
||||
setSendFn(send);
|
||||
|
||||
|
||||
if (!initialized) {
|
||||
loadMacros();
|
||||
}
|
||||
|
@ -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" />
|
||||
|
@ -45,4 +45,4 @@ export default function MacroBar() {
|
|||
</div>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ import { MAX_KEYS_PER_STEP, DEFAULT_DELAY } from "@/constants/macros";
|
|||
import FieldLabel from "@/components/FieldLabel";
|
||||
|
||||
// Filter out modifier keys since they're handled in the modifiers section
|
||||
const modifierKeyPrefixes = ['Alt', 'Control', 'Shift', 'Meta'];
|
||||
const modifierKeyPrefixes = ["Alt", "Control", "Shift", "Meta"];
|
||||
|
||||
const keyOptions = Object.keys(keys)
|
||||
.filter(key => !modifierKeyPrefixes.some(prefix => key.startsWith(prefix)))
|
||||
|
@ -24,10 +24,10 @@ const modifierOptions = Object.keys(modifiers).map(modifier => ({
|
|||
}));
|
||||
|
||||
const groupedModifiers: Record<string, typeof modifierOptions> = {
|
||||
Control: modifierOptions.filter(mod => mod.value.startsWith('Control')),
|
||||
Shift: modifierOptions.filter(mod => mod.value.startsWith('Shift')),
|
||||
Alt: modifierOptions.filter(mod => mod.value.startsWith('Alt')),
|
||||
Meta: modifierOptions.filter(mod => mod.value.startsWith('Meta')),
|
||||
Control: modifierOptions.filter(mod => mod.value.startsWith("Control")),
|
||||
Shift: modifierOptions.filter(mod => mod.value.startsWith("Shift")),
|
||||
Alt: modifierOptions.filter(mod => mod.value.startsWith("Alt")),
|
||||
Meta: modifierOptions.filter(mod => mod.value.startsWith("Meta")),
|
||||
};
|
||||
|
||||
const basePresetDelays = [
|
||||
|
@ -84,16 +84,20 @@ export function MacroStepCard({
|
|||
keyQuery,
|
||||
onModifierChange,
|
||||
onDelayChange,
|
||||
isLastStep
|
||||
isLastStep,
|
||||
}: MacroStepCardProps) {
|
||||
const getFilteredKeys = () => {
|
||||
const selectedKeys = ensureArray(step.keys);
|
||||
const availableKeys = keyOptions.filter(option => !selectedKeys.includes(option.value));
|
||||
|
||||
if (keyQuery === '') {
|
||||
const availableKeys = keyOptions.filter(
|
||||
option => !selectedKeys.includes(option.value),
|
||||
);
|
||||
|
||||
if (keyQuery === "") {
|
||||
return availableKeys;
|
||||
} else {
|
||||
return availableKeys.filter(option => option.label.toLowerCase().includes(keyQuery.toLowerCase()));
|
||||
return availableKeys.filter(option =>
|
||||
option.label.toLowerCase().includes(keyQuery.toLowerCase()),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -136,13 +140,16 @@ export function MacroStepCard({
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-4 mt-2">
|
||||
<div className="w-full flex flex-col gap-2">
|
||||
<div className="mt-2 space-y-4">
|
||||
<div className="flex w-full flex-col gap-2">
|
||||
<FieldLabel label="Modifiers" />
|
||||
<div className="inline-flex flex-wrap gap-3">
|
||||
{Object.entries(groupedModifiers).map(([group, mods]) => (
|
||||
<div key={group} className="relative min-w-[120px] rounded-md border border-slate-200 dark:border-slate-700 p-2">
|
||||
<span className="absolute -top-2.5 left-2 px-1 text-xs font-medium bg-white dark:bg-slate-800 text-slate-500 dark:text-slate-400">
|
||||
<div
|
||||
key={group}
|
||||
className="relative min-w-[120px] rounded-md border border-slate-200 p-2 dark:border-slate-700"
|
||||
>
|
||||
<span className="absolute -top-2.5 left-2 bg-white px-1 text-xs font-medium text-slate-500 dark:bg-slate-800 dark:text-slate-400">
|
||||
{group}
|
||||
</span>
|
||||
<div className="flex flex-wrap gap-4 pt-1">
|
||||
|
@ -150,8 +157,12 @@ export function MacroStepCard({
|
|||
<Button
|
||||
key={option.value}
|
||||
size="XS"
|
||||
theme={ensureArray(step.modifiers).includes(option.value) ? "primary" : "light"}
|
||||
text={option.label.split(' ')[1] || option.label}
|
||||
theme={
|
||||
ensureArray(step.modifiers).includes(option.value)
|
||||
? "primary"
|
||||
: "light"
|
||||
}
|
||||
text={option.label.split(" ")[1] || option.label}
|
||||
onClick={() => {
|
||||
const modifiersArray = ensureArray(step.modifiers);
|
||||
const isSelected = modifiersArray.includes(option.value);
|
||||
|
@ -167,27 +178,30 @@ export function MacroStepCard({
|
|||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="w-full flex flex-col gap-1">
|
||||
|
||||
<div className="flex w-full flex-col gap-1">
|
||||
<div className="flex items-center gap-1">
|
||||
<FieldLabel label="Keys" description={`Maximum ${MAX_KEYS_PER_STEP} keys per step.`} />
|
||||
<FieldLabel
|
||||
label="Keys"
|
||||
description={`Maximum ${MAX_KEYS_PER_STEP} keys per step.`}
|
||||
/>
|
||||
</div>
|
||||
{ensureArray(step.keys) && step.keys.length > 0 && (
|
||||
<div className="flex flex-wrap gap-1 pb-2">
|
||||
{step.keys.map((key, keyIndex) => (
|
||||
<span
|
||||
key={keyIndex}
|
||||
className="inline-flex items-center py-0.5 rounded-md bg-blue-100 px-1 text-xs font-medium text-blue-800 dark:bg-blue-900/40 dark:text-blue-200"
|
||||
className="inline-flex items-center rounded-md bg-blue-100 px-1 py-0.5 text-xs font-medium text-blue-800 dark:bg-blue-900/40 dark:text-blue-200"
|
||||
>
|
||||
<span className="px-1">
|
||||
{keyDisplayMap[key] || key}
|
||||
</span>
|
||||
<span className="px-1">{keyDisplayMap[key] || key}</span>
|
||||
<Button
|
||||
size="XS"
|
||||
className=""
|
||||
theme="blank"
|
||||
onClick={() => {
|
||||
const newKeys = ensureArray(step.keys).filter((_, i) => i !== keyIndex);
|
||||
const newKeys = ensureArray(step.keys).filter(
|
||||
(_, i) => i !== keyIndex,
|
||||
);
|
||||
onKeySelect({ value: null, keys: newKeys });
|
||||
}}
|
||||
LeadingIcon={LuX}
|
||||
|
@ -200,7 +214,7 @@ export function MacroStepCard({
|
|||
<Combobox
|
||||
onChange={(value: { value: string; label: string }) => {
|
||||
onKeySelect(value);
|
||||
onKeyQueryChange('');
|
||||
onKeyQueryChange("");
|
||||
}}
|
||||
displayValue={() => keyQuery}
|
||||
onInputChange={onKeyQueryChange}
|
||||
|
@ -209,22 +223,29 @@ export function MacroStepCard({
|
|||
size="SM"
|
||||
immediate
|
||||
disabled={ensureArray(step.keys).length >= MAX_KEYS_PER_STEP}
|
||||
placeholder={ensureArray(step.keys).length >= MAX_KEYS_PER_STEP ? "Max keys reached" : "Search for key..."}
|
||||
placeholder={
|
||||
ensureArray(step.keys).length >= MAX_KEYS_PER_STEP
|
||||
? "Max keys reached"
|
||||
: "Search for key..."
|
||||
}
|
||||
emptyMessage="No matching keys found"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="w-full flex flex-col gap-1">
|
||||
|
||||
<div className="flex w-full flex-col gap-1">
|
||||
<div className="flex items-center gap-1">
|
||||
<FieldLabel label="Step Duration" description="Time to wait before executing the next step." />
|
||||
<FieldLabel
|
||||
label="Step Duration"
|
||||
description="Time to wait before executing the next step."
|
||||
/>
|
||||
</div>
|
||||
<div className="flex items-center gap-3">
|
||||
<SelectMenuBasic
|
||||
size="SM"
|
||||
fullWidth
|
||||
value={step.delay.toString()}
|
||||
onChange={(e) => onDelayChange(parseInt(e.target.value, 10))}
|
||||
onChange={e => onDelayChange(parseInt(e.target.value, 10))}
|
||||
options={PRESET_DELAYS}
|
||||
/>
|
||||
</div>
|
||||
|
@ -232,4 +253,4 @@ export function MacroStepCard({
|
|||
</div>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,18 +18,21 @@ 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,
|
||||
)}
|
||||
>
|
||||
|
|
|
@ -6,7 +6,6 @@ import { cva } from "@/cva.config";
|
|||
|
||||
import Card from "./Card";
|
||||
|
||||
|
||||
type SelectMenuProps = Pick<
|
||||
JSX.IntrinsicElements["select"],
|
||||
"disabled" | "onChange" | "name" | "value"
|
||||
|
@ -84,10 +83,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}
|
||||
|
|
|
@ -9,7 +9,7 @@ export function SettingsPageHeader({
|
|||
}) {
|
||||
return (
|
||||
<div className="select-none">
|
||||
<h2 className=" text-xl font-extrabold text-black dark:text-white">{title}</h2>
|
||||
<h2 className="text-xl font-extrabold text-black dark:text-white">{title}</h2>
|
||||
<div className="text-sm text-black dark:text-slate-300">{description}</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -10,9 +10,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 Container from "@/components/Container";
|
|||
import LogoBlueIcon from "@/assets/logo-blue.png";
|
||||
import LogoWhiteIcon from "@/assets/logo-white.svg";
|
||||
|
||||
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>
|
||||
|
|
|
@ -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,7 +26,7 @@ export default function StatusCard({
|
|||
) : null}
|
||||
|
||||
<div className="space-y-1">
|
||||
<div className="text-xs font-semibold leading-none transition text-ellipsis">
|
||||
<div className="text-xs leading-none font-semibold text-ellipsis transition">
|
||||
{title}
|
||||
</div>
|
||||
<div className="text-xs leading-none">
|
||||
|
|
|
@ -23,7 +23,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 (
|
||||
|
|
|
@ -161,10 +161,7 @@ function Terminal({
|
|||
}, [ref, instance]);
|
||||
|
||||
return (
|
||||
<div
|
||||
onKeyDown={e => e.stopPropagation()}
|
||||
onKeyUp={e => e.stopPropagation()}
|
||||
>
|
||||
<div onKeyDown={e => e.stopPropagation()} onKeyUp={e => e.stopPropagation()}>
|
||||
<div>
|
||||
<div
|
||||
className={cx(
|
||||
|
@ -183,7 +180,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,
|
||||
)}
|
||||
/>
|
||||
|
|
|
@ -10,13 +10,13 @@ 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">
|
||||
Update in Progress
|
||||
</div>
|
||||
<div className="text-sm leading-none">
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { useCallback , useEffect, useState } from "react";
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
|
||||
import { useJsonRpc } from "../hooks/useJsonRpc";
|
||||
import notifications from "../notifications";
|
||||
|
@ -174,7 +174,7 @@ export function UsbDeviceSetting() {
|
|||
</SettingsItem>
|
||||
|
||||
{selectedPreset === "custom" && (
|
||||
<div className="ml-2 border-l border-slate-800/10 pl-4 dark:border-slate-300/20 ">
|
||||
<div className="ml-2 border-l border-slate-800/10 pl-4 dark:border-slate-300/20">
|
||||
<div className="space-y-4">
|
||||
<div className="space-y-4">
|
||||
<SettingsItem title="Enable Keyboard" description="Enable Keyboard">
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
import { useMemo , useCallback , useEffect, useState } from "react";
|
||||
import { useMemo, useCallback, useEffect, useState } from "react";
|
||||
|
||||
import { Button } from "@components/Button";
|
||||
|
||||
|
||||
import { UsbConfigState } from "../hooks/stores";
|
||||
import { useJsonRpc } from "../hooks/useJsonRpc";
|
||||
import notifications from "../notifications";
|
||||
|
@ -174,7 +173,7 @@ export function UsbInfoSetting() {
|
|||
/>
|
||||
</SettingsItem>
|
||||
{usbConfigProduct === "custom" && (
|
||||
<div className="ml-2 space-y-4 border-l border-slate-800/10 pl-4 dark:border-slate-300/20 ">
|
||||
<div className="ml-2 space-y-4 border-l border-slate-800/10 pl-4 dark:border-slate-300/20">
|
||||
<USBConfigDialog
|
||||
loading={loading}
|
||||
onSetUsbConfig={usbConfig => handleUsbConfigChange(usbConfig)}
|
||||
|
|
|
@ -369,7 +369,7 @@ export function PointerLockBar({ show }: PointerLockBarProps) {
|
|||
<AnimatePresence mode="wait">
|
||||
{show ? (
|
||||
<motion.div
|
||||
className="absolute -top-[36px] left-0 right-0 z-20 bg-white"
|
||||
className="absolute -top-[36px] right-0 left-0 z-20 bg-white"
|
||||
initial={{ y: 20, opacity: 0, zIndex: 0 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
exit={{ y: 43, zIndex: 0 }}
|
||||
|
|
|
@ -210,7 +210,7 @@ function KeyboardWrapper() {
|
|||
<div
|
||||
className={cx(
|
||||
!showAttachedVirtualKeyboard
|
||||
? "fixed left-0 top-0 z-50 select-none"
|
||||
? "fixed top-0 left-0 z-50 select-none"
|
||||
: "relative",
|
||||
)}
|
||||
ref={keyboardRef}
|
||||
|
@ -244,7 +244,7 @@ function KeyboardWrapper() {
|
|||
/>
|
||||
)}
|
||||
</div>
|
||||
<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">
|
||||
Virtual Keyboard
|
||||
</h2>
|
||||
<div className="absolute right-2">
|
||||
|
@ -304,8 +304,16 @@ function KeyboardWrapper() {
|
|||
onKeyPress={onKeyDown}
|
||||
display={keyDisplayMap}
|
||||
layout={{
|
||||
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",
|
||||
],
|
||||
}}
|
||||
syncInstanceInputs={true}
|
||||
debug={false}
|
||||
|
|
|
@ -704,7 +704,7 @@ export default function WebRTCVideo() {
|
|||
{peerConnection?.connectionState == "connected" && (
|
||||
<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} />
|
||||
|
|
|
@ -92,7 +92,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",
|
||||
|
@ -113,7 +113,7 @@ export default function ExtensionPopover() {
|
|||
title="Extensions"
|
||||
description="Load and manage your extensions"
|
||||
/>
|
||||
<Card className="animate-fadeIn opacity-0" >
|
||||
<Card className="animate-fadeIn opacity-0">
|
||||
<div className="w-full divide-y divide-slate-700/30 dark:divide-slate-600/30">
|
||||
{AVAILABLE_EXTENSIONS.map(extension => (
|
||||
<div
|
||||
|
@ -121,7 +121,7 @@ export default function ExtensionPopover() {
|
|||
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">
|
||||
|
|
|
@ -80,7 +80,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">
|
||||
No mounted media
|
||||
</h3>
|
||||
<p className="text-xs leading-none text-slate-700 dark:text-slate-300">
|
||||
|
@ -214,7 +214,7 @@ const MountPopopover = forwardRef<HTMLDivElement, object>((_props, ref) => {
|
|||
) : null}
|
||||
|
||||
<div
|
||||
className="animate-fadeIn opacity-0 space-y-2"
|
||||
className="animate-fadeIn space-y-2 opacity-0"
|
||||
style={{
|
||||
animationDuration: "0.7s",
|
||||
animationDelay: "0.1s",
|
||||
|
@ -232,8 +232,8 @@ 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>Mounted as</span>{" "}
|
||||
<span className="font-semibold">
|
||||
{remoteVirtualMediaState.mode === "Disk" ? "Disk" : "CD-ROM"}
|
||||
|
@ -289,7 +289,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",
|
||||
|
|
|
@ -83,7 +83,7 @@ export default function PasteModal() {
|
|||
/>
|
||||
|
||||
<div
|
||||
className="animate-fadeIn opacity-0 space-y-2"
|
||||
className="animate-fadeIn space-y-2 opacity-0"
|
||||
style={{
|
||||
animationDuration: "0.7s",
|
||||
animationDelay: "0.1s",
|
||||
|
@ -137,7 +137,7 @@ export default function PasteModal() {
|
|||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="flex animate-fadeIn opacity-0 items-center justify-end gap-x-2"
|
||||
className="flex animate-fadeIn items-center justify-end gap-x-2 opacity-0"
|
||||
style={{
|
||||
animationDuration: "0.7s",
|
||||
animationDelay: "0.2s",
|
||||
|
|
|
@ -26,7 +26,7 @@ export default function AddDeviceForm({
|
|||
return (
|
||||
<div className="space-y-4">
|
||||
<div
|
||||
className="animate-fadeIn opacity-0 space-y-4"
|
||||
className="animate-fadeIn space-y-4 opacity-0"
|
||||
style={{
|
||||
animationDuration: "0.5s",
|
||||
animationFillMode: "forwards",
|
||||
|
@ -73,7 +73,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",
|
||||
|
|
|
@ -33,7 +33,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">
|
||||
|
@ -63,7 +63,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",
|
||||
|
|
|
@ -12,7 +12,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">
|
||||
|
@ -24,7 +24,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">
|
||||
No devices added
|
||||
</h3>
|
||||
<p className="text-xs leading-none text-slate-700 dark:text-slate-300">
|
||||
|
@ -35,7 +35,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",
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
export const DEFAULT_DELAY = 50;
|
||||
export const MAX_STEPS_PER_MACRO = 10;
|
||||
export const MAX_KEYS_PER_STEP = 10;
|
||||
export const MAX_TOTAL_MACROS = 25;
|
||||
export const COPY_SUFFIX = "(copy)";
|
||||
export const MAX_TOTAL_MACROS = 25;
|
||||
export const COPY_SUFFIX = "(copy)";
|
||||
|
|
|
@ -29,10 +29,13 @@ export default function useKeyboard() {
|
|||
sendKeyboardEvent([], []);
|
||||
}, [sendKeyboardEvent]);
|
||||
|
||||
const executeMacro = async (steps: { keys: string[] | null; modifiers: string[] | null; delay: number }[]) => {
|
||||
const executeMacro = async (
|
||||
steps: { keys: string[] | null; modifiers: string[] | null; delay: number }[],
|
||||
) => {
|
||||
for (const [index, step] of steps.entries()) {
|
||||
const keyValues = step.keys?.map(key => keys[key]).filter(Boolean) || [];
|
||||
const modifierValues = step.modifiers?.map(mod => modifiers[mod]).filter(Boolean) || [];
|
||||
const modifierValues =
|
||||
step.modifiers?.map(mod => modifiers[mod]).filter(Boolean) || [];
|
||||
|
||||
// If the step has keys and/or modifiers, press them and hold for the delay
|
||||
if (keyValues.length > 0 || modifierValues.length > 0) {
|
||||
|
|
|
@ -207,7 +207,7 @@ export const chars = {
|
|||
Tab: { key: "Tab", shift: false },
|
||||
PrintScreen: { key: "Prt Sc", shift: false },
|
||||
SystemRequest: { key: "Prt Sc", shift: true },
|
||||
ScrollLock: { key: "ScrollLock", shift: false},
|
||||
ScrollLock: { key: "ScrollLock", shift: false },
|
||||
Pause: { key: "Pause", shift: false },
|
||||
Break: { key: "Pause", shift: true },
|
||||
Insert: { key: "Insert", shift: false },
|
||||
|
@ -264,30 +264,86 @@ export const keyDisplayMap: Record<string, string> = {
|
|||
ArrowRight: "→",
|
||||
ArrowUp: "↑",
|
||||
ArrowDown: "↓",
|
||||
|
||||
|
||||
// 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: "-",
|
||||
|
@ -295,7 +351,7 @@ export const keyDisplayMap: Record<string, string> = {
|
|||
Equal: "=",
|
||||
"(Equal)": "+",
|
||||
BracketLeft: "[",
|
||||
"(BracketLeft)": "{",
|
||||
"(BracketLeft)": "{",
|
||||
BracketRight: "]",
|
||||
"(BracketRight)": "}",
|
||||
Backslash: "\\",
|
||||
|
@ -303,7 +359,7 @@ export const keyDisplayMap: Record<string, string> = {
|
|||
Semicolon: ";",
|
||||
"(Semicolon)": ":",
|
||||
Quote: "'",
|
||||
"(Quote)": "\"",
|
||||
"(Quote)": '"',
|
||||
Comma: ",",
|
||||
"(Comma)": "<",
|
||||
Period: ".",
|
||||
|
@ -315,21 +371,45 @@ 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",
|
||||
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",
|
||||
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",
|
||||
};
|
||||
|
|
|
@ -341,10 +341,11 @@ if (isOnDevice) {
|
|||
loader: DeviceIdRename.loader,
|
||||
action: DeviceIdRename.action,
|
||||
},
|
||||
{
|
||||
path: "devices",
|
||||
{
|
||||
path: "devices",
|
||||
element: <DevicesRoute />,
|
||||
loader: DevicesRoute.loader },
|
||||
loader: DevicesRoute.loader,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
|
|
|
@ -4,7 +4,6 @@ import { CheckCircleIcon, XCircleIcon } from "@heroicons/react/20/solid";
|
|||
|
||||
import Card from "@/components/Card";
|
||||
|
||||
|
||||
interface NotificationOptions {
|
||||
duration?: number;
|
||||
// Add other options as needed
|
||||
|
@ -26,7 +25,9 @@ const ToastContent = ({
|
|||
>
|
||||
<div className="flex items-center gap-x-2 p-2.5 px-2">
|
||||
{icon}
|
||||
<p className="text-[14px] font-medium text-gray-900 dark:text-gray-100">{message}</p>
|
||||
<p className="text-[14px] font-medium text-gray-900 dark:text-gray-100">
|
||||
{message}
|
||||
</p>
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
|
@ -36,7 +37,9 @@ const notifications = {
|
|||
return toast.custom(
|
||||
t => (
|
||||
<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}
|
||||
/>
|
||||
|
@ -49,7 +52,7 @@ const notifications = {
|
|||
return toast.custom(
|
||||
t => (
|
||||
<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}
|
||||
/>
|
||||
|
|
|
@ -80,9 +80,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"
|
||||
|
|
|
@ -1364,7 +1364,7 @@ function UploadFileView({
|
|||
{/* Display upload error if present */}
|
||||
{uploadError && (
|
||||
<div
|
||||
className="mt-2 animate-fadeIn truncate text-sm text-red-600 dark:text-red-400 opacity-0"
|
||||
className="mt-2 animate-fadeIn truncate text-sm text-red-600 opacity-0 dark:text-red-400"
|
||||
style={{ animationDuration: "0.7s" }}
|
||||
>
|
||||
Error: {uploadError}
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
import { useCallback, useState, useEffect } from "react";
|
||||
|
||||
import { GridCard } from "@components/Card";
|
||||
|
@ -12,7 +11,6 @@ import { isOnDevice } from "../main";
|
|||
import { Button } from "../components/Button";
|
||||
import { useSettingsStore } from "../hooks/stores";
|
||||
|
||||
|
||||
import { SettingsItem } from "./devices.$id.settings";
|
||||
|
||||
export default function SettingsAdvancedRoute() {
|
||||
|
@ -153,7 +151,7 @@ export default function SettingsAdvancedRoute() {
|
|||
|
||||
{settings.developerMode && (
|
||||
<GridCard>
|
||||
<div className="flex select-none items-start gap-x-4 p-4">
|
||||
<div className="flex items-start gap-x-4 p-4 select-none">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
|
||||
import { useState , useEffect } from "react";
|
||||
import { useState, useEffect } from "react";
|
||||
|
||||
import { useJsonRpc } from "@/hooks/useJsonRpc";
|
||||
|
||||
|
|
|
@ -242,9 +242,9 @@ function UpdatingDeviceState({
|
|||
|
||||
console.log(
|
||||
`For ${type}:\n` +
|
||||
` Download Progress: ${downloadProgress}% (${otaState[`${type}DownloadProgress`]})\n` +
|
||||
` Update Progress: ${updateProgress}% (${otaState[`${type}UpdateProgress`]})\n` +
|
||||
` Verification Progress: ${verificationProgress}% (${otaState[`${type}VerificationProgress`]})`,
|
||||
` Download Progress: ${downloadProgress}% (${otaState[`${type}DownloadProgress`]})\n` +
|
||||
` Update Progress: ${updateProgress}% (${otaState[`${type}UpdateProgress`]})\n` +
|
||||
` Verification Progress: ${verificationProgress}% (${otaState[`${type}VerificationProgress`]})`,
|
||||
);
|
||||
|
||||
if (type === "app") {
|
||||
|
|
|
@ -23,15 +23,19 @@ export default function SettingsHardwareRoute() {
|
|||
};
|
||||
|
||||
const handleDisplayRotationSave = () => {
|
||||
send("setDisplayRotation", { params: { rotation: settings.displayRotation } }, resp => {
|
||||
if ("error" in resp) {
|
||||
notifications.error(
|
||||
`Failed to set display orientation: ${resp.error.data || "Unknown error"}`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
notifications.success("Display orientation updated successfully");
|
||||
});
|
||||
send(
|
||||
"setDisplayRotation",
|
||||
{ params: { rotation: settings.displayRotation } },
|
||||
resp => {
|
||||
if ("error" in resp) {
|
||||
notifications.error(
|
||||
`Failed to set display orientation: ${resp.error.data || "Unknown error"}`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
notifications.success("Display orientation updated successfully");
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
const setBacklightSettings = useSettingsStore(state => state.setBacklightSettings);
|
||||
|
|
|
@ -60,4 +60,4 @@ export default function SettingsMacrosAddRoute() {
|
|||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,8 +34,8 @@ export default function SettingsMacrosEditRoute() {
|
|||
...step,
|
||||
keys: Array.isArray(step.keys) ? step.keys : [],
|
||||
modifiers: Array.isArray(step.modifiers) ? step.modifiers : [],
|
||||
delay: typeof step.delay === 'number' ? step.delay : 0
|
||||
}))
|
||||
delay: typeof step.delay === "number" ? step.delay : 0,
|
||||
})),
|
||||
});
|
||||
} else {
|
||||
navigate("../");
|
||||
|
@ -47,12 +47,14 @@ export default function SettingsMacrosEditRoute() {
|
|||
|
||||
setIsUpdating(true);
|
||||
try {
|
||||
const newMacros = macros.map(m =>
|
||||
m.id === macro.id ? {
|
||||
...macro,
|
||||
name: updatedMacro.name!.trim(),
|
||||
steps: updatedMacro.steps || [],
|
||||
} : m
|
||||
const newMacros = macros.map(m =>
|
||||
m.id === macro.id
|
||||
? {
|
||||
...macro,
|
||||
name: updatedMacro.name!.trim(),
|
||||
steps: updatedMacro.steps || [],
|
||||
}
|
||||
: m,
|
||||
);
|
||||
|
||||
await saveMacros(normalizeSortOrders(newMacros));
|
||||
|
@ -94,10 +96,7 @@ export default function SettingsMacrosEditRoute() {
|
|||
return (
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<SettingsPageHeader
|
||||
title="Edit Macro"
|
||||
description="Modify your keyboard macro"
|
||||
/>
|
||||
<SettingsPageHeader title="Edit Macro" description="Modify your keyboard macro" />
|
||||
<Button
|
||||
size="SM"
|
||||
theme="light"
|
||||
|
@ -131,4 +130,4 @@ export default function SettingsMacrosEditRoute() {
|
|||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,7 +22,6 @@ import useKeyboard from "@/hooks/useKeyboard";
|
|||
|
||||
import { cx } from "../cva.config";
|
||||
|
||||
|
||||
/* TODO: Migrate to using URLs instead of the global state. To simplify the refactoring, we'll keep the global state for now. */
|
||||
export default function SettingsRoute() {
|
||||
const location = useLocation();
|
||||
|
@ -31,7 +30,9 @@ export default function SettingsRoute() {
|
|||
const scrollContainerRef = useRef<HTMLDivElement>(null);
|
||||
const [showLeftGradient, setShowLeftGradient] = useState(false);
|
||||
const [showRightGradient, setShowRightGradient] = useState(false);
|
||||
const { width = 0 } = useResizeObserver({ ref: scrollContainerRef as React.RefObject<HTMLDivElement> });
|
||||
const { width = 0 } = useResizeObserver({
|
||||
ref: scrollContainerRef as React.RefObject<HTMLDivElement>,
|
||||
});
|
||||
|
||||
// Handle scroll position to show/hide gradients
|
||||
const handleScroll = () => {
|
||||
|
@ -82,8 +83,8 @@ export default function SettingsRoute() {
|
|||
return (
|
||||
<div className="pointer-events-auto relative mx-auto max-w-4xl translate-x-0 transform text-left dark:text-white">
|
||||
<div className="h-full">
|
||||
<div className="w-full gap-x-8 gap-y-4 space-y-4 md:grid md:grid-cols-8 md:space-y-0">
|
||||
<div className="w-full select-none space-y-4 md:col-span-2">
|
||||
<div className="w-full space-y-4 gap-x-8 gap-y-4 md:grid md:grid-cols-8 md:space-y-0">
|
||||
<div className="w-full space-y-4 select-none md:col-span-2">
|
||||
<Card className="flex w-full gap-x-4 overflow-hidden p-2 md:flex-col dark:bg-slate-800">
|
||||
<div className="md:hidden">
|
||||
<LinkButton
|
||||
|
@ -130,14 +131,14 @@ export default function SettingsRoute() {
|
|||
></div>
|
||||
<div
|
||||
ref={scrollContainerRef}
|
||||
className="hide-scrollbar relative flex w-full gap-x-4 overflow-x-auto whitespace-nowrap p-2 md:flex-col md:overflow-visible md:whitespace-normal dark:bg-slate-800"
|
||||
className="hide-scrollbar relative flex w-full gap-x-4 overflow-x-auto p-2 whitespace-nowrap md:flex-col md:overflow-visible md:whitespace-normal dark:bg-slate-800"
|
||||
>
|
||||
<div className="shrink-0">
|
||||
<NavLink
|
||||
to="general"
|
||||
className={({ isActive }) => (isActive ? "active" : "")}
|
||||
>
|
||||
<div className="flex items-center gap-x-2 rounded-md px-2.5 py-2.5 text-sm transition-colors hover:bg-slate-100 dark:hover:bg-slate-700 in-[.active]:bg-blue-50 in-[.active]:text-blue-700! md:in-[.active]:bg-transparent dark:in-[.active]:bg-blue-900 dark:in-[.active]:text-blue-200! dark:md:in-[.active]:bg-transparent">
|
||||
<div className="flex items-center gap-x-2 rounded-md px-2.5 py-2.5 text-sm transition-colors hover:bg-slate-100 in-[.active]:bg-blue-50 in-[.active]:text-blue-700! md:in-[.active]:bg-transparent dark:hover:bg-slate-700 dark:in-[.active]:bg-blue-900 dark:in-[.active]:text-blue-200! dark:md:in-[.active]:bg-transparent">
|
||||
<LuSettings className="h-4 w-4 shrink-0" />
|
||||
<h1>General</h1>
|
||||
</div>
|
||||
|
@ -148,7 +149,7 @@ export default function SettingsRoute() {
|
|||
to="mouse"
|
||||
className={({ isActive }) => (isActive ? "active" : "")}
|
||||
>
|
||||
<div className="flex items-center gap-x-2 rounded-md px-2.5 py-2.5 text-sm transition-colors hover:bg-slate-100 dark:hover:bg-slate-700 in-[.active]:bg-blue-50 in-[.active]:text-blue-700! md:in-[.active]:bg-transparent dark:in-[.active]:bg-blue-900 dark:in-[.active]:text-blue-200! dark:md:in-[.active]:bg-transparent">
|
||||
<div className="flex items-center gap-x-2 rounded-md px-2.5 py-2.5 text-sm transition-colors hover:bg-slate-100 in-[.active]:bg-blue-50 in-[.active]:text-blue-700! md:in-[.active]:bg-transparent dark:hover:bg-slate-700 dark:in-[.active]:bg-blue-900 dark:in-[.active]:text-blue-200! dark:md:in-[.active]:bg-transparent">
|
||||
<LuKeyboard className="h-4 w-4 shrink-0" />
|
||||
<h1>Mouse</h1>
|
||||
</div>
|
||||
|
@ -159,7 +160,7 @@ export default function SettingsRoute() {
|
|||
to="video"
|
||||
className={({ isActive }) => (isActive ? "active" : "")}
|
||||
>
|
||||
<div className="flex items-center gap-x-2 rounded-md px-2.5 py-2.5 text-sm transition-colors hover:bg-slate-100 dark:hover:bg-slate-700 in-[.active]:bg-blue-50 in-[.active]:text-blue-700! md:in-[.active]:bg-transparent dark:in-[.active]:bg-blue-900 dark:in-[.active]:text-blue-200! dark:md:in-[.active]:bg-transparent">
|
||||
<div className="flex items-center gap-x-2 rounded-md px-2.5 py-2.5 text-sm transition-colors hover:bg-slate-100 in-[.active]:bg-blue-50 in-[.active]:text-blue-700! md:in-[.active]:bg-transparent dark:hover:bg-slate-700 dark:in-[.active]:bg-blue-900 dark:in-[.active]:text-blue-200! dark:md:in-[.active]:bg-transparent">
|
||||
<LuVideo className="h-4 w-4 shrink-0" />
|
||||
<h1>Video</h1>
|
||||
</div>
|
||||
|
@ -170,7 +171,7 @@ export default function SettingsRoute() {
|
|||
to="hardware"
|
||||
className={({ isActive }) => (isActive ? "active" : "")}
|
||||
>
|
||||
<div className="flex items-center gap-x-2 rounded-md px-2.5 py-2.5 text-sm transition-colors hover:bg-slate-100 dark:hover:bg-slate-700 in-[.active]:bg-blue-50 in-[.active]:text-blue-700! md:in-[.active]:bg-transparent dark:in-[.active]:bg-blue-900 dark:in-[.active]:text-blue-200! dark:md:in-[.active]:bg-transparent">
|
||||
<div className="flex items-center gap-x-2 rounded-md px-2.5 py-2.5 text-sm transition-colors hover:bg-slate-100 in-[.active]:bg-blue-50 in-[.active]:text-blue-700! md:in-[.active]:bg-transparent dark:hover:bg-slate-700 dark:in-[.active]:bg-blue-900 dark:in-[.active]:text-blue-200! dark:md:in-[.active]:bg-transparent">
|
||||
<LuCpu className="h-4 w-4 shrink-0" />
|
||||
<h1>Hardware</h1>
|
||||
</div>
|
||||
|
@ -181,7 +182,7 @@ export default function SettingsRoute() {
|
|||
to="access"
|
||||
className={({ isActive }) => (isActive ? "active" : "")}
|
||||
>
|
||||
<div className="flex items-center gap-x-2 rounded-md px-2.5 py-2.5 text-sm transition-colors hover:bg-slate-100 dark:hover:bg-slate-700 in-[.active]:bg-blue-50 in-[.active]:text-blue-700! md:in-[.active]:bg-transparent dark:in-[.active]:bg-blue-900 dark:in-[.active]:text-blue-200! dark:md:in-[.active]:bg-transparent">
|
||||
<div className="flex items-center gap-x-2 rounded-md px-2.5 py-2.5 text-sm transition-colors hover:bg-slate-100 in-[.active]:bg-blue-50 in-[.active]:text-blue-700! md:in-[.active]:bg-transparent dark:hover:bg-slate-700 dark:in-[.active]:bg-blue-900 dark:in-[.active]:text-blue-200! dark:md:in-[.active]:bg-transparent">
|
||||
<LuShieldCheck className="h-4 w-4 shrink-0" />
|
||||
<h1>Access</h1>
|
||||
</div>
|
||||
|
@ -192,7 +193,7 @@ export default function SettingsRoute() {
|
|||
to="appearance"
|
||||
className={({ isActive }) => (isActive ? "active" : "")}
|
||||
>
|
||||
<div className="flex items-center gap-x-2 rounded-md px-2.5 py-2.5 text-sm transition-colors hover:bg-slate-100 dark:hover:bg-slate-700 in-[.active]:bg-blue-50 in-[.active]:text-blue-700! md:in-[.active]:bg-transparent dark:in-[.active]:bg-blue-900 dark:in-[.active]:text-blue-200! dark:md:in-[.active]:bg-transparent">
|
||||
<div className="flex items-center gap-x-2 rounded-md px-2.5 py-2.5 text-sm transition-colors hover:bg-slate-100 in-[.active]:bg-blue-50 in-[.active]:text-blue-700! md:in-[.active]:bg-transparent dark:hover:bg-slate-700 dark:in-[.active]:bg-blue-900 dark:in-[.active]:text-blue-200! dark:md:in-[.active]:bg-transparent">
|
||||
<LuPalette className="h-4 w-4 shrink-0" />
|
||||
<h1>Appearance</h1>
|
||||
</div>
|
||||
|
@ -203,7 +204,7 @@ export default function SettingsRoute() {
|
|||
to="macros"
|
||||
className={({ isActive }) => (isActive ? "active" : "")}
|
||||
>
|
||||
<div className="flex items-center gap-x-2 rounded-md px-2.5 py-2.5 text-sm transition-colors hover:bg-slate-100 dark:hover:bg-slate-700 in-[.active]:bg-blue-50 in-[.active]:text-blue-700! md:in-[.active]:bg-transparent dark:in-[.active]:bg-blue-900 dark:in-[.active]:text-blue-200! dark:md:in-[.active]:bg-transparent">
|
||||
<div className="flex items-center gap-x-2 rounded-md px-2.5 py-2.5 text-sm transition-colors hover:bg-slate-100 in-[.active]:bg-blue-50 in-[.active]:text-blue-700! md:in-[.active]:bg-transparent dark:hover:bg-slate-700 dark:in-[.active]:bg-blue-900 dark:in-[.active]:text-blue-200! dark:md:in-[.active]:bg-transparent">
|
||||
<LuCommand className="h-4 w-4 shrink-0" />
|
||||
<h1>Keyboard Macros</h1>
|
||||
</div>
|
||||
|
@ -214,7 +215,7 @@ export default function SettingsRoute() {
|
|||
to="network"
|
||||
className={({ isActive }) => (isActive ? "active" : "")}
|
||||
>
|
||||
<div className="flex items-center gap-x-2 rounded-md px-2.5 py-2.5 text-sm transition-colors hover:bg-slate-100 dark:hover:bg-slate-700 in-[.active]:bg-blue-50 in-[.active]:text-blue-700! md:in-[.active]:bg-transparent dark:in-[.active]:bg-blue-900 dark:in-[.active]:text-blue-200! dark:md:in-[.active]:bg-transparent">
|
||||
<div className="flex items-center gap-x-2 rounded-md px-2.5 py-2.5 text-sm transition-colors hover:bg-slate-100 in-[.active]:bg-blue-50 in-[.active]:text-blue-700! md:in-[.active]:bg-transparent dark:hover:bg-slate-700 dark:in-[.active]:bg-blue-900 dark:in-[.active]:text-blue-200! dark:md:in-[.active]:bg-transparent">
|
||||
<LuNetwork className="h-4 w-4 shrink-0" />
|
||||
<h1>Network</h1>
|
||||
</div>
|
||||
|
@ -225,7 +226,7 @@ export default function SettingsRoute() {
|
|||
to="advanced"
|
||||
className={({ isActive }) => (isActive ? "active" : "")}
|
||||
>
|
||||
<div className="flex items-center gap-x-2 rounded-md px-2.5 py-2.5 text-sm transition-colors hover:bg-slate-100 dark:hover:bg-slate-700 in-[.active]:bg-blue-50 in-[.active]:text-blue-700! md:in-[.active]:bg-transparent dark:in-[.active]:bg-blue-900 dark:in-[.active]:text-blue-200! dark:md:in-[.active]:bg-transparent">
|
||||
<div className="flex items-center gap-x-2 rounded-md px-2.5 py-2.5 text-sm transition-colors hover:bg-slate-100 in-[.active]:bg-blue-50 in-[.active]:text-blue-700! md:in-[.active]:bg-transparent dark:hover:bg-slate-700 dark:in-[.active]:bg-blue-900 dark:in-[.active]:text-blue-200! dark:md:in-[.active]:bg-transparent">
|
||||
<LuWrench className="h-4 w-4 shrink-0" />
|
||||
<h1>Advanced</h1>
|
||||
</div>
|
||||
|
@ -272,7 +273,7 @@ export function SettingsItem({
|
|||
return (
|
||||
<label
|
||||
className={cx(
|
||||
"flex select-none items-center justify-between gap-x-8 rounded",
|
||||
"flex items-center justify-between gap-x-8 rounded select-none",
|
||||
className,
|
||||
)}
|
||||
>
|
||||
|
@ -281,7 +282,7 @@ export function SettingsItem({
|
|||
<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>
|
||||
)}
|
||||
|
|
|
@ -84,7 +84,9 @@ export default function SettingsVideoRoute() {
|
|||
return;
|
||||
}
|
||||
|
||||
notifications.success(`Stream quality set to ${streamQualityOptions.find(x => x.value === factor)?.label}`);
|
||||
notifications.success(
|
||||
`Stream quality set to ${streamQualityOptions.find(x => x.value === factor)?.label}`,
|
||||
);
|
||||
setStreamQuality(factor);
|
||||
});
|
||||
};
|
||||
|
|
|
@ -59,14 +59,16 @@ export default function SetupRoute() {
|
|||
<div className="grid min-h-screen grid-rows-(--grid-layout)">
|
||||
<SimpleNavbar />
|
||||
<Container>
|
||||
<div className="flex items-center justify-center w-full h-full isolate">
|
||||
<div className="max-w-2xl -mt-32 space-y-8">
|
||||
<div className="isolate flex h-full w-full items-center justify-center">
|
||||
<div className="-mt-32 max-w-2xl space-y-8">
|
||||
<div className="text-center">
|
||||
<StepCounter currStepIdx={1} nSteps={2} />
|
||||
</div>
|
||||
|
||||
<div className="space-y-2 text-center">
|
||||
<h1 className="text-4xl font-semibold text-black dark:text-white">Let's name your device</h1>
|
||||
<h1 className="text-4xl font-semibold text-black dark:text-white">
|
||||
Let's name your device
|
||||
</h1>
|
||||
<p className="text-slate-600 dark:text-slate-400">
|
||||
Name your device so you can easily identify it later. You can change
|
||||
this name at any time.
|
||||
|
@ -74,7 +76,7 @@ export default function SetupRoute() {
|
|||
</div>
|
||||
|
||||
<Fieldset className="space-y-12">
|
||||
<Form method="POST" className="max-w-sm mx-auto space-y-4">
|
||||
<Form method="POST" className="mx-auto max-w-sm space-y-4">
|
||||
<InputFieldWithLabel
|
||||
label="Device Name"
|
||||
type="text"
|
||||
|
|
|
@ -704,7 +704,7 @@ export default function KvmIdRoute() {
|
|||
send("getUpdateStatus", {}, async resp => {
|
||||
if ("error" in resp) {
|
||||
notifications.error(`Failed to get device version: ${resp.error}`);
|
||||
return
|
||||
return;
|
||||
}
|
||||
|
||||
const result = resp.result as SystemVersionInfo;
|
||||
|
@ -797,7 +797,7 @@ export default function KvmIdRoute() {
|
|||
<WebRTCVideo />
|
||||
<div
|
||||
style={{ animationDuration: "500ms" }}
|
||||
className="animate-slideUpFade pointer-events-none absolute inset-0 flex items-center justify-center p-4"
|
||||
className="pointer-events-none absolute inset-0 flex animate-slideUpFade items-center justify-center p-4"
|
||||
>
|
||||
<div className="relative h-full max-h-[720px] w-full max-w-[1280px] rounded-md">
|
||||
{!!ConnectionStatusElement && ConnectionStatusElement}
|
||||
|
|
|
@ -11,18 +11,20 @@ export default function DevicesAlreadyAdopted() {
|
|||
<div className="grid min-h-screen grid-rows-(--grid-layout)">
|
||||
<SimpleNavbar />
|
||||
<Container>
|
||||
<div className="flex items-center justify-center w-full h-full isolate">
|
||||
<div className="max-w-2xl -mt-16 space-y-8">
|
||||
<div className="isolate flex h-full w-full items-center justify-center">
|
||||
<div className="-mt-16 max-w-2xl space-y-8">
|
||||
<div className="space-y-4 text-center">
|
||||
<h1 className="text-4xl font-semibold text-black dark:text-white">Device Already Registered</h1>
|
||||
<h1 className="text-4xl font-semibold text-black dark:text-white">
|
||||
Device Already Registered
|
||||
</h1>
|
||||
<p className="text-lg text-slate-600 dark:text-slate-400">
|
||||
This device is currently registered to another user in our cloud
|
||||
dashboard.
|
||||
</p>
|
||||
<p className="mt-4 text-lg text-slate-600 dark:text-slate-400">
|
||||
If you're the new owner, please ask the previous owner to de-register
|
||||
the device from their account in the cloud dashboard. If you believe
|
||||
this is an error, contact our support team for assistance.
|
||||
If you're the new owner, please ask the previous owner to
|
||||
de-register the device from their account in the cloud dashboard. If you
|
||||
believe this is an error, contact our support team for assistance.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -40,7 +40,7 @@ export default function DevicesRoute() {
|
|||
useInterval(revalidate.revalidate, 4000);
|
||||
return (
|
||||
<div className="relative h-full">
|
||||
<div className="grid h-full select-none grid-rows-(--grid-headerBody)">
|
||||
<div className="grid h-full grid-rows-(--grid-headerBody) select-none">
|
||||
<DashboardNavbar
|
||||
isLoggedIn={!!user}
|
||||
primaryLinks={[{ title: "Cloud Devices", to: "/devices" }]}
|
||||
|
|
|
@ -60,7 +60,7 @@ export default function WelcomeLocalModeRoute() {
|
|||
<Container>
|
||||
<div className="isolate flex h-full w-full items-center justify-center">
|
||||
<div className="max-w-xl space-y-8">
|
||||
<div className="animate-fadeIn flex items-center justify-center opacity-0">
|
||||
<div className="flex animate-fadeIn items-center justify-center opacity-0">
|
||||
<img
|
||||
src={LogoWhiteIcon}
|
||||
alt=""
|
||||
|
@ -83,14 +83,14 @@ export default function WelcomeLocalModeRoute() {
|
|||
|
||||
<Form method="POST" className="space-y-8">
|
||||
<div
|
||||
className="animate-fadeIn grid grid-cols-1 gap-6 opacity-0 sm:grid-cols-2"
|
||||
className="grid animate-fadeIn grid-cols-1 gap-6 opacity-0 sm:grid-cols-2"
|
||||
style={{ animationDelay: "400ms" }}
|
||||
>
|
||||
{["password", "noPassword"].map(mode => (
|
||||
<GridCard
|
||||
key={mode}
|
||||
cardClassName={cx("transition-all duration-100", {
|
||||
"outline-blue-700! outline-2!": selectedMode === mode,
|
||||
"outline-2! outline-blue-700!": selectedMode === mode,
|
||||
"hover:outline-blue-700!": selectedMode !== mode,
|
||||
})}
|
||||
>
|
||||
|
@ -116,7 +116,7 @@ export default function WelcomeLocalModeRoute() {
|
|||
onChange={() => {
|
||||
setSelectedMode(mode as "password" | "noPassword");
|
||||
}}
|
||||
className="form-radio absolute top-2 right-2 h-4 w-4 text-blue-600"
|
||||
className="absolute top-2 right-2 form-radio h-4 w-4 text-blue-600"
|
||||
/>
|
||||
</div>
|
||||
</GridCard>
|
||||
|
@ -133,7 +133,7 @@ export default function WelcomeLocalModeRoute() {
|
|||
)}
|
||||
|
||||
<div
|
||||
className="animate-fadeIn mx-auto max-w-sm opacity-0"
|
||||
className="mx-auto max-w-sm animate-fadeIn opacity-0"
|
||||
style={{ animationDelay: "500ms" }}
|
||||
>
|
||||
<Button
|
||||
|
@ -149,7 +149,7 @@ export default function WelcomeLocalModeRoute() {
|
|||
</Form>
|
||||
|
||||
<p
|
||||
className="animate-fadeIn mx-auto max-w-md text-center text-xs text-slate-500 opacity-0 dark:text-slate-400"
|
||||
className="mx-auto max-w-md animate-fadeIn text-center text-xs text-slate-500 opacity-0 dark:text-slate-400"
|
||||
style={{ animationDelay: "600ms" }}
|
||||
>
|
||||
You can always change your authentication method later in the settings.
|
||||
|
|
|
@ -71,7 +71,7 @@ export default function WelcomeLocalPasswordRoute() {
|
|||
<Container>
|
||||
<div className="isolate flex h-full w-full items-center justify-center">
|
||||
<div className="max-w-2xl space-y-8">
|
||||
<div className="animate-fadeIn flex items-center justify-center opacity-0">
|
||||
<div className="flex animate-fadeIn items-center justify-center opacity-0">
|
||||
<img
|
||||
src={LogoWhiteIcon}
|
||||
alt=""
|
||||
|
@ -159,7 +159,7 @@ export default function WelcomeLocalPasswordRoute() {
|
|||
</Fieldset>
|
||||
|
||||
<p
|
||||
className="animate-fadeIn max-w-md text-center text-xs text-slate-500 opacity-0 dark:text-slate-400"
|
||||
className="max-w-md animate-fadeIn text-center text-xs text-slate-500 opacity-0 dark:text-slate-400"
|
||||
style={{ animationDelay: "800ms" }}
|
||||
>
|
||||
This password will be used to secure your device data and protect against
|
||||
|
|
|
@ -45,7 +45,7 @@ export default function WelcomeRoute() {
|
|||
<div className="max-w-3xl text-center">
|
||||
<div className="space-y-8">
|
||||
<div className="space-y-4">
|
||||
<div className="animate-fadeIn animation-delay-1000 flex items-center justify-center opacity-0">
|
||||
<div className="flex animate-fadeIn items-center justify-center opacity-0 animation-delay-1000">
|
||||
<img
|
||||
src={LogoWhiteIcon}
|
||||
alt="JetKVM Logo"
|
||||
|
@ -58,7 +58,7 @@ export default function WelcomeRoute() {
|
|||
/>
|
||||
</div>
|
||||
|
||||
<div className="animate-fadeIn animation-delay-1500 space-y-1 opacity-0">
|
||||
<div className="animate-fadeIn space-y-1 opacity-0 animation-delay-1500">
|
||||
<h1 className="text-4xl font-semibold text-black dark:text-white">
|
||||
Welcome to JetKVM
|
||||
</h1>
|
||||
|
@ -72,19 +72,19 @@ export default function WelcomeRoute() {
|
|||
<img
|
||||
src={DeviceImage}
|
||||
alt="JetKVM Device"
|
||||
className="animation-delay-300 animate-fadeInScaleFloat max-w-md scale-[0.98] opacity-0 transition-all duration-1000 ease-out"
|
||||
className="max-w-md scale-[0.98] animate-fadeInScaleFloat opacity-0 transition-all duration-1000 ease-out animation-delay-300"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="-mt-8 space-y-4">
|
||||
<p
|
||||
style={{ animationDelay: "2000ms" }}
|
||||
className="animate-fadeIn mx-auto max-w-lg text-lg text-slate-700 opacity-0 dark:text-slate-300"
|
||||
className="mx-auto max-w-lg animate-fadeIn text-lg text-slate-700 opacity-0 dark:text-slate-300"
|
||||
>
|
||||
JetKVM combines powerful hardware with intuitive software to provide a
|
||||
seamless remote control experience.
|
||||
</p>
|
||||
<div className="animate-fadeIn animation-delay-2300 opacity-0">
|
||||
<div className="animate-fadeIn opacity-0 animation-delay-2300">
|
||||
<LinkButton
|
||||
size="LG"
|
||||
theme="light"
|
||||
|
|
Loading…
Reference in New Issue