diff --git a/frontend/.prettierignore b/frontend/.prettierignore new file mode 100644 index 0000000..48ba5e3 --- /dev/null +++ b/frontend/.prettierignore @@ -0,0 +1,6 @@ +.svelte-kit/ +build/ +dist/ +node_modules/ +static/export-viewer/ +package-lock.json diff --git a/frontend/.prettierrc b/frontend/.prettierrc new file mode 100644 index 0000000..9573023 --- /dev/null +++ b/frontend/.prettierrc @@ -0,0 +1,8 @@ +{ + "useTabs": true, + "singleQuote": true, + "trailingComma": "none", + "printWidth": 100, + "plugins": ["prettier-plugin-svelte"], + "overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }] +} diff --git a/frontend/eslint.config.js b/frontend/eslint.config.js new file mode 100644 index 0000000..ddd416d --- /dev/null +++ b/frontend/eslint.config.js @@ -0,0 +1,70 @@ +import js from '@eslint/js'; +import ts from 'typescript-eslint'; +import svelte from 'eslint-plugin-svelte'; +import prettier from 'eslint-config-prettier'; +import globals from 'globals'; +import svelteConfig from './svelte.config.js'; + +/** + * Flat-config ESLint for the SvelteKit frontend. Type-aware where it's cheap; Prettier owns + * formatting (its config is last so it disables every stylistic rule that would fight the formatter). + */ +export default ts.config( + js.configs.recommended, + ...ts.configs.recommended, + ...svelte.configs.recommended, + prettier, + ...svelte.configs.prettier, + { + languageOptions: { + globals: { ...globals.browser, ...globals.node } + } + }, + { + files: ['**/*.svelte', '**/*.svelte.ts', '**/*.svelte.js'], + languageOptions: { + parserOptions: { + projectService: true, + extraFileExtensions: ['.svelte'], + parser: ts.parser, + svelteConfig + } + } + }, + { + rules: { + // A leading underscore is the deliberate "intentionally unused" marker (e.g. `{#each x as _}`). + '@typescript-eslint/no-unused-vars': [ + 'error', + { argsIgnorePattern: '^_', varsIgnorePattern: '^_', caughtErrorsIgnorePattern: '^_' } + ], + // Off on purpose. This rule wants every `goto('/x')` / `href="/x"` wrapped in `resolve()` + // for typed routes. That is a taste/ergonomics preference, not a correctness rule — string + // routes work fine and the app uses them deliberately. We keep the rules that catch actual + // bugs (require-each-key, prefer-svelte-reactivity) and drop this churn. + 'svelte/no-navigation-without-resolve': 'off', + // Off: `svelte-ignore` comments are consumed by the Svelte compiler / svelte-check, which + // ESLint cannot see — so it reports every one as "unused" even when it is actively + // suppressing a real svelte-check a11y warning. Removing them on ESLint's say-so would + // reintroduce those warnings. svelte-check is the authority on these, not ESLint. + 'svelte/no-unused-svelte-ignore': 'off' + } + }, + { + // Test fixtures legitimately use `any` for partial/mock shapes (e.g. a stub upload that only + // sets the two fields the function under test reads). Not worth threading full types through. + files: ['**/*.test.ts'], + rules: { '@typescript-eslint/no-explicit-any': 'off' } + }, + { + ignores: [ + '.svelte-kit/', + 'build/', + 'dist/', + 'node_modules/', + 'static/export-viewer/', + '*.config.js', + '*.config.ts' + ] + } +); diff --git a/frontend/export-viewer/src/app.css b/frontend/export-viewer/src/app.css index 43a5ae6..6093704 100644 --- a/frontend/export-viewer/src/app.css +++ b/frontend/export-viewer/src/app.css @@ -1,3 +1,3 @@ /* Pulls the live app's design tokens so the offline keepsake matches visually. * See ../../src/tailwind-theme.css for the source of truth. */ -@import "../../src/tailwind-theme.css"; +@import '../../src/tailwind-theme.css'; diff --git a/frontend/export-viewer/src/routes/+page.svelte b/frontend/export-viewer/src/routes/+page.svelte index aa891d5..ee8d5b5 100644 --- a/frontend/export-viewer/src/routes/+page.svelte +++ b/frontend/export-viewer/src/routes/+page.svelte @@ -1,6 +1,6 @@ - - {#if active} - + {/if} + + diff --git a/frontend/src/lib/components/LightboxModal.svelte b/frontend/src/lib/components/LightboxModal.svelte index 583c6de..2bd2cc6 100644 --- a/frontend/src/lib/components/LightboxModal.svelte +++ b/frontend/src/lib/components/LightboxModal.svelte @@ -55,7 +55,9 @@ try { const { comment_id } = JSON.parse(data) as { comment_id: string }; comments = comments.filter((c) => c.id !== comment_id); - } catch { /* ignore */ } + } catch { + /* ignore */ + } }); // Pull in a comment posted from another device on the CURRENTLY-open upload, so the @@ -66,7 +68,9 @@ try { const { upload_id } = JSON.parse(data) as { upload_id: string }; if (upload_id === upload.id) void loadComments(upload.id); - } catch { /* ignore */ } + } catch { + /* ignore */ + } }); onDestroy(() => { @@ -124,7 +128,10 @@ function formatTime(iso: string): string { return new Date(iso).toLocaleString('de-DE', { - day: '2-digit', month: '2-digit', hour: '2-digit', minute: '2-digit' + day: '2-digit', + month: '2-digit', + hour: '2-digit', + minute: '2-digit' }); } @@ -138,7 +145,9 @@ use:scrollLock use:modalInert > -
+
-
+
{#if isVideo(upload.mime_type)}