chore: run eslint --fix on all code with new pretter rules to enforce prettier

This commit is contained in:
Adam Shiervani 2025-05-21 12:47:33 +02:00
parent cf71508bcd
commit 6d74e51086
61 changed files with 440 additions and 314 deletions

View File

@ -16,7 +16,7 @@ const sizes = {
const themes = { const themes = {
primary: cx( primary: cx(
// Base styles // Base styles
"bg-blue-700 dark:border-blue-600 border border-blue-900/60 text-white shadow-sm", "border border-blue-900/60 bg-blue-700 text-white shadow-sm dark:border-blue-600",
// Hover states // Hover states
"group-hover:bg-blue-800", "group-hover:bg-blue-800",
// Active states // Active states
@ -24,9 +24,9 @@ const themes = {
), ),
danger: cx( danger: cx(
// Base styles // Base styles
"bg-red-600 text-white border-red-700 shadow-xs shadow-red-200/80 dark:border-red-600 dark:shadow-red-900/20", "border-red-700 bg-red-600 text-white shadow-xs shadow-red-200/80 dark:border-red-600 dark:shadow-red-900/20",
// Hover states // Hover states
"group-hover:bg-red-700 group-hover:border-red-800 dark:group-hover:bg-red-700 dark:group-hover:border-red-600", "group-hover:border-red-800 group-hover:bg-red-700 dark:group-hover:border-red-600 dark:group-hover:bg-red-700",
// Active states // Active states
"group-active:bg-red-800 dark:group-active:bg-red-800", "group-active:bg-red-800 dark:group-active:bg-red-800",
// Focus states // Focus states
@ -34,7 +34,7 @@ const themes = {
), ),
light: cx( light: cx(
// Base styles // Base styles
"bg-white text-black border-slate-800/30 shadow-xs dark:bg-slate-800 dark:border-slate-300/20 dark:text-white", "border-slate-800/30 bg-white text-black shadow-xs dark:border-slate-300/20 dark:bg-slate-800 dark:text-white",
// Hover states // Hover states
"group-hover:bg-blue-50/80 dark:group-hover:bg-slate-700", "group-hover:bg-blue-50/80 dark:group-hover:bg-slate-700",
// Active states // Active states
@ -44,7 +44,7 @@ const themes = {
), ),
lightDanger: cx( lightDanger: cx(
// Base styles // Base styles
"bg-white text-black border-red-400/60 shadow-xs", "border-red-400/60 bg-white text-black shadow-xs",
// Hover states // Hover states
"group-hover:bg-red-50/80", "group-hover:bg-red-50/80",
// Active states // Active states
@ -54,9 +54,9 @@ const themes = {
), ),
blank: cx( blank: cx(
// Base styles // Base styles
"bg-white/0 text-black border-transparent dark:text-white", "border-transparent bg-white/0 text-black dark:text-white",
// Hover states // Hover states
"group-hover:bg-white group-hover:border-slate-800/30 group-hover:shadow-sm dark:group-hover:bg-slate-700 dark:group-hover:border-slate-600", "group-hover:border-slate-800/30 group-hover:bg-white group-hover:shadow-sm dark:group-hover:border-slate-600 dark:group-hover:bg-slate-700",
// Active states // Active states
"group-active:bg-slate-100/80", "group-active:bg-slate-100/80",
), ),
@ -65,16 +65,16 @@ const themes = {
const btnVariants = cva({ const btnVariants = cva({
base: cx( base: cx(
// Base styles // Base styles
"border rounded-sm select-none", "rounded-sm border select-none",
// Size classes // Size classes
"justify-center items-center shrink-0", "shrink-0 items-center justify-center",
// Transition classes // Transition classes
"outline-hidden transition-all duration-200", "outline-hidden transition-all duration-200",
// Text classes // Text classes
"font-display text-center font-medium leading-tight", "text-center font-display leading-tight font-medium",
// States // States
"group-focus:outline-hidden group-focus:ring-2 group-focus:ring-offset-2 group-focus:ring-blue-700", "group-focus:ring-2 group-focus:ring-blue-700 group-focus:ring-offset-2 group-focus:outline-hidden",
"group-disabled:opacity-50 group-disabled:pointer-events-none", "group-disabled:pointer-events-none group-disabled:opacity-50",
), ),
variants: { variants: {
@ -241,7 +241,7 @@ type LabelPropsType = Pick<HTMLLabelElement, "htmlFor"> &
React.ComponentProps<typeof ButtonContent> & { disabled?: boolean }; React.ComponentProps<typeof ButtonContent> & { disabled?: boolean };
export const LabelButton = ({ htmlFor, ...props }: LabelPropsType) => { export const LabelButton = ({ htmlFor, ...props }: LabelPropsType) => {
const classes = cx( const classes = cx(
"group outline-hidden block cursor-pointer", "group block cursor-pointer outline-hidden",
props.disabled ? "pointer-events-none opacity-70!" : "", props.disabled ? "pointer-events-none opacity-70!" : "",
props.fullWidth ? "w-full" : "", props.fullWidth ? "w-full" : "",
props.loading ? "pointer-events-none" : "", props.loading ? "pointer-events-none" : "",

View File

@ -8,10 +8,14 @@ interface Props {
export const CardHeader = ({ headline, description, Button }: Props) => { export const CardHeader = ({ headline, description, Button }: Props) => {
return ( return (
<div className="flex items-center justify-between pb-0 gap-x-4"> <div className="flex items-center justify-between gap-x-4 pb-0">
<div className="space-y-1 grow"> <div className="grow space-y-1">
<h3 className="text-lg font-bold leading-none text-black dark:text-white">{headline}</h3> <h3 className="text-lg leading-none font-bold text-black dark:text-white">
{description && <div className="text-sm text-slate-700 dark:text-slate-300">{description}</div>} {headline}
</h3>
{description && (
<div className="text-sm text-slate-700 dark:text-slate-300">{description}</div>
)}
</div> </div>
{Button && <div>{Button}</div>} {Button && <div>{Button}</div>}
</div> </div>

View File

@ -15,7 +15,7 @@ const checkboxVariants = cva({
"form-checkbox block rounded", "form-checkbox block rounded",
// Colors // Colors
"border-slate-300 dark:border-slate-600 bg-slate-50 dark:bg-slate-800 checked:accent-blue-700 checked:dark:accent-blue-500 transition-colors", "border-slate-300 bg-slate-50 transition-colors checked:accent-blue-700 dark:border-slate-600 dark:bg-slate-800 checked:dark:accent-blue-500",
// Hover // Hover
"hover:bg-slate-200/50 dark:hover:bg-slate-700/50", "hover:bg-slate-200/50 dark:hover:bg-slate-700/50",
@ -24,7 +24,7 @@ const checkboxVariants = cva({
"active:bg-slate-200 dark:active:bg-slate-700", "active:bg-slate-200 dark:active:bg-slate-700",
// Focus // Focus
"focus:border-slate-300 dark:focus:border-slate-600 focus:outline-hidden focus:ring-2 focus:ring-blue-700 dark:focus:ring-blue-500 focus:ring-offset-2 dark:focus:ring-offset-slate-900", "focus:border-slate-300 focus:ring-2 focus:ring-blue-700 focus:ring-offset-2 focus:outline-hidden dark:focus:border-slate-600 dark:focus:ring-blue-500 dark:focus:ring-offset-slate-900",
// Disabled // Disabled
"disabled:pointer-events-none disabled:opacity-30", "disabled:pointer-events-none disabled:opacity-30",

View File

@ -74,11 +74,11 @@ export function Combobox({
"dark:bg-slate-800 dark:text-white dark:hover:bg-slate-700 dark:active:bg-slate-800/60", "dark:bg-slate-800 dark:text-white dark:hover:bg-slate-700 dark:active:bg-slate-800/60",
// Focus // Focus
"focus:outline-blue-600 focus:ring-2 focus:ring-blue-700 focus:ring-offset-2 dark:focus:outline-blue-500 dark:focus:ring-blue-500", "focus:ring-2 focus:ring-blue-700 focus:ring-offset-2 focus:outline-blue-600 dark:focus:ring-blue-500 dark:focus:outline-blue-500",
// Disabled // Disabled
disabled && disabled &&
"pointer-events-none select-none bg-slate-50 text-slate-500/80 disabled:hover:bg-white dark:bg-slate-800 dark:text-slate-400/80 dark:disabled:hover:bg-slate-800", "pointer-events-none bg-slate-50 text-slate-500/80 select-none disabled:hover:bg-white dark:bg-slate-800 dark:text-slate-400/80 dark:disabled:hover:bg-slate-800",
)} )}
placeholder={disabled ? disabledMessage : placeholder} placeholder={disabled ? disabledMessage : placeholder}
displayValue={displayValue} displayValue={displayValue}
@ -95,7 +95,7 @@ export function Combobox({
value={option} value={option}
className={clsx( className={clsx(
// General styling // General styling
"cursor-default select-none px-4 py-2", "cursor-default px-4 py-2 select-none",
// Hover and active states // Hover and active states
"hover:bg-blue-50/80 ui-active:bg-blue-50/80 ui-active:text-blue-900", "hover:bg-blue-50/80 ui-active:bg-blue-50/80 ui-active:text-blue-900",

View File

@ -84,8 +84,8 @@ export function ConfirmDialog({
> >
<Icon aria-hidden="true" className={cx("size-6", iconClass)} /> <Icon aria-hidden="true" className={cx("size-6", iconClass)} />
</div> </div>
<div className="mt-3 text-center sm:ml-4 sm:mt-0 sm:text-left"> <div className="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left">
<h2 className="text-lg font-bold leading-tight text-black dark:text-white"> <h2 className="text-lg leading-tight font-bold text-black dark:text-white">
{title} {title}
</h2> </h2>
<div className="mt-2 text-sm leading-snug text-slate-600 dark:text-slate-400"> <div className="mt-2 text-sm leading-snug text-slate-600 dark:text-slate-400">

View File

@ -4,7 +4,7 @@ import React, { ReactNode } from "react";
import { cx } from "@/cva.config"; import { cx } from "@/cva.config";
function Container({ children, className }: { children: ReactNode; className?: string }) { function Container({ children, className }: { children: ReactNode; className?: string }) {
return <div className={cx("mx-auto h-full w-full px-8 ", className)}>{children}</div>; return <div className={cx("mx-auto h-full w-full px-8", className)}>{children}</div>;
} }
function Article({ children }: { children: React.ReactNode }) { function Article({ children }: { children: React.ReactNode }) {

View File

@ -18,7 +18,7 @@ export default function CustomTooltip({ payload }: CustomTooltipProps) {
<div className="space-y-1"> <div className="space-y-1">
<div className="flex items-center gap-x-1"> <div className="flex items-center gap-x-1">
<div className="h-[2px] w-2 bg-blue-700" /> <div className="h-[2px] w-2 bg-blue-700" />
<span > <span>
{stat} {toolTipData?.unit} {stat} {toolTipData?.unit}
</span> </span>
</div> </div>

View File

@ -14,7 +14,7 @@ export default function DhcpLeaseCard({
}) { }) {
return ( return (
<GridCard> <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"> <div className="space-y-3">
<h3 className="text-base font-bold text-slate-900 dark:text-white"> <h3 className="text-base font-bold text-slate-900 dark:text-white">
DHCP Lease Information DHCP Lease Information

View File

@ -32,7 +32,7 @@ export default function EmptyCard({
{IconElm && ( {IconElm && (
<IconElm className="mx-auto h-5 w-5 text-blue-600 dark:text-blue-600" /> <IconElm className="mx-auto h-5 w-5 text-blue-600 dark:text-blue-600" />
)} )}
<h4 className="text-base font-bold leading-none text-black dark:text-white"> <h4 className="text-base leading-none font-bold text-black dark:text-white">
{headline} {headline}
</h4> </h4>
</div> </div>

View File

@ -21,7 +21,7 @@ export default function FieldLabel({
<label <label
htmlFor={id} htmlFor={id}
className={cx( className={cx(
"flex select-none flex-col text-left font-display text-[13px] font-semibold leading-snug text-black dark:text-white", "flex flex-col text-left font-display text-[13px] leading-snug font-semibold text-black select-none dark:text-white",
disabled && "opacity-50", disabled && "opacity-50",
)} )}
> >
@ -35,8 +35,8 @@ export default function FieldLabel({
); );
} else if (as === "span") { } else if (as === "span") {
return ( return (
<div className="flex select-none flex-col"> <div className="flex flex-col select-none">
<span className="font-display text-[13px] font-medium leading-snug text-black dark:text-white"> <span className="font-display text-[13px] leading-snug font-medium text-black dark:text-white">
{label} {label}
</span> </span>
{description && ( {description && (

View File

@ -112,7 +112,7 @@ export default function DashboardNavbar({
className="size-6 rounded-full border-2 border-transparent transition-colors group-hover:border-blue-700" className="size-6 rounded-full border-2 border-transparent transition-colors group-hover:border-blue-700"
/> />
) : userEmail ? ( ) : userEmail ? (
<span className="font-display max-w-[200px] truncate text-sm/6 font-semibold"> <span className="max-w-[200px] truncate font-display text-sm/6 font-semibold">
{userEmail} {userEmail}
</span> </span>
) : null} ) : null}
@ -133,7 +133,7 @@ export default function DashboardNavbar({
<div className="font-display text-xs"> <div className="font-display text-xs">
Logged in as Logged in as
</div> </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} {userEmail}
</div> </div>
</div> </div>

View File

@ -46,10 +46,10 @@ export default function InfoBar() {
const hdmiState = useVideoStore(state => state.hdmiState); const hdmiState = useVideoStore(state => state.hdmiState);
return ( return (
<div className="bg-white border-t border-t-slate-800/30 text-slate-800 dark:border-t-slate-300/20 dark:bg-slate-900 dark:text-slate-300"> <div className="border-t border-t-slate-800/30 bg-white text-slate-800 dark:border-t-slate-300/20 dark:bg-slate-900 dark:text-slate-300">
<div className="flex flex-wrap items-stretch justify-between gap-1"> <div className="flex flex-wrap items-stretch justify-between gap-1">
<div className="flex items-center"> <div className="flex items-center">
<div className="flex flex-wrap items-center pl-2 gap-x-4"> <div className="flex flex-wrap items-center gap-x-4 pl-2">
{settings.debugMode ? ( {settings.debugMode ? (
<div className="flex"> <div className="flex">
<span className="text-xs font-semibold">Resolution:</span>{" "} <span className="text-xs font-semibold">Resolution:</span>{" "}
@ -64,7 +64,7 @@ export default function InfoBar() {
</div> </div>
) : null} ) : null}
{(settings.debugMode && settings.mouseMode == "absolute") ? ( {settings.debugMode && settings.mouseMode == "absolute" ? (
<div className="flex w-[118px] items-center gap-x-1"> <div className="flex w-[118px] items-center gap-x-1">
<span className="text-xs font-semibold">Pointer:</span> <span className="text-xs font-semibold">Pointer:</span>
<span className="text-xs"> <span className="text-xs">
@ -73,13 +73,13 @@ export default function InfoBar() {
</div> </div>
) : null} ) : null}
{(settings.debugMode && settings.mouseMode == "relative") ? ( {settings.debugMode && settings.mouseMode == "relative" ? (
<div className="flex w-[118px] items-center gap-x-1"> <div className="flex w-[118px] items-center gap-x-1">
<span className="text-xs font-semibold">Last Move:</span> <span className="text-xs font-semibold">Last Move:</span>
<span className="text-xs"> <span className="text-xs">
{mouseMove ? {mouseMove
`${mouseMove.x},${mouseMove.y} ${mouseMove.buttons ? `(${mouseMove.buttons})` : ""}` : ? `${mouseMove.x},${mouseMove.y} ${mouseMove.buttons ? `(${mouseMove.buttons})` : ""}`
"N/A"} : "N/A"}
</span> </span>
</div> </div>
) : null} ) : null}
@ -112,7 +112,7 @@ export default function InfoBar() {
</div> </div>
</div> </div>
</div> </div>
<div className="flex items-center divide-x first:divide-l divide-slate-800/20 dark:divide-slate-300/20"> <div className="first:divide-l flex items-center divide-x divide-slate-800/20 dark:divide-slate-300/20">
{isTurnServerInUse && ( {isTurnServerInUse && (
<div className="shrink-0 p-1 px-1.5 text-xs text-black dark:text-white"> <div className="shrink-0 p-1 px-1.5 text-xs text-black dark:text-white">
Relayed by Cloudflare Relayed by Cloudflare

View File

@ -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", "[&:has(:user-invalid)]:ring-2 [&:has(:user-invalid)]:ring-red-600 [&:has(:user-invalid)]:ring-offset-2",
// Focus Within // Focus Within
"focus-within:border-slate-300 dark:focus-within:border-slate-600 focus-within:outline-hidden focus-within:ring-2 focus-within:ring-blue-700 focus-within:ring-offset-2", "focus-within:border-slate-300 focus-within:ring-2 focus-within:ring-blue-700 focus-within:ring-offset-2 focus-within:outline-hidden dark:focus-within:border-slate-600",
// Disabled Within // Disabled Within
"disabled-within:pointer-events-none disabled-within:select-none disabled-within:bg-slate-50 dark:disabled-within:bg-slate-800 disabled-within:text-slate-500/80", "disabled-within:pointer-events-none disabled-within:bg-slate-50 disabled-within:text-slate-500/80 disabled-within:select-none dark:disabled-within:bg-slate-800",
)} )}
> >
{LeadingElm && ( {LeadingElm && (
<div className={clsx("pointer-events-none border-r border-r-slate-300 dark:border-r-slate-600")}> <div
className={clsx(
"pointer-events-none border-r border-r-slate-300 dark:border-r-slate-600",
)}
>
{LeadingElm} {LeadingElm}
</div> </div>
)} )}
@ -61,12 +65,12 @@ const InputField = forwardRef<HTMLInputElement, InputFieldProps>(function InputF
sizeClasses, sizeClasses,
TrailingElm ? "pr-2" : "", TrailingElm ? "pr-2" : "",
className, className,
"block flex-1 border-0 bg-transparent leading-none placeholder:text-sm placeholder:text-slate-300 dark:placeholder:text-slate-500 focus:ring-0 text-black dark:text-white", "block flex-1 border-0 bg-transparent leading-none text-black placeholder:text-sm placeholder:text-slate-300 focus:ring-0 dark:text-white dark:placeholder:text-slate-500",
)} )}
{...props} {...props}
/> />
{TrailingElm && ( {TrailingElm && (
<div className="flex items-center pr-3 pointer-events-none">{TrailingElm}</div> <div className="pointer-events-none flex items-center pr-3">{TrailingElm}</div>
)} )}
</Card> </Card>
{error && <FieldError error={error} />} {error && <FieldError error={error} />}

View File

@ -33,56 +33,54 @@ export default function Ipv6NetworkCard({
{networkState?.ipv6_addresses && networkState?.ipv6_addresses.length > 0 && ( {networkState?.ipv6_addresses && networkState?.ipv6_addresses.length > 0 && (
<div className="space-y-3"> <div className="space-y-3">
<h4 className="text-sm font-semibold">IPv6 Addresses</h4> <h4 className="text-sm font-semibold">IPv6 Addresses</h4>
{networkState.ipv6_addresses.map( {networkState.ipv6_addresses.map(addr => (
addr => ( <div
<div key={addr.address}
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"
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="grid grid-cols-2 gap-x-8 gap-y-4"> <div className="col-span-2 flex flex-col justify-between">
<div className="col-span-2 flex flex-col justify-between"> <span className="text-sm text-slate-600 dark:text-slate-400">
<span className="text-sm text-slate-600 dark:text-slate-400"> Address
Address </span>
</span> <span className="text-sm font-medium">{addr.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>
{addr.valid_lifetime && (
<div className="flex flex-col justify-between">
<span className="text-sm text-slate-600 dark:text-slate-400">
Valid Lifetime
</span>
<span className="text-sm font-medium">
{addr.valid_lifetime === "" ? (
<span className="text-slate-400 dark:text-slate-600">
N/A
</span>
) : (
<LifeTimeLabel lifetime={`${addr.valid_lifetime}`} />
)}
</span>
</div>
)}
{addr.preferred_lifetime && (
<div className="flex flex-col justify-between">
<span className="text-sm text-slate-600 dark:text-slate-400">
Preferred Lifetime
</span>
<span className="text-sm font-medium">
{addr.preferred_lifetime === "" ? (
<span className="text-slate-400 dark:text-slate-600">
N/A
</span>
) : (
<LifeTimeLabel lifetime={`${addr.preferred_lifetime}`} />
)}
</span>
</div>
)}
</div> </div>
), </div>
)} ))}
</div> </div>
)} )}
</div> </div>

View File

@ -52,10 +52,10 @@ export default function KvmCard({
}) { }) {
return ( return (
<Card> <Card>
<div className="px-5 py-5 space-y-3"> <div className="space-y-3 px-5 py-5">
<div className="flex justify-between items-center"> <div className="flex items-center justify-between">
<div className="space-y-1.5"> <div className="space-y-1.5">
<div className="text-lg font-bold leading-none text-black dark:text-white"> <div className="text-lg leading-none font-bold text-black dark:text-white">
{title} {title}
</div> </div>
@ -66,7 +66,7 @@ export default function KvmCard({
</div> </div>
) : ( ) : (
<div className="flex items-center gap-x-1.5"> <div className="flex items-center gap-x-1.5">
<div className="h-2.5 w-2.5 rounded-full border border-slate-400/60 dark:border-slate-500 bg-slate-200 dark:bg-slate-600" /> <div className="h-2.5 w-2.5 rounded-full border border-slate-400/60 bg-slate-200 dark:border-slate-500 dark:bg-slate-600" />
<div className="text-sm text-black dark:text-white"> <div className="text-sm text-black dark:text-white">
{lastSeen ? ( {lastSeen ? (
<>Last online {getRelativeTimeString(lastSeen)}</> <>Last online {getRelativeTimeString(lastSeen)}</>
@ -111,14 +111,14 @@ export default function KvmCard({
<MenuItems <MenuItems
transition transition
className="data-closed:scale-95 data-closed:transform data-closed:opacity-0 data-enter:duration-100 data-leave:duration-75 data-enter:ease-out data-leave:ease-in" className="data-closed:scale-95 data-closed:transform data-closed:opacity-0 data-enter:duration-100 data-enter:ease-out data-leave:duration-75 data-leave:ease-in"
> >
<Card className="absolute right-0 z-10 w-56 px-1 mt-2 transition origin-top-right ring-1 ring-black/50 focus:outline-hidden"> <Card className="absolute right-0 z-10 mt-2 w-56 origin-top-right px-1 ring-1 ring-black/50 transition focus:outline-hidden">
<div className="divide-y divide-slate-800/20 dark:divide-slate-300/20"> <div className="divide-y divide-slate-800/20 dark:divide-slate-300/20">
<MenuItem> <MenuItem>
<div> <div>
<div className="block w-full"> <div className="block w-full">
<div className="flex items-center px-2 my-1 text-sm transition-colors rounded-md gap-x-2 hover:bg-slate-100 dark:hover:bg-slate-700"> <div className="my-1 flex items-center gap-x-2 rounded-md px-2 text-sm transition-colors hover:bg-slate-100 dark:hover:bg-slate-700">
<Link <Link
className="block w-full py-1.5 text-black dark:text-white" className="block w-full py-1.5 text-black dark:text-white"
to={`./${id}/rename`} to={`./${id}/rename`}
@ -132,7 +132,7 @@ export default function KvmCard({
<MenuItem> <MenuItem>
<div> <div>
<div className="block w-full"> <div className="block w-full">
<div className="flex items-center px-2 my-1 text-sm transition-colors rounded-md gap-x-2 hover:bg-slate-100 dark:hover:bg-slate-700"> <div className="my-1 flex items-center gap-x-2 rounded-md px-2 text-sm transition-colors hover:bg-slate-100 dark:hover:bg-slate-700">
<Link <Link
className="block w-full py-1.5 text-black dark:text-white" className="block w-full py-1.5 text-black dark:text-white"
to={`./${id}/deregister`} to={`./${id}/deregister`}

View File

@ -1,10 +1,6 @@
import clsx from "clsx"; import clsx from "clsx";
export default function LoadingSpinner({ export default function LoadingSpinner({ className }: { className: string | undefined }) {
className,
}: {
className: string | undefined;
}) {
return ( return (
<svg <svg
className={clsx(className, "shrink-0 animate-spin p-[2px]")} className={clsx(className, "shrink-0 animate-spin p-[2px]")}

View File

@ -25,7 +25,7 @@ export default function MacroBar() {
} }
return ( return (
<Container className="bg-white dark:bg-slate-900 border-b border-b-slate-800/20 dark:border-b-slate-300/20"> <Container className="border-b border-b-slate-800/20 bg-white dark:border-b-slate-300/20 dark:bg-slate-900">
<div className="flex items-center gap-x-2 py-1.5"> <div className="flex items-center gap-x-2 py-1.5">
<div className="absolute -ml-5"> <div className="absolute -ml-5">
<LuCommand className="h-4 w-4 text-slate-500" /> <LuCommand className="h-4 w-4 text-slate-500" />

View File

@ -9,7 +9,7 @@ import { MAX_KEYS_PER_STEP, DEFAULT_DELAY } from "@/constants/macros";
import FieldLabel from "@/components/FieldLabel"; import FieldLabel from "@/components/FieldLabel";
// Filter out modifier keys since they're handled in the modifiers section // 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) const keyOptions = Object.keys(keys)
.filter(key => !modifierKeyPrefixes.some(prefix => key.startsWith(prefix))) .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> = { const groupedModifiers: Record<string, typeof modifierOptions> = {
Control: modifierOptions.filter(mod => mod.value.startsWith('Control')), Control: modifierOptions.filter(mod => mod.value.startsWith("Control")),
Shift: modifierOptions.filter(mod => mod.value.startsWith('Shift')), Shift: modifierOptions.filter(mod => mod.value.startsWith("Shift")),
Alt: modifierOptions.filter(mod => mod.value.startsWith('Alt')), Alt: modifierOptions.filter(mod => mod.value.startsWith("Alt")),
Meta: modifierOptions.filter(mod => mod.value.startsWith('Meta')), Meta: modifierOptions.filter(mod => mod.value.startsWith("Meta")),
}; };
const basePresetDelays = [ const basePresetDelays = [
@ -84,16 +84,20 @@ export function MacroStepCard({
keyQuery, keyQuery,
onModifierChange, onModifierChange,
onDelayChange, onDelayChange,
isLastStep isLastStep,
}: MacroStepCardProps) { }: MacroStepCardProps) {
const getFilteredKeys = () => { const getFilteredKeys = () => {
const selectedKeys = ensureArray(step.keys); 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 === '') { if (keyQuery === "") {
return availableKeys; return availableKeys;
} else { } 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> </div>
<div className="space-y-4 mt-2"> <div className="mt-2 space-y-4">
<div className="w-full flex flex-col gap-2"> <div className="flex w-full flex-col gap-2">
<FieldLabel label="Modifiers" /> <FieldLabel label="Modifiers" />
<div className="inline-flex flex-wrap gap-3"> <div className="inline-flex flex-wrap gap-3">
{Object.entries(groupedModifiers).map(([group, mods]) => ( {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"> <div
<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"> 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} {group}
</span> </span>
<div className="flex flex-wrap gap-4 pt-1"> <div className="flex flex-wrap gap-4 pt-1">
@ -150,8 +157,12 @@ export function MacroStepCard({
<Button <Button
key={option.value} key={option.value}
size="XS" size="XS"
theme={ensureArray(step.modifiers).includes(option.value) ? "primary" : "light"} theme={
text={option.label.split(' ')[1] || option.label} ensureArray(step.modifiers).includes(option.value)
? "primary"
: "light"
}
text={option.label.split(" ")[1] || option.label}
onClick={() => { onClick={() => {
const modifiersArray = ensureArray(step.modifiers); const modifiersArray = ensureArray(step.modifiers);
const isSelected = modifiersArray.includes(option.value); const isSelected = modifiersArray.includes(option.value);
@ -168,26 +179,29 @@ export function MacroStepCard({
</div> </div>
</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"> <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> </div>
{ensureArray(step.keys) && step.keys.length > 0 && ( {ensureArray(step.keys) && step.keys.length > 0 && (
<div className="flex flex-wrap gap-1 pb-2"> <div className="flex flex-wrap gap-1 pb-2">
{step.keys.map((key, keyIndex) => ( {step.keys.map((key, keyIndex) => (
<span <span
key={keyIndex} 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"> <span className="px-1">{keyDisplayMap[key] || key}</span>
{keyDisplayMap[key] || key}
</span>
<Button <Button
size="XS" size="XS"
className="" className=""
theme="blank" theme="blank"
onClick={() => { onClick={() => {
const newKeys = ensureArray(step.keys).filter((_, i) => i !== keyIndex); const newKeys = ensureArray(step.keys).filter(
(_, i) => i !== keyIndex,
);
onKeySelect({ value: null, keys: newKeys }); onKeySelect({ value: null, keys: newKeys });
}} }}
LeadingIcon={LuX} LeadingIcon={LuX}
@ -200,7 +214,7 @@ export function MacroStepCard({
<Combobox <Combobox
onChange={(value: { value: string; label: string }) => { onChange={(value: { value: string; label: string }) => {
onKeySelect(value); onKeySelect(value);
onKeyQueryChange(''); onKeyQueryChange("");
}} }}
displayValue={() => keyQuery} displayValue={() => keyQuery}
onInputChange={onKeyQueryChange} onInputChange={onKeyQueryChange}
@ -209,22 +223,29 @@ export function MacroStepCard({
size="SM" size="SM"
immediate immediate
disabled={ensureArray(step.keys).length >= MAX_KEYS_PER_STEP} 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" emptyMessage="No matching keys found"
/> />
</div> </div>
</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"> <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>
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
<SelectMenuBasic <SelectMenuBasic
size="SM" size="SM"
fullWidth fullWidth
value={step.delay.toString()} value={step.delay.toString()}
onChange={(e) => onDelayChange(parseInt(e.target.value, 10))} onChange={e => onDelayChange(parseInt(e.target.value, 10))}
options={PRESET_DELAYS} options={PRESET_DELAYS}
/> />
</div> </div>

View File

@ -18,18 +18,21 @@ const Modal = React.memo(function Modal({
<Dialog open={open} onClose={onClose} className="relative z-20"> <Dialog open={open} onClose={onClose} className="relative z-20">
<DialogBackdrop <DialogBackdrop
transition transition
className="fixed inset-0 bg-gray-500/75 transition-opacity data-closed:opacity-0 data-enter:duration-500 data-leave:duration-200 data-enter:ease-out data-leave:ease-in dark:bg-slate-900/90" className="fixed inset-0 bg-gray-500/75 transition-opacity data-closed:opacity-0 data-enter:duration-500 data-enter:ease-out data-leave:duration-200 data-leave:ease-in dark:bg-slate-900/90"
/> />
<div className="fixed inset-0 z-20 w-screen overflow-y-auto" style={{ <div
scrollbarGutter: 'stable' className="fixed inset-0 z-20 w-screen overflow-y-auto"
}}> style={{
scrollbarGutter: "stable",
}}
>
{/* TODO: This doesn't work well with other-sessions */} {/* TODO: This doesn't work well with other-sessions */}
<div className="flex min-h-full items-end justify-center p-4 text-center md:items-baseline md:p-4"> <div className="flex min-h-full items-end justify-center p-4 text-center md:items-baseline md:p-4">
<DialogPanel <DialogPanel
transition transition
className={cx( className={cx(
"pointer-events-none relative w-full md:my-8 md:mt-[10vh]!", "pointer-events-none relative w-full md:my-8 md:mt-[10vh]!",
"transform transition-all data-closed:translate-y-8 data-closed:opacity-0 data-enter:duration-500 data-leave:duration-200 data-enter:ease-out data-leave:ease-in", "transform transition-all data-closed:translate-y-8 data-closed:opacity-0 data-enter:duration-500 data-enter:ease-out data-leave:duration-200 data-leave:ease-in",
className, className,
)} )}
> >

View File

@ -6,7 +6,6 @@ import { cva } from "@/cva.config";
import Card from "./Card"; import Card from "./Card";
type SelectMenuProps = Pick< type SelectMenuProps = Pick<
JSX.IntrinsicElements["select"], JSX.IntrinsicElements["select"],
"disabled" | "onChange" | "name" | "value" "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", "invalid:ring-2 invalid:ring-red-600 invalid:ring-offset-2",
// Focus // Focus
"focus:outline-blue-600 focus:ring-2 focus:ring-blue-700 focus:ring-offset-2 dark:focus:outline-blue-500 dark:focus:ring-blue-500", "focus:ring-2 focus:ring-blue-700 focus:ring-offset-2 focus:outline-blue-600 dark:focus:ring-blue-500 dark:focus:outline-blue-500",
// Disabled // Disabled
"disabled:pointer-events-none disabled:select-none disabled:bg-slate-50 disabled:text-slate-500/80 dark:disabled:bg-slate-800 dark:disabled:text-slate-400/80", "disabled:pointer-events-none disabled:bg-slate-50 disabled:text-slate-500/80 disabled:select-none dark:disabled:bg-slate-800 dark:disabled:text-slate-400/80",
)} )}
value={value} value={value}
id={id} id={id}

View File

@ -9,7 +9,7 @@ export function SettingsPageHeader({
}) { }) {
return ( return (
<div className="select-none"> <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 className="text-sm text-black dark:text-slate-300">{description}</div>
</div> </div>
); );

View File

@ -10,9 +10,9 @@ export default function SidebarHeader({
setSidebarView: (view: AvailableSidebarViews | null) => void; setSidebarView: (view: AvailableSidebarViews | null) => void;
}) { }) {
return ( return (
<div className="flex items-center justify-between border-b border-b-slate-800/20 bg-white px-4 py-1.5 font-semibold text-black dark:bg-slate-900 dark:border-b-slate-300/20"> <div className="flex items-center justify-between border-b border-b-slate-800/20 bg-white px-4 py-1.5 font-semibold text-black dark:border-b-slate-300/20 dark:bg-slate-900">
<div className="min-w-0" style={{ flex: 1 }}> <div className="min-w-0" style={{ flex: 1 }}>
<h2 className="text-sm text-black truncate dark:text-white">{title}</h2> <h2 className="truncate text-sm text-black dark:text-white">{title}</h2>
</div> </div>
<Button <Button
size="XS" size="XS"

View File

@ -5,16 +5,19 @@ import Container from "@/components/Container";
import LogoBlueIcon from "@/assets/logo-blue.png"; import LogoBlueIcon from "@/assets/logo-blue.png";
import LogoWhiteIcon from "@/assets/logo-white.svg"; 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) { export default function SimpleNavbar({ logoHref, actionElement }: Props) {
return ( return (
<div> <div>
<Container> <Container>
<div className="pb-4 my-4 border-b border-b-800/20 isolate dark:border-b-slate-300/20"> <div className="border-b-800/20 isolate my-4 border-b pb-4 dark:border-b-slate-300/20">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<Link to={logoHref ?? "/"} className="hidden h-[26px] dark:inline-block"> <Link to={logoHref ?? "/"} className="hidden h-[26px] dark:inline-block">
<img src={LogoWhiteIcon} alt="" className="h-[26px] dark:block hidden" /> <img src={LogoWhiteIcon} alt="" className="hidden h-[26px] dark:block" />
<img src={LogoBlueIcon} alt="" className="h-[26px] dark:hidden" /> <img src={LogoBlueIcon} alt="" className="h-[26px] dark:hidden" />
</Link> </Link>
<div>{actionElement}</div> <div>{actionElement}</div>

View File

@ -18,7 +18,7 @@ export default function StatusCard({
statusIndicatorClassName, statusIndicatorClassName,
}: Props) { }: Props) {
return ( return (
<div className="flex items-center gap-x-3 rounded-md border bg-white dark:border-slate-600 dark:bg-slate-800 dark:text-white border-slate-800/20 px-2 py-1.5"> <div className="flex items-center gap-x-3 rounded-md border border-slate-800/20 bg-white px-2 py-1.5 dark:border-slate-600 dark:bg-slate-800 dark:text-white">
{Icon ? ( {Icon ? (
<span> <span>
<Icon className={cx(iconClassName, "shrink-0")} /> <Icon className={cx(iconClassName, "shrink-0")} />
@ -26,7 +26,7 @@ export default function StatusCard({
) : null} ) : null}
<div className="space-y-1"> <div className="space-y-1">
<div className="text-xs font-semibold leading-none transition text-ellipsis"> <div className="text-xs leading-none font-semibold text-ellipsis transition">
{title} {title}
</div> </div>
<div className="text-xs leading-none"> <div className="text-xs leading-none">

View File

@ -23,7 +23,7 @@ const variants = cva({
export default function StepCounter({ nSteps, currStepIdx, size = "MD" }: Props) { export default function StepCounter({ nSteps, currStepIdx, size = "MD" }: Props) {
const textStyle = variants({ size }); const textStyle = variants({ size });
return ( return (
<Card className="inline-flex! w-auto select-none items-center justify-center gap-x-2 rounded-lg p-1"> <Card className="inline-flex! w-auto items-center justify-center gap-x-2 rounded-lg p-1 select-none">
{[...Array(nSteps).keys()].map(i => { {[...Array(nSteps).keys()].map(i => {
if (i < currStepIdx) { if (i < currStepIdx) {
return ( return (

View File

@ -161,10 +161,7 @@ function Terminal({
}, [ref, instance]); }, [ref, instance]);
return ( return (
<div <div onKeyDown={e => e.stopPropagation()} onKeyUp={e => e.stopPropagation()}>
onKeyDown={e => e.stopPropagation()}
onKeyUp={e => e.stopPropagation()}
>
<div> <div>
<div <div
className={cx( className={cx(
@ -183,7 +180,7 @@ function Terminal({
> >
<div className="h-[500px] w-full bg-[#0f172a]"> <div className="h-[500px] w-full bg-[#0f172a]">
<div className="flex items-center justify-center border-y border-y-slate-800/30 bg-white px-2 py-1 dark:border-y-slate-300/20 dark:bg-slate-800"> <div className="flex items-center justify-center border-y border-y-slate-800/30 bg-white px-2 py-1 dark:border-y-slate-300/20 dark:bg-slate-800">
<h2 className="select-none self-center font-sans text-[12px] text-slate-700 dark:text-slate-300"> <h2 className="self-center font-sans text-[12px] text-slate-700 select-none dark:text-slate-300">
{title} {title}
</h2> </h2>
<div className="absolute right-2"> <div className="absolute right-2">

View File

@ -17,7 +17,7 @@ const TextArea = React.forwardRef<HTMLTextAreaElement, TextAreaProps>(
className={cx( className={cx(
"relative w-full", "relative w-full",
"invalid-within::ring-2 invalid-within::ring-red-600 invalid-within::ring-offset-2", "invalid-within::ring-2 invalid-within::ring-red-600 invalid-within::ring-offset-2",
"focus-within:border-slate-300 focus-within:outline-hidden focus-within:ring-1 focus-within:ring-blue-700 dark:focus-within:border-slate-600", "focus-within:border-slate-300 focus-within:ring-1 focus-within:ring-blue-700 focus-within:outline-hidden dark:focus-within:border-slate-600",
)} )}
> >
<textarea <textarea
@ -25,7 +25,7 @@ const TextArea = React.forwardRef<HTMLTextAreaElement, TextAreaProps>(
{...props} {...props}
id="asd" id="asd"
className={clsx( className={clsx(
"block w-full rounded-sm border-transparent bg-transparent text-black placeholder:text-slate-300 focus:ring-0 disabled:pointer-events-none disabled:select-none disabled:bg-slate-50 disabled:text-slate-300 dark:text-white dark:placeholder:text-slate-500 dark:disabled:bg-slate-800 sm:text-sm", "block w-full rounded-sm border-transparent bg-transparent text-black placeholder:text-slate-300 focus:ring-0 disabled:pointer-events-none disabled:bg-slate-50 disabled:text-slate-300 disabled:select-none sm:text-sm dark:text-white dark:placeholder:text-slate-500 dark:disabled:bg-slate-800",
props.className, props.className,
)} )}
/> />

View File

@ -10,13 +10,13 @@ export default function UpdateInProgressStatusCard() {
const { navigateTo } = useDeviceUiNavigation(); const { navigateTo } = useDeviceUiNavigation();
return ( return (
<div className="w-full select-none opacity-100 transition-all duration-300 ease-in-out"> <div className="w-full opacity-100 transition-all duration-300 ease-in-out select-none">
<GridCard cardClassName="shadow-xl!"> <GridCard cardClassName="shadow-xl!">
<div className="flex items-center justify-between gap-x-3 px-2.5 py-2.5 text-black dark:text-white"> <div className="flex items-center justify-between gap-x-3 px-2.5 py-2.5 text-black dark:text-white">
<div className="flex items-center gap-x-3"> <div className="flex items-center gap-x-3">
<LoadingSpinner className={cx("h-5 w-5", "shrink-0 text-blue-700")} /> <LoadingSpinner className={cx("h-5 w-5", "shrink-0 text-blue-700")} />
<div className="space-y-1"> <div className="space-y-1">
<div className="text-ellipsis text-sm font-semibold leading-none transition"> <div className="text-sm leading-none font-semibold text-ellipsis transition">
Update in Progress Update in Progress
</div> </div>
<div className="text-sm leading-none"> <div className="text-sm leading-none">

View File

@ -1,4 +1,4 @@
import { useCallback , useEffect, useState } from "react"; import { useCallback, useEffect, useState } from "react";
import { useJsonRpc } from "../hooks/useJsonRpc"; import { useJsonRpc } from "../hooks/useJsonRpc";
import notifications from "../notifications"; import notifications from "../notifications";
@ -174,7 +174,7 @@ export function UsbDeviceSetting() {
</SettingsItem> </SettingsItem>
{selectedPreset === "custom" && ( {selectedPreset === "custom" && (
<div className="ml-2 border-l border-slate-800/10 pl-4 dark:border-slate-300/20 "> <div className="ml-2 border-l border-slate-800/10 pl-4 dark:border-slate-300/20">
<div className="space-y-4"> <div className="space-y-4">
<div className="space-y-4"> <div className="space-y-4">
<SettingsItem title="Enable Keyboard" description="Enable Keyboard"> <SettingsItem title="Enable Keyboard" description="Enable Keyboard">

View File

@ -1,8 +1,7 @@
import { useMemo , useCallback , useEffect, useState } from "react"; import { useMemo, useCallback, useEffect, useState } from "react";
import { Button } from "@components/Button"; import { Button } from "@components/Button";
import { UsbConfigState } from "../hooks/stores"; import { UsbConfigState } from "../hooks/stores";
import { useJsonRpc } from "../hooks/useJsonRpc"; import { useJsonRpc } from "../hooks/useJsonRpc";
import notifications from "../notifications"; import notifications from "../notifications";
@ -174,7 +173,7 @@ export function UsbInfoSetting() {
/> />
</SettingsItem> </SettingsItem>
{usbConfigProduct === "custom" && ( {usbConfigProduct === "custom" && (
<div className="ml-2 space-y-4 border-l border-slate-800/10 pl-4 dark:border-slate-300/20 "> <div className="ml-2 space-y-4 border-l border-slate-800/10 pl-4 dark:border-slate-300/20">
<USBConfigDialog <USBConfigDialog
loading={loading} loading={loading}
onSetUsbConfig={usbConfig => handleUsbConfigChange(usbConfig)} onSetUsbConfig={usbConfig => handleUsbConfigChange(usbConfig)}

View File

@ -369,7 +369,7 @@ export function PointerLockBar({ show }: PointerLockBarProps) {
<AnimatePresence mode="wait"> <AnimatePresence mode="wait">
{show ? ( {show ? (
<motion.div <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 }} initial={{ y: 20, opacity: 0, zIndex: 0 }}
animate={{ opacity: 1, y: 0 }} animate={{ opacity: 1, y: 0 }}
exit={{ y: 43, zIndex: 0 }} exit={{ y: 43, zIndex: 0 }}

View File

@ -210,7 +210,7 @@ function KeyboardWrapper() {
<div <div
className={cx( className={cx(
!showAttachedVirtualKeyboard !showAttachedVirtualKeyboard
? "fixed left-0 top-0 z-50 select-none" ? "fixed top-0 left-0 z-50 select-none"
: "relative", : "relative",
)} )}
ref={keyboardRef} ref={keyboardRef}
@ -244,7 +244,7 @@ function KeyboardWrapper() {
/> />
)} )}
</div> </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 Virtual Keyboard
</h2> </h2>
<div className="absolute right-2"> <div className="absolute right-2">
@ -304,8 +304,16 @@ function KeyboardWrapper() {
onKeyPress={onKeyDown} onKeyPress={onKeyDown}
display={keyDisplayMap} display={keyDisplayMap}
layout={{ layout={{
default: ["PrintScreen ScrollLock Pause", "Insert Home Pageup", "Delete End Pagedown"], default: [
shift: ["(PrintScreen) ScrollLock (Pause)", "Insert Home Pageup", "Delete End Pagedown"], "PrintScreen ScrollLock Pause",
"Insert Home Pageup",
"Delete End Pagedown",
],
shift: [
"(PrintScreen) ScrollLock (Pause)",
"Insert Home Pageup",
"Delete End Pagedown",
],
}} }}
syncInstanceInputs={true} syncInstanceInputs={true}
debug={false} debug={false}

View File

@ -704,7 +704,7 @@ export default function WebRTCVideo() {
{peerConnection?.connectionState == "connected" && ( {peerConnection?.connectionState == "connected" && (
<div <div
style={{ animationDuration: "500ms" }} style={{ animationDuration: "500ms" }}
className="animate-slideUpFade pointer-events-none absolute inset-0 flex items-center justify-center" className="pointer-events-none absolute inset-0 flex animate-slideUpFade items-center justify-center"
> >
<div className="relative h-full w-full rounded-md"> <div className="relative h-full w-full rounded-md">
<LoadingVideoOverlay show={isVideoLoading} /> <LoadingVideoOverlay show={isVideoLoading} />

View File

@ -92,7 +92,7 @@ export default function ExtensionPopover() {
{renderActiveExtension()} {renderActiveExtension()}
<div <div
className="flex animate-fadeIn opacity-0 items-center justify-end space-x-2" className="flex animate-fadeIn items-center justify-end space-x-2 opacity-0"
style={{ style={{
animationDuration: "0.7s", animationDuration: "0.7s",
animationDelay: "0.2s", animationDelay: "0.2s",
@ -113,7 +113,7 @@ export default function ExtensionPopover() {
title="Extensions" title="Extensions"
description="Load and manage your 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"> <div className="w-full divide-y divide-slate-700/30 dark:divide-slate-600/30">
{AVAILABLE_EXTENSIONS.map(extension => ( {AVAILABLE_EXTENSIONS.map(extension => (
<div <div
@ -121,7 +121,7 @@ export default function ExtensionPopover() {
className="flex items-center justify-between p-3" className="flex items-center justify-between p-3"
> >
<div className="space-y-0.5"> <div className="space-y-0.5">
<p className="text-sm font-semibold leading-none text-slate-900 dark:text-slate-100"> <p className="text-sm leading-none font-semibold text-slate-900 dark:text-slate-100">
{extension.name} {extension.name}
</p> </p>
<p className="text-sm text-slate-600 dark:text-slate-400"> <p className="text-sm text-slate-600 dark:text-slate-400">

View File

@ -80,7 +80,7 @@ const MountPopopover = forwardRef<HTMLDivElement, object>((_props, ref) => {
</Card> </Card>
</div> </div>
<div className="space-y-1"> <div className="space-y-1">
<h3 className="text-sm font-semibold leading-none text-black dark:text-white"> <h3 className="text-sm leading-none font-semibold text-black dark:text-white">
No mounted media No mounted media
</h3> </h3>
<p className="text-xs leading-none text-slate-700 dark:text-slate-300"> <p className="text-xs leading-none text-slate-700 dark:text-slate-300">
@ -214,7 +214,7 @@ const MountPopopover = forwardRef<HTMLDivElement, object>((_props, ref) => {
) : null} ) : null}
<div <div
className="animate-fadeIn opacity-0 space-y-2" className="animate-fadeIn space-y-2 opacity-0"
style={{ style={{
animationDuration: "0.7s", animationDuration: "0.7s",
animationDelay: "0.1s", animationDelay: "0.1s",
@ -232,8 +232,8 @@ const MountPopopover = forwardRef<HTMLDivElement, object>((_props, ref) => {
</div> </div>
</div> </div>
{remoteVirtualMediaState ? ( {remoteVirtualMediaState ? (
<div className="flex select-none items-center justify-between text-xs"> <div className="flex items-center justify-between text-xs select-none">
<div className="select-none text-white dark:text-slate-300"> <div className="text-white select-none dark:text-slate-300">
<span>Mounted as</span>{" "} <span>Mounted as</span>{" "}
<span className="font-semibold"> <span className="font-semibold">
{remoteVirtualMediaState.mode === "Disk" ? "Disk" : "CD-ROM"} {remoteVirtualMediaState.mode === "Disk" ? "Disk" : "CD-ROM"}
@ -289,7 +289,7 @@ const MountPopopover = forwardRef<HTMLDivElement, object>((_props, ref) => {
{!remoteVirtualMediaState && ( {!remoteVirtualMediaState && (
<div <div
className="flex animate-fadeIn opacity-0 items-center justify-end space-x-2" className="flex animate-fadeIn items-center justify-end space-x-2 opacity-0"
style={{ style={{
animationDuration: "0.7s", animationDuration: "0.7s",
animationDelay: "0.2s", animationDelay: "0.2s",

View File

@ -83,7 +83,7 @@ export default function PasteModal() {
/> />
<div <div
className="animate-fadeIn opacity-0 space-y-2" className="animate-fadeIn space-y-2 opacity-0"
style={{ style={{
animationDuration: "0.7s", animationDuration: "0.7s",
animationDelay: "0.1s", animationDelay: "0.1s",
@ -137,7 +137,7 @@ export default function PasteModal() {
</div> </div>
</div> </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={{ style={{
animationDuration: "0.7s", animationDuration: "0.7s",
animationDelay: "0.2s", animationDelay: "0.2s",

View File

@ -26,7 +26,7 @@ export default function AddDeviceForm({
return ( return (
<div className="space-y-4"> <div className="space-y-4">
<div <div
className="animate-fadeIn opacity-0 space-y-4" className="animate-fadeIn space-y-4 opacity-0"
style={{ style={{
animationDuration: "0.5s", animationDuration: "0.5s",
animationFillMode: "forwards", animationFillMode: "forwards",
@ -73,7 +73,7 @@ export default function AddDeviceForm({
/> />
</div> </div>
<div <div
className="flex animate-fadeIn opacity-0 items-center justify-end space-x-2" className="flex animate-fadeIn items-center justify-end space-x-2 opacity-0"
style={{ style={{
animationDuration: "0.7s", animationDuration: "0.7s",
animationDelay: "0.2s", animationDelay: "0.2s",

View File

@ -33,7 +33,7 @@ export default function DeviceList({
{storedDevices.map((device, index) => ( {storedDevices.map((device, index) => (
<div key={index} className="flex items-center justify-between gap-x-2 p-3"> <div key={index} className="flex items-center justify-between gap-x-2 p-3">
<div className="space-y-0.5"> <div className="space-y-0.5">
<p className="text-sm font-semibold leading-none text-slate-900 dark:text-slate-100"> <p className="text-sm leading-none font-semibold text-slate-900 dark:text-slate-100">
{device?.name} {device?.name}
</p> </p>
<p className="text-sm text-slate-600 dark:text-slate-400"> <p className="text-sm text-slate-600 dark:text-slate-400">
@ -63,7 +63,7 @@ export default function DeviceList({
</div> </div>
</Card> </Card>
<div <div
className="flex animate-fadeIn opacity-0 items-center justify-end space-x-2" className="flex animate-fadeIn items-center justify-end space-x-2 opacity-0"
style={{ style={{
animationDuration: "0.7s", animationDuration: "0.7s",
animationDelay: "0.2s", animationDelay: "0.2s",

View File

@ -12,7 +12,7 @@ export default function EmptyStateCard({
setShowAddForm: (show: boolean) => void; setShowAddForm: (show: boolean) => void;
}) { }) {
return ( return (
<div className="select-none space-y-4"> <div className="space-y-4 select-none">
<Card className="animate-fadeIn opacity-0"> <Card className="animate-fadeIn opacity-0">
<div className="flex items-center justify-center py-8 text-center"> <div className="flex items-center justify-center py-8 text-center">
<div className="space-y-3"> <div className="space-y-3">
@ -24,7 +24,7 @@ export default function EmptyStateCard({
</div> </div>
</Card> </Card>
</div> </div>
<h3 className="text-sm font-semibold leading-none text-black dark:text-white"> <h3 className="text-sm leading-none font-semibold text-black dark:text-white">
No devices added No devices added
</h3> </h3>
<p className="text-xs leading-none text-slate-700 dark:text-slate-300"> <p className="text-xs leading-none text-slate-700 dark:text-slate-300">
@ -35,7 +35,7 @@ export default function EmptyStateCard({
</div> </div>
</Card> </Card>
<div <div
className="flex animate-fadeIn opacity-0 items-center justify-end space-x-2" className="flex animate-fadeIn items-center justify-end space-x-2 opacity-0"
style={{ style={{
animationDuration: "0.7s", animationDuration: "0.7s",
animationDelay: "0.2s", animationDelay: "0.2s",

View File

@ -29,10 +29,13 @@ export default function useKeyboard() {
sendKeyboardEvent([], []); sendKeyboardEvent([], []);
}, [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()) { for (const [index, step] of steps.entries()) {
const keyValues = step.keys?.map(key => keys[key]).filter(Boolean) || []; 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 the step has keys and/or modifiers, press them and hold for the delay
if (keyValues.length > 0 || modifierValues.length > 0) { if (keyValues.length > 0 || modifierValues.length > 0) {

View File

@ -207,7 +207,7 @@ export const chars = {
Tab: { key: "Tab", shift: false }, Tab: { key: "Tab", shift: false },
PrintScreen: { key: "Prt Sc", shift: false }, PrintScreen: { key: "Prt Sc", shift: false },
SystemRequest: { key: "Prt Sc", shift: true }, SystemRequest: { key: "Prt Sc", shift: true },
ScrollLock: { key: "ScrollLock", shift: false}, ScrollLock: { key: "ScrollLock", shift: false },
Pause: { key: "Pause", shift: false }, Pause: { key: "Pause", shift: false },
Break: { key: "Pause", shift: true }, Break: { key: "Pause", shift: true },
Insert: { key: "Insert", shift: false }, Insert: { key: "Insert", shift: false },
@ -266,28 +266,84 @@ export const keyDisplayMap: Record<string, string> = {
ArrowDown: "↓", ArrowDown: "↓",
// Letters // Letters
KeyA: "a", KeyB: "b", KeyC: "c", KeyD: "d", KeyE: "e", KeyA: "a",
KeyF: "f", KeyG: "g", KeyH: "h", KeyI: "i", KeyJ: "j", KeyB: "b",
KeyK: "k", KeyL: "l", KeyM: "m", KeyN: "n", KeyO: "o", KeyC: "c",
KeyP: "p", KeyQ: "q", KeyR: "r", KeyS: "s", KeyT: "t", KeyD: "d",
KeyU: "u", KeyV: "v", KeyW: "w", KeyX: "x", KeyY: "y", KeyE: "e",
KeyF: "f",
KeyG: "g",
KeyH: "h",
KeyI: "i",
KeyJ: "j",
KeyK: "k",
KeyL: "l",
KeyM: "m",
KeyN: "n",
KeyO: "o",
KeyP: "p",
KeyQ: "q",
KeyR: "r",
KeyS: "s",
KeyT: "t",
KeyU: "u",
KeyV: "v",
KeyW: "w",
KeyX: "x",
KeyY: "y",
KeyZ: "z", KeyZ: "z",
// Capital letters // Capital letters
"(KeyA)": "A", "(KeyB)": "B", "(KeyC)": "C", "(KeyD)": "D", "(KeyE)": "E", "(KeyA)": "A",
"(KeyF)": "F", "(KeyG)": "G", "(KeyH)": "H", "(KeyI)": "I", "(KeyJ)": "J", "(KeyB)": "B",
"(KeyK)": "K", "(KeyL)": "L", "(KeyM)": "M", "(KeyN)": "N", "(KeyO)": "O", "(KeyC)": "C",
"(KeyP)": "P", "(KeyQ)": "Q", "(KeyR)": "R", "(KeyS)": "S", "(KeyT)": "T", "(KeyD)": "D",
"(KeyU)": "U", "(KeyV)": "V", "(KeyW)": "W", "(KeyX)": "X", "(KeyY)": "Y", "(KeyE)": "E",
"(KeyF)": "F",
"(KeyG)": "G",
"(KeyH)": "H",
"(KeyI)": "I",
"(KeyJ)": "J",
"(KeyK)": "K",
"(KeyL)": "L",
"(KeyM)": "M",
"(KeyN)": "N",
"(KeyO)": "O",
"(KeyP)": "P",
"(KeyQ)": "Q",
"(KeyR)": "R",
"(KeyS)": "S",
"(KeyT)": "T",
"(KeyU)": "U",
"(KeyV)": "V",
"(KeyW)": "W",
"(KeyX)": "X",
"(KeyY)": "Y",
"(KeyZ)": "Z", "(KeyZ)": "Z",
// Numbers // Numbers
Digit1: "1", Digit2: "2", Digit3: "3", Digit4: "4", Digit5: "5", Digit1: "1",
Digit6: "6", Digit7: "7", Digit8: "8", Digit9: "9", Digit0: "0", Digit2: "2",
Digit3: "3",
Digit4: "4",
Digit5: "5",
Digit6: "6",
Digit7: "7",
Digit8: "8",
Digit9: "9",
Digit0: "0",
// Shifted Numbers // Shifted Numbers
"(Digit1)": "!", "(Digit2)": "@", "(Digit3)": "#", "(Digit4)": "$", "(Digit5)": "%", "(Digit1)": "!",
"(Digit6)": "^", "(Digit7)": "&", "(Digit8)": "*", "(Digit9)": "(", "(Digit0)": ")", "(Digit2)": "@",
"(Digit3)": "#",
"(Digit4)": "$",
"(Digit5)": "%",
"(Digit6)": "^",
"(Digit7)": "&",
"(Digit8)": "*",
"(Digit9)": "(",
"(Digit0)": ")",
// Symbols // Symbols
Minus: "-", Minus: "-",
@ -295,7 +351,7 @@ export const keyDisplayMap: Record<string, string> = {
Equal: "=", Equal: "=",
"(Equal)": "+", "(Equal)": "+",
BracketLeft: "[", BracketLeft: "[",
"(BracketLeft)": "{", "(BracketLeft)": "{",
BracketRight: "]", BracketRight: "]",
"(BracketRight)": "}", "(BracketRight)": "}",
Backslash: "\\", Backslash: "\\",
@ -303,7 +359,7 @@ export const keyDisplayMap: Record<string, string> = {
Semicolon: ";", Semicolon: ";",
"(Semicolon)": ":", "(Semicolon)": ":",
Quote: "'", Quote: "'",
"(Quote)": "\"", "(Quote)": '"',
Comma: ",", Comma: ",",
"(Comma)": "<", "(Comma)": "<",
Period: ".", Period: ".",
@ -315,21 +371,45 @@ export const keyDisplayMap: Record<string, string> = {
IntlBackslash: "\\", IntlBackslash: "\\",
// Function keys // Function keys
F1: "F1", F2: "F2", F3: "F3", F4: "F4", F1: "F1",
F5: "F5", F6: "F6", F7: "F7", F8: "F8", F2: "F2",
F9: "F9", F10: "F10", F11: "F11", F12: "F12", F3: "F3",
F4: "F4",
F5: "F5",
F6: "F6",
F7: "F7",
F8: "F8",
F9: "F9",
F10: "F10",
F11: "F11",
F12: "F12",
// Numpad // Numpad
Numpad0: "Num 0", Numpad1: "Num 1", Numpad2: "Num 2", Numpad0: "Num 0",
Numpad3: "Num 3", Numpad4: "Num 4", Numpad5: "Num 5", Numpad1: "Num 1",
Numpad6: "Num 6", Numpad7: "Num 7", Numpad8: "Num 8", Numpad2: "Num 2",
Numpad9: "Num 9", NumpadAdd: "Num +", NumpadSubtract: "Num -", Numpad3: "Num 3",
NumpadMultiply: "Num *", NumpadDivide: "Num /", NumpadDecimal: "Num .", Numpad4: "Num 4",
NumpadEqual: "Num =", NumpadEnter: "Num Enter", 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", NumLock: "Num Lock",
// Modals // Modals
PrintScreen: "prt sc", ScrollLock: "scr lk", Pause: "pause", PrintScreen: "prt sc",
"(PrintScreen)": "sys rq", "(Pause)": "break", ScrollLock: "scr lk",
SystemRequest: "sys rq", Break: "break" Pause: "pause",
"(PrintScreen)": "sys rq",
"(Pause)": "break",
SystemRequest: "sys rq",
Break: "break",
}; };

View File

@ -344,7 +344,8 @@ if (isOnDevice) {
{ {
path: "devices", path: "devices",
element: <DevicesRoute />, element: <DevicesRoute />,
loader: DevicesRoute.loader }, loader: DevicesRoute.loader,
},
], ],
}, },
], ],

View File

@ -4,7 +4,6 @@ import { CheckCircleIcon, XCircleIcon } from "@heroicons/react/20/solid";
import Card from "@/components/Card"; import Card from "@/components/Card";
interface NotificationOptions { interface NotificationOptions {
duration?: number; duration?: number;
// Add other options as needed // Add other options as needed
@ -26,7 +25,9 @@ const ToastContent = ({
> >
<div className="flex items-center gap-x-2 p-2.5 px-2"> <div className="flex items-center gap-x-2 p-2.5 px-2">
{icon} {icon}
<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> </div>
</Card> </Card>
); );
@ -36,7 +37,9 @@ const notifications = {
return toast.custom( return toast.custom(
t => ( t => (
<ToastContent <ToastContent
icon={<CheckCircleIcon className="w-5 h-5 text-green-500 dark:text-green-400" />} icon={
<CheckCircleIcon className="h-5 w-5 text-green-500 dark:text-green-400" />
}
message={message} message={message}
t={t} t={t}
/> />
@ -49,7 +52,7 @@ const notifications = {
return toast.custom( return toast.custom(
t => ( t => (
<ToastContent <ToastContent
icon={<XCircleIcon className="w-5 h-5 text-red-500 dark:text-red-400" />} icon={<XCircleIcon className="h-5 w-5 text-red-500 dark:text-red-400" />}
message={message} message={message}
t={t} t={t}
/> />

View File

@ -80,9 +80,9 @@ export default function DevicesIdDeregister() {
kvmName={device?.name} kvmName={device?.name}
/> />
<div className="w-full h-full"> <div className="h-full w-full">
<div className="mt-4"> <div className="mt-4">
<div className="w-full h-full px-4 mx-auto space-y-6 sm:max-w-6xl sm:px-8 md:max-w-7xl md:px-12 lg:max-w-8xl"> <div className="mx-auto h-full w-full space-y-6 px-4 sm:max-w-6xl sm:px-8 md:max-w-7xl md:px-12 lg:max-w-8xl">
<div className="space-y-4"> <div className="space-y-4">
<LinkButton <LinkButton
size="SM" size="SM"

View File

@ -1364,7 +1364,7 @@ function UploadFileView({
{/* Display upload error if present */} {/* Display upload error if present */}
{uploadError && ( {uploadError && (
<div <div
className="mt-2 animate-fadeIn truncate text-sm text-red-600 dark:text-red-400 opacity-0" className="mt-2 animate-fadeIn truncate text-sm text-red-600 opacity-0 dark:text-red-400"
style={{ animationDuration: "0.7s" }} style={{ animationDuration: "0.7s" }}
> >
Error: {uploadError} Error: {uploadError}

View File

@ -1,4 +1,3 @@
import { useCallback, useState, useEffect } from "react"; import { useCallback, useState, useEffect } from "react";
import { GridCard } from "@components/Card"; import { GridCard } from "@components/Card";
@ -12,7 +11,6 @@ import { isOnDevice } from "../main";
import { Button } from "../components/Button"; import { Button } from "../components/Button";
import { useSettingsStore } from "../hooks/stores"; import { useSettingsStore } from "../hooks/stores";
import { SettingsItem } from "./devices.$id.settings"; import { SettingsItem } from "./devices.$id.settings";
export default function SettingsAdvancedRoute() { export default function SettingsAdvancedRoute() {
@ -153,7 +151,7 @@ export default function SettingsAdvancedRoute() {
{settings.developerMode && ( {settings.developerMode && (
<GridCard> <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 <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24" viewBox="0 0 24 24"

View File

@ -1,5 +1,4 @@
import { useState, useEffect } from "react";
import { useState , useEffect } from "react";
import { useJsonRpc } from "@/hooks/useJsonRpc"; import { useJsonRpc } from "@/hooks/useJsonRpc";

View File

@ -242,9 +242,9 @@ function UpdatingDeviceState({
console.log( console.log(
`For ${type}:\n` + `For ${type}:\n` +
` Download Progress: ${downloadProgress}% (${otaState[`${type}DownloadProgress`]})\n` + ` Download Progress: ${downloadProgress}% (${otaState[`${type}DownloadProgress`]})\n` +
` Update Progress: ${updateProgress}% (${otaState[`${type}UpdateProgress`]})\n` + ` Update Progress: ${updateProgress}% (${otaState[`${type}UpdateProgress`]})\n` +
` Verification Progress: ${verificationProgress}% (${otaState[`${type}VerificationProgress`]})`, ` Verification Progress: ${verificationProgress}% (${otaState[`${type}VerificationProgress`]})`,
); );
if (type === "app") { if (type === "app") {

View File

@ -23,15 +23,19 @@ export default function SettingsHardwareRoute() {
}; };
const handleDisplayRotationSave = () => { const handleDisplayRotationSave = () => {
send("setDisplayRotation", { params: { rotation: settings.displayRotation } }, resp => { send(
if ("error" in resp) { "setDisplayRotation",
notifications.error( { params: { rotation: settings.displayRotation } },
`Failed to set display orientation: ${resp.error.data || "Unknown error"}`, resp => {
); if ("error" in resp) {
return; notifications.error(
} `Failed to set display orientation: ${resp.error.data || "Unknown error"}`,
notifications.success("Display orientation updated successfully"); );
}); return;
}
notifications.success("Display orientation updated successfully");
},
);
}; };
const setBacklightSettings = useSettingsStore(state => state.setBacklightSettings); const setBacklightSettings = useSettingsStore(state => state.setBacklightSettings);

View File

@ -34,8 +34,8 @@ export default function SettingsMacrosEditRoute() {
...step, ...step,
keys: Array.isArray(step.keys) ? step.keys : [], keys: Array.isArray(step.keys) ? step.keys : [],
modifiers: Array.isArray(step.modifiers) ? step.modifiers : [], modifiers: Array.isArray(step.modifiers) ? step.modifiers : [],
delay: typeof step.delay === 'number' ? step.delay : 0 delay: typeof step.delay === "number" ? step.delay : 0,
})) })),
}); });
} else { } else {
navigate("../"); navigate("../");
@ -48,11 +48,13 @@ export default function SettingsMacrosEditRoute() {
setIsUpdating(true); setIsUpdating(true);
try { try {
const newMacros = macros.map(m => const newMacros = macros.map(m =>
m.id === macro.id ? { m.id === macro.id
...macro, ? {
name: updatedMacro.name!.trim(), ...macro,
steps: updatedMacro.steps || [], name: updatedMacro.name!.trim(),
} : m steps: updatedMacro.steps || [],
}
: m,
); );
await saveMacros(normalizeSortOrders(newMacros)); await saveMacros(normalizeSortOrders(newMacros));
@ -94,10 +96,7 @@ export default function SettingsMacrosEditRoute() {
return ( return (
<div className="space-y-4"> <div className="space-y-4">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<SettingsPageHeader <SettingsPageHeader title="Edit Macro" description="Modify your keyboard macro" />
title="Edit Macro"
description="Modify your keyboard macro"
/>
<Button <Button
size="SM" size="SM"
theme="light" theme="light"

View File

@ -22,7 +22,6 @@ import useKeyboard from "@/hooks/useKeyboard";
import { cx } from "../cva.config"; 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. */ /* 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() { export default function SettingsRoute() {
const location = useLocation(); const location = useLocation();
@ -31,7 +30,9 @@ export default function SettingsRoute() {
const scrollContainerRef = useRef<HTMLDivElement>(null); const scrollContainerRef = useRef<HTMLDivElement>(null);
const [showLeftGradient, setShowLeftGradient] = useState(false); const [showLeftGradient, setShowLeftGradient] = useState(false);
const [showRightGradient, setShowRightGradient] = 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 // Handle scroll position to show/hide gradients
const handleScroll = () => { const handleScroll = () => {
@ -82,8 +83,8 @@ export default function SettingsRoute() {
return ( return (
<div className="pointer-events-auto relative mx-auto max-w-4xl translate-x-0 transform text-left dark:text-white"> <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="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 space-y-4 gap-x-8 gap-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 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"> <Card className="flex w-full gap-x-4 overflow-hidden p-2 md:flex-col dark:bg-slate-800">
<div className="md:hidden"> <div className="md:hidden">
<LinkButton <LinkButton
@ -130,14 +131,14 @@ export default function SettingsRoute() {
></div> ></div>
<div <div
ref={scrollContainerRef} 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"> <div className="shrink-0">
<NavLink <NavLink
to="general" to="general"
className={({ isActive }) => (isActive ? "active" : "")} 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" /> <LuSettings className="h-4 w-4 shrink-0" />
<h1>General</h1> <h1>General</h1>
</div> </div>
@ -148,7 +149,7 @@ export default function SettingsRoute() {
to="mouse" to="mouse"
className={({ isActive }) => (isActive ? "active" : "")} 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" /> <LuKeyboard className="h-4 w-4 shrink-0" />
<h1>Mouse</h1> <h1>Mouse</h1>
</div> </div>
@ -159,7 +160,7 @@ export default function SettingsRoute() {
to="video" to="video"
className={({ isActive }) => (isActive ? "active" : "")} 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" /> <LuVideo className="h-4 w-4 shrink-0" />
<h1>Video</h1> <h1>Video</h1>
</div> </div>
@ -170,7 +171,7 @@ export default function SettingsRoute() {
to="hardware" to="hardware"
className={({ isActive }) => (isActive ? "active" : "")} 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" /> <LuCpu className="h-4 w-4 shrink-0" />
<h1>Hardware</h1> <h1>Hardware</h1>
</div> </div>
@ -181,7 +182,7 @@ export default function SettingsRoute() {
to="access" to="access"
className={({ isActive }) => (isActive ? "active" : "")} 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" /> <LuShieldCheck className="h-4 w-4 shrink-0" />
<h1>Access</h1> <h1>Access</h1>
</div> </div>
@ -192,7 +193,7 @@ export default function SettingsRoute() {
to="appearance" to="appearance"
className={({ isActive }) => (isActive ? "active" : "")} 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" /> <LuPalette className="h-4 w-4 shrink-0" />
<h1>Appearance</h1> <h1>Appearance</h1>
</div> </div>
@ -203,7 +204,7 @@ export default function SettingsRoute() {
to="macros" to="macros"
className={({ isActive }) => (isActive ? "active" : "")} 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" /> <LuCommand className="h-4 w-4 shrink-0" />
<h1>Keyboard Macros</h1> <h1>Keyboard Macros</h1>
</div> </div>
@ -214,7 +215,7 @@ export default function SettingsRoute() {
to="network" to="network"
className={({ isActive }) => (isActive ? "active" : "")} 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" /> <LuNetwork className="h-4 w-4 shrink-0" />
<h1>Network</h1> <h1>Network</h1>
</div> </div>
@ -225,7 +226,7 @@ export default function SettingsRoute() {
to="advanced" to="advanced"
className={({ isActive }) => (isActive ? "active" : "")} 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" /> <LuWrench className="h-4 w-4 shrink-0" />
<h1>Advanced</h1> <h1>Advanced</h1>
</div> </div>
@ -272,7 +273,7 @@ export function SettingsItem({
return ( return (
<label <label
className={cx( 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, className,
)} )}
> >
@ -281,7 +282,7 @@ export function SettingsItem({
<div className="flex items-center text-base font-semibold text-black dark:text-white"> <div className="flex items-center text-base font-semibold text-black dark:text-white">
{title} {title}
{badge && ( {badge && (
<span className="ml-2 rounded-full bg-red-500 px-2 py-1 text-[10px] font-medium leading-none text-white dark:border dark:border-red-700 dark:bg-red-800 dark:text-red-50"> <span className="ml-2 rounded-full bg-red-500 px-2 py-1 text-[10px] leading-none font-medium text-white dark:border dark:border-red-700 dark:bg-red-800 dark:text-red-50">
{badge} {badge}
</span> </span>
)} )}

View File

@ -84,7 +84,9 @@ export default function SettingsVideoRoute() {
return; 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); setStreamQuality(factor);
}); });
}; };

View File

@ -59,14 +59,16 @@ export default function SetupRoute() {
<div className="grid min-h-screen grid-rows-(--grid-layout)"> <div className="grid min-h-screen grid-rows-(--grid-layout)">
<SimpleNavbar /> <SimpleNavbar />
<Container> <Container>
<div className="flex items-center justify-center w-full h-full isolate"> <div className="isolate flex h-full w-full items-center justify-center">
<div className="max-w-2xl -mt-32 space-y-8"> <div className="-mt-32 max-w-2xl space-y-8">
<div className="text-center"> <div className="text-center">
<StepCounter currStepIdx={1} nSteps={2} /> <StepCounter currStepIdx={1} nSteps={2} />
</div> </div>
<div className="space-y-2 text-center"> <div className="space-y-2 text-center">
<h1 className="text-4xl font-semibold text-black dark:text-white">Let&apos;s name your device</h1> <h1 className="text-4xl font-semibold text-black dark:text-white">
Let&apos;s name your device
</h1>
<p className="text-slate-600 dark:text-slate-400"> <p className="text-slate-600 dark:text-slate-400">
Name your device so you can easily identify it later. You can change Name your device so you can easily identify it later. You can change
this name at any time. this name at any time.
@ -74,7 +76,7 @@ export default function SetupRoute() {
</div> </div>
<Fieldset className="space-y-12"> <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 <InputFieldWithLabel
label="Device Name" label="Device Name"
type="text" type="text"

View File

@ -704,7 +704,7 @@ export default function KvmIdRoute() {
send("getUpdateStatus", {}, async resp => { send("getUpdateStatus", {}, async resp => {
if ("error" in resp) { if ("error" in resp) {
notifications.error(`Failed to get device version: ${resp.error}`); notifications.error(`Failed to get device version: ${resp.error}`);
return return;
} }
const result = resp.result as SystemVersionInfo; const result = resp.result as SystemVersionInfo;
@ -797,7 +797,7 @@ export default function KvmIdRoute() {
<WebRTCVideo /> <WebRTCVideo />
<div <div
style={{ animationDuration: "500ms" }} 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"> <div className="relative h-full max-h-[720px] w-full max-w-[1280px] rounded-md">
{!!ConnectionStatusElement && ConnectionStatusElement} {!!ConnectionStatusElement && ConnectionStatusElement}

View File

@ -11,18 +11,20 @@ export default function DevicesAlreadyAdopted() {
<div className="grid min-h-screen grid-rows-(--grid-layout)"> <div className="grid min-h-screen grid-rows-(--grid-layout)">
<SimpleNavbar /> <SimpleNavbar />
<Container> <Container>
<div className="flex items-center justify-center w-full h-full isolate"> <div className="isolate flex h-full w-full items-center justify-center">
<div className="max-w-2xl -mt-16 space-y-8"> <div className="-mt-16 max-w-2xl space-y-8">
<div className="space-y-4 text-center"> <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"> <p className="text-lg text-slate-600 dark:text-slate-400">
This device is currently registered to another user in our cloud This device is currently registered to another user in our cloud
dashboard. dashboard.
</p> </p>
<p className="mt-4 text-lg text-slate-600 dark:text-slate-400"> <p className="mt-4 text-lg text-slate-600 dark:text-slate-400">
If you&apos;re the new owner, please ask the previous owner to de-register If you&apos;re the new owner, please ask the previous owner to
the device from their account in the cloud dashboard. If you believe de-register the device from their account in the cloud dashboard. If you
this is an error, contact our support team for assistance. believe this is an error, contact our support team for assistance.
</p> </p>
</div> </div>

View File

@ -40,7 +40,7 @@ export default function DevicesRoute() {
useInterval(revalidate.revalidate, 4000); useInterval(revalidate.revalidate, 4000);
return ( return (
<div className="relative h-full"> <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 <DashboardNavbar
isLoggedIn={!!user} isLoggedIn={!!user}
primaryLinks={[{ title: "Cloud Devices", to: "/devices" }]} primaryLinks={[{ title: "Cloud Devices", to: "/devices" }]}

View File

@ -60,7 +60,7 @@ export default function WelcomeLocalModeRoute() {
<Container> <Container>
<div className="isolate flex h-full w-full items-center justify-center"> <div className="isolate flex h-full w-full items-center justify-center">
<div className="max-w-xl space-y-8"> <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 <img
src={LogoWhiteIcon} src={LogoWhiteIcon}
alt="" alt=""
@ -83,14 +83,14 @@ export default function WelcomeLocalModeRoute() {
<Form method="POST" className="space-y-8"> <Form method="POST" className="space-y-8">
<div <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" }} style={{ animationDelay: "400ms" }}
> >
{["password", "noPassword"].map(mode => ( {["password", "noPassword"].map(mode => (
<GridCard <GridCard
key={mode} key={mode}
cardClassName={cx("transition-all duration-100", { 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, "hover:outline-blue-700!": selectedMode !== mode,
})} })}
> >
@ -116,7 +116,7 @@ export default function WelcomeLocalModeRoute() {
onChange={() => { onChange={() => {
setSelectedMode(mode as "password" | "noPassword"); 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> </div>
</GridCard> </GridCard>
@ -133,7 +133,7 @@ export default function WelcomeLocalModeRoute() {
)} )}
<div <div
className="animate-fadeIn mx-auto max-w-sm opacity-0" className="mx-auto max-w-sm animate-fadeIn opacity-0"
style={{ animationDelay: "500ms" }} style={{ animationDelay: "500ms" }}
> >
<Button <Button
@ -149,7 +149,7 @@ export default function WelcomeLocalModeRoute() {
</Form> </Form>
<p <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" }} style={{ animationDelay: "600ms" }}
> >
You can always change your authentication method later in the settings. You can always change your authentication method later in the settings.

View File

@ -71,7 +71,7 @@ export default function WelcomeLocalPasswordRoute() {
<Container> <Container>
<div className="isolate flex h-full w-full items-center justify-center"> <div className="isolate flex h-full w-full items-center justify-center">
<div className="max-w-2xl space-y-8"> <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 <img
src={LogoWhiteIcon} src={LogoWhiteIcon}
alt="" alt=""
@ -159,7 +159,7 @@ export default function WelcomeLocalPasswordRoute() {
</Fieldset> </Fieldset>
<p <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" }} style={{ animationDelay: "800ms" }}
> >
This password will be used to secure your device data and protect against This password will be used to secure your device data and protect against

View File

@ -45,7 +45,7 @@ export default function WelcomeRoute() {
<div className="max-w-3xl text-center"> <div className="max-w-3xl text-center">
<div className="space-y-8"> <div className="space-y-8">
<div className="space-y-4"> <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 <img
src={LogoWhiteIcon} src={LogoWhiteIcon}
alt="JetKVM Logo" alt="JetKVM Logo"
@ -58,7 +58,7 @@ export default function WelcomeRoute() {
/> />
</div> </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"> <h1 className="text-4xl font-semibold text-black dark:text-white">
Welcome to JetKVM Welcome to JetKVM
</h1> </h1>
@ -72,19 +72,19 @@ export default function WelcomeRoute() {
<img <img
src={DeviceImage} src={DeviceImage}
alt="JetKVM Device" 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> </div>
<div className="-mt-8 space-y-4"> <div className="-mt-8 space-y-4">
<p <p
style={{ animationDelay: "2000ms" }} 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 JetKVM combines powerful hardware with intuitive software to provide a
seamless remote control experience. seamless remote control experience.
</p> </p>
<div className="animate-fadeIn animation-delay-2300 opacity-0"> <div className="animate-fadeIn opacity-0 animation-delay-2300">
<LinkButton <LinkButton
size="LG" size="LG"
theme="light" theme="light"