chore(frontend): add ESLint + Prettier; fix real findings; format the tree

The frontend had no JS/TS linter — only svelte-check. Add flat-config ESLint (typescript-eslint
+ eslint-plugin-svelte) and Prettier (tabs/single-quote, matching the existing style).

Rules encode "catch bugs, not enforce taste":
  - svelte/require-each-key KEPT — it is the exact bug class as the feed mis-tap fix. Fixed every
    flagged block: keyed activeFilters, filteredUsers, stagedFiles (by previewUrl), captionTags,
    admin tabs/jobs/users, the export-viewer suggestions/filters/comments, and the static skeleton
    loops.
  - svelte/prefer-svelte-reactivity KEPT — inline-disabled only the verified-safe sites (a local
    freq Map in a $derived.by, throwaway URLSearchParams query builders), with a reason each.
  - svelte/no-navigation-without-resolve OFF — wants resolve() around every goto()/href; taste, not
    a bug, and pure churn.
  - svelte/no-unused-svelte-ignore OFF — those comments are consumed by svelte-check, which ESLint
    can't see, so it wrongly calls them unused; removing them would reintroduce a11y warnings.
  - no-explicit-any OFF for *.test.ts only (partial fixtures legitimately use any).

Real code fixes beyond keys: removed a dead jobLabel(), an unused ViewerComment import and unused
catch binding, an unused scroll-lock arg, and replaced an empty interface with a type alias.

Then `prettier --write` (54 files). Formatting only. Verified: eslint clean, svelte-check 0 errors,
vitest 46 passed, vite build succeeds.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
fabi
2026-07-15 20:45:42 +02:00
parent 4d14df18d0
commit f8cba95e49
59 changed files with 4046 additions and 998 deletions

View File

@@ -20,8 +20,8 @@ export interface DoubletapOptions {
}
interface DoubletapAttributes {
'ondoubletap'?: (event: CustomEvent<void>) => void;
'onsingletap'?: (event: CustomEvent<void>) => void;
ondoubletap?: (event: CustomEvent<void>) => void;
onsingletap?: (event: CustomEvent<void>) => void;
}
export function doubletap(

View File

@@ -30,7 +30,9 @@ export function focusTrap(
options: FocusTrapOptions = {}
): ActionReturn<FocusTrapOptions> {
let opts = options;
const previouslyFocused = (typeof document !== 'undefined' ? document.activeElement : null) as HTMLElement | null;
const previouslyFocused = (
typeof document !== 'undefined' ? document.activeElement : null
) as HTMLElement | null;
function onKeyDown(e: KeyboardEvent) {
if (e.key === 'Escape' && opts.closeOnEscape !== false && opts.onclose) {
@@ -85,7 +87,11 @@ export function focusTrap(
destroy() {
node.removeEventListener('keydown', onKeyDown);
if (previouslyFocused && typeof previouslyFocused.focus === 'function') {
try { previouslyFocused.focus({ preventScroll: true }); } catch { /* element may have unmounted */ }
try {
previouslyFocused.focus({ preventScroll: true });
} catch {
/* element may have unmounted */
}
}
}
};

View File

@@ -24,7 +24,7 @@ export interface LongpressOptions {
}
interface LongpressAttributes {
'onlongpress'?: (event: CustomEvent<void>) => void;
onlongpress?: (event: CustomEvent<void>) => void;
}
export function longpress(

View File

@@ -40,7 +40,7 @@ function unlock() {
* dialog is open (e.g. inside `{#if open}` or alongside a `class:` toggle) so the
* action's create/destroy line up with open/close.
*/
export function scrollLock(node: HTMLElement): ActionReturn {
export function scrollLock(_node: HTMLElement): ActionReturn {
lock();
return {
destroy() {