Merges Renovate's kit 3 (PR #149) and adapter-static 4 (PR #148) bumps, plus the migration they need. The mechanical part is `sv migrate sveltekit-3`, run one task at a time and reviewed: - svelte.config.js is gone. Its options move into sveltekit() in vite.config.ts, exported as kitOptions so vitest.config.ts runs the same kit setup, including the $test-utils alias the tests import. - $lib becomes #lib through package.json "imports". There is no src/lib/index, so only the "#lib/*" entry is kept. - tsconfig extends $app/tsconfig. - Peer floors raised to kit 3's requirements: svelte ^5.57.1, vite ^8.0.12, svelte-check ^4.7.5. By hand, from the codemod's list of non-automated tasks: - goto's replaceState option is now replace; keepFocus becomes reset: false. For the search typeahead, reset: false also stops the scroll-to-top, which is wanted while typing. - The test setup mocks drop pushState/replaceState and $app/paths base/assets, which kit 3 removed, and mock refreshAll in place of invalidateAll. - The other flagged files only read page.url or goto internal routes, so they needed no change. TypeScript goes to ^6, not the ^7 Renovate offers: kit 3 declares typescript ^6 as a peer and svelte-check 4.7 accepts ^5 || ^6. Move to 7 once both accept it. With Tailwind 4 and kit 3 in, `npm audit` on the whole tree reports 0, so the web lane now audits every dependency rather than only what ships to browsers. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
72 lines
2.1 KiB
Svelte
72 lines
2.1 KiB
Svelte
<script lang="ts">
|
|
import { page } from '$app/state';
|
|
import { goto } from '$app/navigation';
|
|
import { untrack } from 'svelte';
|
|
import { Search } from 'lucide-svelte';
|
|
|
|
let value = $state(page.url.searchParams.get('q') ?? '');
|
|
let timeout: ReturnType<typeof setTimeout> | null = null;
|
|
|
|
// Re-sync local value when the URL changes from outside (back/forward,
|
|
// result-link clicks). Read `value` non-reactively to avoid a feedback
|
|
// loop where the user's typing would race against the URL re-read.
|
|
$effect(() => {
|
|
const fromUrl = page.url.searchParams.get('q') ?? '';
|
|
if (fromUrl !== untrack(() => value)) value = fromUrl;
|
|
});
|
|
|
|
function navigate(next: string) {
|
|
const onSearchPage = page.url.pathname.startsWith('/search');
|
|
const trimmed = next.trim();
|
|
// reset: false: SvelteKit blurs the active element and scrolls to the top
|
|
// on navigation by default. For a typeahead-style URL update we want the
|
|
// user to keep typing without re-clicking the input every 250 ms.
|
|
if (trimmed === '') {
|
|
if (onSearchPage) goto('/search', { replace: true, reset: false });
|
|
return;
|
|
}
|
|
goto(`/search?q=${encodeURIComponent(trimmed)}`, {
|
|
replace: onSearchPage,
|
|
reset: false,
|
|
});
|
|
}
|
|
|
|
function onInput() {
|
|
if (timeout) clearTimeout(timeout);
|
|
timeout = setTimeout(() => {
|
|
timeout = null;
|
|
navigate(value);
|
|
}, 250);
|
|
}
|
|
|
|
function onKey(e: KeyboardEvent) {
|
|
if (e.key === 'Escape') {
|
|
e.preventDefault();
|
|
value = '';
|
|
if (timeout) {
|
|
clearTimeout(timeout);
|
|
timeout = null;
|
|
}
|
|
navigate('');
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<label class="relative block w-full max-w-md">
|
|
<Search
|
|
size={14}
|
|
strokeWidth={1.5}
|
|
aria-hidden="true"
|
|
class="pointer-events-none absolute left-2 top-1/2 -translate-y-1/2 text-text-secondary"
|
|
/>
|
|
<input
|
|
type="search"
|
|
placeholder="Search"
|
|
aria-label="Search"
|
|
bind:value
|
|
oninput={onInput}
|
|
onkeydown={onKey}
|
|
class="w-full rounded border border-border bg-surface pl-7 pr-3 py-1 text-sm focus-visible:outline-solid focus-visible:outline-2 focus-visible:outline-accent"
|
|
/>
|
|
</label>
|