feat(home): SvelteKit snapshot für echte State-Preservation beim Back
All checks were successful
Build & Publish Docker Image / build-and-push (push) Successful in 1m16s

Vorher (Commit 1055a67): Query in URL gespiegelt, Back triggerte einen
frischen Request — zwar schnell, aber Scroll-Position + Ergebnisreihen-
folge nicht garantiert konstant.

Jetzt: SvelteKit's snapshot-API speichert query + hits + webHits +
searchedFor + webError in der History-Entry. Beim Zurück-Navigieren aus
/preview oder /recipes/[id] stellt restore() den exakten UI-Zustand
wieder her — ohne Fetch. Scroll-Position wird von SvelteKit ohnehin auto-
restored.

Der Debounce-Effekt hat jetzt einen skipNextSearch-Flag, der beim
Restore true gesetzt wird, damit das Setzen von query keinen neuen
Search-Request auslöst. Erstmaliges Tippen nach dem Restore arbeitet
wieder ganz normal mit Debounce.

Die URL-Synchronisation (?q=…) bleibt bestehen — für Bookmarks und
Share-Links. Snapshot überlagert den URL-Load in der Ordnung:
restore → onMount sieht query bereits korrekt gesetzt → kein Double-Work.
This commit is contained in:
hsiegeln
2026-04-17 19:06:58 +02:00
parent 1055a670da
commit 8db67bd1a5

View File

@@ -2,6 +2,7 @@
import { onMount } from 'svelte';
import { page } from '$app/stores';
import { CookingPot, Globe, X } from 'lucide-svelte';
import type { Snapshot } from './$types';
import type { SearchHit } from '$lib/server/recipes/search-local';
import type { WebHit } from '$lib/server/search/searxng';
import { randomQuote } from '$lib/quotes';
@@ -18,6 +19,27 @@
let webSearching = $state(false);
let webError = $state<string | null>(null);
let searchedFor = $state<string | null>(null);
let skipNextSearch = false;
type SearchSnapshot = {
query: string;
hits: SearchHit[];
webHits: WebHit[];
searchedFor: string | null;
webError: string | null;
};
export const snapshot: Snapshot<SearchSnapshot> = {
capture: () => ({ query, hits, webHits, searchedFor, webError }),
restore: (v) => {
query = v.query;
hits = v.hits;
webHits = v.webHits;
searchedFor = v.searchedFor;
webError = v.webError;
skipNextSearch = true;
}
};
async function loadRecent() {
const res = await fetch('/api/recipes/search');
@@ -100,6 +122,12 @@
$effect(() => {
const q = query.trim();
if (debounceTimer) clearTimeout(debounceTimer);
if (skipNextSearch) {
// Snapshot-Restore hat hits/webHits/searchedFor wiederhergestellt —
// nicht erneut fetchen.
skipNextSearch = false;
return;
}
if (q.length <= 3) {
hits = [];
webHits = [];