Skip to content

Commit f1a5365

Browse files
pinkroosteraiclaude
andcommitted
chore(frontend): fix eslint warnings across codebase
Fix ~100 lint warnings: prettier formatting, import ordering, CRLF line endings, jsx-a11y accessibility (keyboard handlers, aria-selected, label associations), react-hooks/exhaustive-deps (useMemo wrappers), and eslint-disable for intentional patterns (autoFocus, shadcn/ui variant exports). Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
1 parent 19016da commit f1a5365

52 files changed

Lines changed: 2514 additions & 2460 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

src/frontend/src/App.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
/* eslint-disable import/order -- @/ alias misclassified as external by eslint-plugin-import */
12
import { QueryClientProvider } from '@tanstack/react-query';
23
import { lazy, Suspense, useEffect, useState } from 'react';
34
import { BrowserRouter, Routes, Route, Navigate, useLocation } from 'react-router-dom';

src/frontend/src/components/editor/EditorAiOverlay.tsx

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import { AnimatePresence, motion } from 'framer-motion';
22
import { useEffect, useRef, useState } from 'react';
33

4-
import { AiLoadingAnimation } from '@/components/wizard/AiLoadingAnimation';
54
import { Button } from '@/components/ui/button';
5+
import { AiLoadingAnimation } from '@/components/wizard/AiLoadingAnimation';
66

77
interface EditorAiOverlayProps {
88
isVisible: boolean;
@@ -43,12 +43,7 @@ export function EditorAiOverlay({ isVisible, label, onCancel }: EditorAiOverlayP
4343
<p className="text-sm font-medium text-foreground-muted">{label}</p>
4444
<span className="text-xs text-foreground-muted tabular-nums">{elapsed}s</span>
4545
{onCancel && (
46-
<Button
47-
variant="outline"
48-
size="sm"
49-
onClick={onCancel}
50-
className="mt-2"
51-
>
46+
<Button variant="outline" size="sm" onClick={onCancel} className="mt-2">
5247
Cancel
5348
</Button>
5449
)}

src/frontend/src/components/editor/MarkdownEditor.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,12 +210,17 @@ export function MarkdownEditor({
210210
return (
211211
<div
212212
ref={containerRef}
213+
role="textbox"
214+
tabIndex={0}
213215
className={cn(
214216
'relative rounded-md border border-border bg-elevated focus-within:ring-2 focus-within:ring-primary/30 transition-shadow',
215217
minHeightClass,
216218
className
217219
)}
218220
onClick={handleEditorClick}
221+
onKeyDown={(e) => {
222+
if (e.key === 'Enter' || e.key === ' ') handleEditorClick();
223+
}}
219224
>
220225
{templateHighlight && editable && (
221226
<div className="flex justify-end px-2 pt-1.5">

src/frontend/src/components/editor/ShareDialog.tsx

Lines changed: 23 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,16 @@
1-
import { useState, useCallback } from 'react';
21
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
3-
import { toast } from 'sonner';
42
import { Copy, Trash2, RefreshCw, Link, Eye, EyeOff } from 'lucide-react';
5-
import {
6-
Dialog,
7-
DialogContent,
8-
DialogHeader,
9-
DialogTitle,
10-
} from '@/components/ui/dialog';
3+
import { useState, useCallback } from 'react';
4+
import { toast } from 'sonner';
5+
116
import { Button } from '@/components/ui/button';
7+
import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog';
128
import { Input } from '@/components/ui/input';
139
import { Label } from '@/components/ui/label';
1410
import { Switch } from '@/components/ui/switch';
15-
import {
16-
createShareLink,
17-
getShareLink,
18-
revokeShareLink,
19-
} from '@/services/api/shareLinkService';
20-
import type { CreateShareLinkRequest } from '@/types/shareLink';
2111
import { ApiError } from '@/services/api/apiClient';
12+
import { createShareLink, getShareLink, revokeShareLink } from '@/services/api/shareLinkService';
13+
import type { CreateShareLinkRequest } from '@/types/shareLink';
2214

2315
interface ShareDialogProps {
2416
entryId: string;
@@ -76,17 +68,20 @@ export function ShareDialog({ entryId, open, onOpenChange }: ShareDialogProps) {
7668
createMutation.mutate(req);
7769
}, [useExpiry, expiresAt, usePassword, password, createMutation]);
7870

79-
const handleCopy = useCallback(async (token?: string) => {
80-
const t = token ?? createdToken;
81-
if (!t) return;
82-
const url = `${window.location.origin}/share/${encodeURIComponent(t)}`;
83-
try {
84-
await navigator.clipboard.writeText(url);
85-
toast.success('Link copied to clipboard');
86-
} catch {
87-
toast.error('Failed to copy to clipboard');
88-
}
89-
}, [createdToken]);
71+
const handleCopy = useCallback(
72+
async (token?: string) => {
73+
const t = token ?? createdToken;
74+
if (!t) return;
75+
const url = `${window.location.origin}/share/${encodeURIComponent(t)}`;
76+
try {
77+
await navigator.clipboard.writeText(url);
78+
toast.success('Link copied to clipboard');
79+
} catch {
80+
toast.error('Failed to copy to clipboard');
81+
}
82+
},
83+
[createdToken]
84+
);
9085

9186
const existingLink = linkQuery.data;
9287
const hasLink = !!existingLink && !linkQuery.error;
@@ -185,11 +180,7 @@ export function ShareDialog({ entryId, open, onOpenChange }: ShareDialogProps) {
185180
<div>
186181
<Label className="text-xs text-muted-foreground mb-1.5 block">Share URL</Label>
187182
<div className="flex gap-2">
188-
<Input
189-
readOnly
190-
value={shareUrl ?? ''}
191-
className="text-xs font-mono"
192-
/>
183+
<Input readOnly value={shareUrl ?? ''} className="text-xs font-mono" />
193184
<Button variant="outline" size="icon" onClick={() => handleCopy()}>
194185
<Copy className="h-4 w-4" />
195186
</Button>
@@ -221,11 +212,7 @@ export function ShareDialog({ entryId, open, onOpenChange }: ShareDialogProps) {
221212
<Label htmlFor="use-expiry" className="text-sm">
222213
Set expiration
223214
</Label>
224-
<Switch
225-
id="use-expiry"
226-
checked={useExpiry}
227-
onCheckedChange={setUseExpiry}
228-
/>
215+
<Switch id="use-expiry" checked={useExpiry} onCheckedChange={setUseExpiry} />
229216
</div>
230217
{useExpiry && (
231218
<Input
@@ -240,11 +227,7 @@ export function ShareDialog({ entryId, open, onOpenChange }: ShareDialogProps) {
240227
<Label htmlFor="use-password" className="text-sm">
241228
Password protect
242229
</Label>
243-
<Switch
244-
id="use-password"
245-
checked={usePassword}
246-
onCheckedChange={setUsePassword}
247-
/>
230+
<Switch id="use-password" checked={usePassword} onCheckedChange={setUsePassword} />
248231
</div>
249232
{usePassword && (
250233
<>

src/frontend/src/components/editor/SystemMessageSection.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ export function SystemMessageSection({
8383
editable={!isReadOnly}
8484
placeholder="Enter a system message to set the AI's behavior..."
8585
minHeightClass="min-h-[80px]"
86-
autoFocus={shouldAutoFocus}
86+
autoFocus={shouldAutoFocus} // eslint-disable-line jsx-a11y/no-autofocus
8787
/>
8888
</CollapsibleContent>
8989
</Collapsible>

src/frontend/src/components/editor/VersionDiffDialog.tsx

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { toast } from 'sonner';
55

66
import { DiffBlock } from './DiffBlock';
77

8+
import { LoadingSpinner } from '@/components/common/LoadingSpinner';
89
import {
910
AlertDialog,
1011
AlertDialogAction,
@@ -15,7 +16,6 @@ import {
1516
AlertDialogHeader,
1617
AlertDialogTitle,
1718
} from '@/components/ui/alert-dialog';
18-
import { LoadingSpinner } from '@/components/common/LoadingSpinner';
1919
import { Badge } from '@/components/ui/badge';
2020
import { Button } from '@/components/ui/button';
2121
import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog';
@@ -157,7 +157,7 @@ export function VersionDiffDialog({
157157

158158
<div className="flex items-center gap-3 pt-2">
159159
<div className="flex-1 space-y-1">
160-
<label className="text-xs text-foreground-muted">Left (old)</label>
160+
<span className="text-xs text-foreground-muted">Left (old)</span>
161161
<Select value={String(leftVersion)} onValueChange={(v) => setLeftVersion(Number(v))}>
162162
<SelectTrigger>
163163
<SelectValue />
@@ -184,8 +184,11 @@ export function VersionDiffDialog({
184184
</div>
185185
<span className="text-foreground-muted pt-5">vs</span>
186186
<div className="flex-1 space-y-1">
187-
<label className="text-xs text-foreground-muted">Right (new)</label>
188-
<Select value={String(rightVersion)} onValueChange={(v) => setRightVersion(Number(v))}>
187+
<span className="text-xs text-foreground-muted">Right (new)</span>
188+
<Select
189+
value={String(rightVersion)}
190+
onValueChange={(v) => setRightVersion(Number(v))}
191+
>
189192
<SelectTrigger>
190193
<SelectValue />
191194
</SelectTrigger>
@@ -271,7 +274,10 @@ export function VersionDiffDialog({
271274
</DialogContent>
272275
</Dialog>
273276

274-
<AlertDialog open={restoreVersion !== null} onOpenChange={(o) => !o && setRestoreVersion(null)}>
277+
<AlertDialog
278+
open={restoreVersion !== null}
279+
onOpenChange={(o) => !o && setRestoreVersion(null)}
280+
>
275281
<AlertDialogContent>
276282
<AlertDialogHeader>
277283
<AlertDialogTitle>Restore this version?</AlertDialogTitle>
@@ -283,7 +289,9 @@ export function VersionDiffDialog({
283289
</AlertDialogHeader>
284290
<AlertDialogFooter>
285291
<AlertDialogCancel>Cancel</AlertDialogCancel>
286-
<AlertDialogAction onClick={() => restoreVersion && promoteMutation.mutate(restoreVersion)}>
292+
<AlertDialogAction
293+
onClick={() => restoreVersion && promoteMutation.mutate(restoreVersion)}
294+
>
287295
Restore
288296
</AlertDialogAction>
289297
</AlertDialogFooter>

src/frontend/src/components/library/EntryCard.tsx

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -157,9 +157,7 @@ export const EntryCard = memo(function EntryCard({
157157
</CardHeader>
158158

159159
<CardContent className="pb-3">
160-
<p className="text-sm text-foreground-muted line-clamp-2">
161-
{preview || 'Empty prompt'}
162-
</p>
160+
<p className="text-sm text-foreground-muted line-clamp-2">{preview || 'Empty prompt'}</p>
163161
{entry.tags && entry.tags.length > 0 && (
164162
<div className="flex flex-wrap gap-1 mt-2">
165163
{entry.tags.slice(0, 4).map((tag) => (

src/frontend/src/components/library/FolderPickerDialog.tsx

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,12 +41,21 @@ function PickerNode({
4141
return (
4242
<Collapsible open={isOpen} onOpenChange={setIsOpen}>
4343
<div
44+
role="treeitem"
45+
aria-selected={isSelected}
46+
tabIndex={0}
4447
className={cn(
4548
'flex items-center gap-1 rounded-md py-1 pr-2 cursor-pointer hover:bg-accent',
4649
isSelected && 'bg-primary/10 text-primary'
4750
)}
4851
style={{ paddingLeft: `${depth * 16 + 8}px` }}
4952
onClick={() => onSelect(folder.id)}
53+
onKeyDown={(e) => {
54+
if (e.key === 'Enter' || e.key === ' ') {
55+
e.preventDefault();
56+
onSelect(folder.id);
57+
}
58+
}}
5059
>
5160
<CollapsibleTrigger asChild>
5261
<button
@@ -128,11 +137,20 @@ export function FolderPickerDialog({
128137
<div className="max-h-64 overflow-y-auto rounded-md border p-2">
129138
{/* Root option */}
130139
<div
140+
role="treeitem"
141+
aria-selected={isRootSelected}
142+
tabIndex={0}
131143
className={cn(
132144
'flex items-center gap-2 rounded-md px-2 py-1.5 cursor-pointer hover:bg-accent text-sm',
133145
isRootSelected && 'bg-primary/10 text-primary'
134146
)}
135147
onClick={() => setSelectedId(null)}
148+
onKeyDown={(e) => {
149+
if (e.key === 'Enter' || e.key === ' ') {
150+
e.preventDefault();
151+
setSelectedId(null);
152+
}
153+
}}
136154
>
137155
<Home className="size-4 text-foreground-muted" />
138156
<span>Root</span>

src/frontend/src/components/library/FolderTree.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ export function FolderTree() {
3838
queryFn: () => entryService.getEntriesList(null, 1, 1000),
3939
staleTime: 60_000, // avoid refetching on every navigation — 1 min cache
4040
});
41-
const entries = entriesData?.items ?? [];
41+
const entries = useMemo(() => entriesData?.items ?? [], [entriesData]);
4242

4343
const rootEntries = useMemo(() => entries.filter((e) => e.folderId === null), [entries]);
4444
const entryCountMap = useMemo(() => buildEntryCountMap(entries), [entries]);

src/frontend/src/components/library/TrashPreviewSheet.tsx

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -126,9 +126,7 @@ export function TrashPreviewSheet({
126126
<div className="flex-1 overflow-y-auto space-y-4 py-4">
127127
{entry.systemMessage && (
128128
<div className="space-y-1.5">
129-
<label className="text-sm font-medium text-foreground-muted">
130-
System Message
131-
</label>
129+
<span className="text-sm font-medium text-foreground-muted">System Message</span>
132130
<MarkdownEditor
133131
content={entry.systemMessage}
134132
onContentChange={() => {}}
@@ -141,9 +139,9 @@ export function TrashPreviewSheet({
141139

142140
{entry.prompts.map((prompt, i) => (
143141
<div key={prompt.id} className="space-y-1.5">
144-
<label className="text-sm font-medium text-foreground-muted">
142+
<span className="text-sm font-medium text-foreground-muted">
145143
Prompt {entry.prompts.length > 1 ? i + 1 : ''}
146-
</label>
144+
</span>
147145
<MarkdownEditor
148146
content={prompt.content}
149147
onContentChange={() => {}}
@@ -156,9 +154,9 @@ export function TrashPreviewSheet({
156154

157155
{isTemplate && (
158156
<div className="space-y-1.5">
159-
<label className="text-sm font-medium text-foreground-muted">
157+
<span className="text-sm font-medium text-foreground-muted">
160158
Template Variables
161-
</label>
159+
</span>
162160
<div className="flex flex-wrap gap-1.5">
163161
{templateFields.map((field) => (
164162
<Badge key={field.name} variant="secondary">

0 commit comments

Comments
 (0)