diff --git a/backend/static/export-viewer/index.html b/backend/static/export-viewer/index.html index 9251a67..2775b61 100644 --- a/backend/static/export-viewer/index.html +++ b/backend/static/export-viewer/index.html @@ -4,9 +4,9 @@ EventSnap — Galerie - - + +
diff --git a/frontend/eslint.config.js b/frontend/eslint.config.js index ddd416d..91a567f 100644 --- a/frontend/eslint.config.js +++ b/frontend/eslint.config.js @@ -58,10 +58,16 @@ export default ts.config( }, { ignores: [ - '.svelte-kit/', - 'build/', - 'dist/', - 'node_modules/', + // `**/` matters: these patterns are relative to this config's directory, so a bare + // `.svelte-kit/` matched only the top-level one and left `export-viewer/.svelte-kit/` + // — SvelteKit's GENERATED types, gitignored but present after a viewer build — being + // linted, for 10 errors in code nobody wrote. Rebuilding the viewer is a required + // step whenever its source changes (it is embedded in the binary via include_dir!), + // so that directory exists in any normal working tree. + '**/.svelte-kit/', + '**/build/', + '**/dist/', + '**/node_modules/', 'static/export-viewer/', '*.config.js', '*.config.ts' diff --git a/frontend/export-viewer/package-lock.json b/frontend/export-viewer/package-lock.json index 4807d1b..667dbbd 100644 --- a/frontend/export-viewer/package-lock.json +++ b/frontend/export-viewer/package-lock.json @@ -901,7 +901,6 @@ "integrity": "sha512-9hDOl3yUh8UXWt+mN29dbcdrW0vNwPvMqi01y2Mw+ceErNIISh8MeEY7fXT2Dx1CjC/kfsVqrbxw7DifYr4hsg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@standard-schema/spec": "^1.0.0", "@sveltejs/acorn-typescript": "^1.0.5", @@ -944,7 +943,6 @@ "integrity": "sha512-ou/d51QSdTyN26D7h6dSpusAKaZkAiGM55/AKYi+9AGZw7q85hElbjK3kEyzXHhLSnRISHOYzVge6x0jRZ7DXA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@sveltejs/vite-plugin-svelte-inspector": "^5.0.0", "deepmerge": "^4.3.1", @@ -1291,7 +1289,6 @@ "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", "dev": true, "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -1904,7 +1901,6 @@ "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -2024,7 +2020,6 @@ "integrity": "sha512-QjvU7EFemf6mRzdMGlAFttMWtAAVXrax61SZYHdkD6yoVGQ89VeyKfZD4H1JrV1WLmJBxWhFch9H6ig/87VGjw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jridgewell/remapping": "^2.3.4", "@jridgewell/sourcemap-codec": "^1.5.0", @@ -2114,7 +2109,6 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -2129,7 +2123,6 @@ "integrity": "sha512-Bby3NOsna2jsjfLVOHKes8sGwgl4TT0E6vvpYgnAYDIF/tie7MRaFthmKuHx1NSXjiTueXH3do80FMQgvEktRg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", diff --git a/frontend/export-viewer/src/routes/+page.svelte b/frontend/export-viewer/src/routes/+page.svelte index 4ab0769..1611d2f 100644 --- a/frontend/export-viewer/src/routes/+page.svelte +++ b/frontend/export-viewer/src/routes/+page.svelte @@ -57,22 +57,25 @@ ...allTags.slice(0, 3).map(([t]) => ({ type: 'tag' as const, value: t })) ]; } + // Once the user has TYPED, every match is offered — no `.slice()`. Capping matters + // more here than in the live app, for two reasons: this viewer holds the WHOLE event + // (the export queries are uncapped), so the tag set is far larger than any one feed + // page; and the keepsake is a frozen artifact — a guest opening it years later has no + // server to re-query and no "scroll to load more" escape hatch. A silently dropped + // match would simply be unreachable forever. The dropdown scrolls instead. if (q.startsWith('#')) { const prefix = q.slice(1).toLowerCase(); return allTags .filter(([t]) => t.startsWith(prefix)) - .slice(0, 8) .map(([t]) => ({ type: 'tag' as const, value: t })); } const lower = q.toLowerCase(); return [ ...allUploaders .filter((u) => u.toLowerCase().includes(lower)) - .slice(0, 4) .map((u) => ({ type: 'user' as const, value: u })), ...allTags .filter(([t]) => t.includes(lower)) - .slice(0, 4) .map(([t]) => ({ type: 'tag' as const, value: t })) ]; }); @@ -381,13 +384,24 @@ {#if showAutocomplete && suggestions.length > 0} +
e.preventDefault()} + role="listbox" + tabindex="-1" > {#each suggestions as item (item.type + ':' + item.value)}