Files
minstrel/web/src/lib/components/SearchInput.svelte
T
bvandeusenandClaude Opus 5.5 94a9c8cbe3 chore(deps): SvelteKit 3 with adapter-static 4 and TypeScript 6, full-tree npm audit (#5021)
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>
2026-10-08 10:50:39 -04:00

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>