diff --git a/.oxlintrc.json b/.oxlintrc.json
index 634c4b7c..0caeb87a 100644
--- a/.oxlintrc.json
+++ b/.oxlintrc.json
@@ -1,6 +1,6 @@
{
"$schema": "./node_modules/oxlint/configuration_schema.json",
- "plugins": ["unicorn", "typescript", "oxc"],
+ "plugins": ["eslint", "unicorn", "typescript", "oxc", "import", "react", "react-perf", "node", "jsx-a11y"],
"categories": {},
"rules": {
"constructor-super": "warn",
@@ -112,7 +112,8 @@
"unicorn/no-useless-length-check": "warn",
"unicorn/no-useless-spread": "warn",
"unicorn/prefer-set-size": "warn",
- "unicorn/prefer-string-starts-ends-with": "warn"
+ "unicorn/prefer-string-starts-ends-with": "warn",
+ "import/no-cycle": "error"
},
"settings": {
"jsx-a11y": {
diff --git a/app/client/components/file-tree.tsx b/app/client/components/file-tree.tsx
index fa9559c2..22d0a5c4 100644
--- a/app/client/components/file-tree.tsx
+++ b/app/client/components/file-tree.tsx
@@ -489,18 +489,27 @@ interface ButtonProps {
const NodeButton = memo(({ depth, icon, onClick, onMouseEnter, className, children }: ButtonProps) => {
const paddingLeft = useMemo(() => `${8 + depth * NODE_PADDING_LEFT}px`, [depth]);
+ const handleKeyDown = useCallback(
+ (e: React.KeyboardEvent) => {
+ if (e.key === "Enter" || e.key === " ") {
+ e.preventDefault();
+ onClick?.();
+ }
+ },
+ [onClick],
+ );
+
return (
- // biome-ignore lint/a11y/noStaticElementInteractions: we handle click and hover manually
- // biome-ignore lint/a11y/useKeyWithClickEvents: we handle click and hover manually
-
+
);
});
diff --git a/app/client/components/snapshots-table.tsx b/app/client/components/snapshots-table.tsx
index 2e3b7f79..9ff31b8f 100644
--- a/app/client/components/snapshots-table.tsx
+++ b/app/client/components/snapshots-table.tsx
@@ -158,7 +158,7 @@ export const SnapshotsTable = ({ snapshots, repositoryId, backups }: Props) => {
}
setSelectedIds(newSelected);
}}
- aria-label={`Select snapshot ${snapshot.short_id}`}
+ aria-label={`Select snapshot ${snapshot.short_id}` as string}
/>
diff --git a/app/client/components/ui/alert.tsx b/app/client/components/ui/alert.tsx
index 1014ae72..229491a7 100644
--- a/app/client/components/ui/alert.tsx
+++ b/app/client/components/ui/alert.tsx
@@ -27,9 +27,9 @@ const Alert = React.forwardRef<
));
Alert.displayName = "Alert";
-const AlertTitle = React.forwardRef>(
+const AlertTitle = React.forwardRef>(
({ className, ...props }, ref) => (
-
+ {props.children}
),
);
AlertTitle.displayName = "AlertTitle";
diff --git a/app/client/components/ui/breadcrumb.tsx b/app/client/components/ui/breadcrumb.tsx
index e1fb0843..f838da10 100644
--- a/app/client/components/ui/breadcrumb.tsx
+++ b/app/client/components/ui/breadcrumb.tsx
@@ -43,16 +43,17 @@ function BreadcrumbLink({
);
}
-function BreadcrumbPage({ className, ...props }: React.ComponentProps<"span">) {
+function BreadcrumbPage({ className, children, ...props }: React.ComponentProps<"a">) {
return (
-
+ >
+ {children}
+
);
}
diff --git a/app/client/modules/auth/routes/download-recovery-key.tsx b/app/client/modules/auth/routes/download-recovery-key.tsx
index af29dd05..da20ce32 100644
--- a/app/client/modules/auth/routes/download-recovery-key.tsx
+++ b/app/client/modules/auth/routes/download-recovery-key.tsx
@@ -88,7 +88,6 @@ export default function DownloadRecoveryKeyPage() {
onChange={(e) => setPassword(e.target.value)}
placeholder="Enter your password"
required
- autoFocus
disabled={downloadResticPassword.isPending}
/>
Enter your account password to download the recovery key
diff --git a/app/client/modules/auth/routes/login.tsx b/app/client/modules/auth/routes/login.tsx
index f342ccd5..fb089eb6 100644
--- a/app/client/modules/auth/routes/login.tsx
+++ b/app/client/modules/auth/routes/login.tsx
@@ -84,7 +84,7 @@ export default function LoginPage() {
Username
-
+
diff --git a/app/client/modules/auth/routes/onboarding.tsx b/app/client/modules/auth/routes/onboarding.tsx
index f9e3f490..6290b598 100644
--- a/app/client/modules/auth/routes/onboarding.tsx
+++ b/app/client/modules/auth/routes/onboarding.tsx
@@ -115,7 +115,7 @@ export default function OnboardingPage() {
Username
-
+
Choose a username for the admin account
diff --git a/app/client/modules/backups/components/create-schedule-form.tsx b/app/client/modules/backups/components/create-schedule-form.tsx
index 8e2a088b..577fb064 100644
--- a/app/client/modules/backups/components/create-schedule-form.tsx
+++ b/app/client/modules/backups/components/create-schedule-form.tsx
@@ -393,7 +393,7 @@ export const CreateScheduleForm = ({ initialValues, formId, onSubmit, volume }:
type="button"
onClick={() => handleRemovePath(path)}
className="ml-1 hover:bg-destructive/20 rounded p-0.5 transition-colors"
- aria-label={`Remove ${path}`}
+ aria-label={`Remove ${path}` as string}
>
diff --git a/app/client/modules/backups/components/schedule-summary.tsx b/app/client/modules/backups/components/schedule-summary.tsx
index 2036ccfa..16f1df03 100644
--- a/app/client/modules/backups/components/schedule-summary.tsx
+++ b/app/client/modules/backups/components/schedule-summary.tsx
@@ -65,7 +65,7 @@ export const ScheduleSummary = (props: Props) => {
repositoryLabel: schedule.repositoryId || "No repository selected",
retentionLabel: retentionParts.length > 0 ? retentionParts.join(" • ") : "No retention policy",
};
- }, [schedule, schedule.volume.name]);
+ }, [schedule]);
const handleConfirmDelete = () => {
setShowDeleteConfirm(false);
diff --git a/app/client/modules/settings/routes/settings.tsx b/app/client/modules/settings/routes/settings.tsx
index a1a75225..70a2fe8a 100644
--- a/app/client/modules/settings/routes/settings.tsx
+++ b/app/client/modules/settings/routes/settings.tsx
@@ -244,7 +244,6 @@ export default function Settings({ loaderData }: Route.ComponentProps) {
onChange={(e) => setDownloadPassword(e.target.value)}
placeholder="Enter your password"
required
- autoFocus
/>