success pt. 6 runed
This commit is contained in:
7
bun.lock
7
bun.lock
@@ -18,6 +18,7 @@
|
|||||||
"drizzle-kit": "^0.31.8",
|
"drizzle-kit": "^0.31.8",
|
||||||
"drizzle-orm": "^0.45.1",
|
"drizzle-orm": "^0.45.1",
|
||||||
"lefthook": "^2.1.0",
|
"lefthook": "^2.1.0",
|
||||||
|
"runed": "^0.37.1",
|
||||||
"sqlocal": "^0.17.0",
|
"sqlocal": "^0.17.0",
|
||||||
"svelte": "^5.48.2",
|
"svelte": "^5.48.2",
|
||||||
"svelte-check": "^4.3.5",
|
"svelte-check": "^4.3.5",
|
||||||
@@ -349,6 +350,8 @@
|
|||||||
|
|
||||||
"deepmerge": ["deepmerge@4.3.1", "", {}, "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A=="],
|
"deepmerge": ["deepmerge@4.3.1", "", {}, "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A=="],
|
||||||
|
|
||||||
|
"dequal": ["dequal@2.0.3", "", {}, "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA=="],
|
||||||
|
|
||||||
"detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="],
|
"detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="],
|
||||||
|
|
||||||
"devalue": ["devalue@5.6.2", "", {}, "sha512-nPRkjWzzDQlsejL1WVifk5rvcFi/y1onBRxjaFMjZeR9mFpqu2gmAZ9xUB9/IEanEP/vBtGeGganC/GO1fmufg=="],
|
"devalue": ["devalue@5.6.2", "", {}, "sha512-nPRkjWzzDQlsejL1WVifk5rvcFi/y1onBRxjaFMjZeR9mFpqu2gmAZ9xUB9/IEanEP/vBtGeGganC/GO1fmufg=="],
|
||||||
@@ -449,6 +452,8 @@
|
|||||||
|
|
||||||
"locate-character": ["locate-character@3.0.0", "", {}, "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA=="],
|
"locate-character": ["locate-character@3.0.0", "", {}, "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA=="],
|
||||||
|
|
||||||
|
"lz-string": ["lz-string@1.5.0", "", { "bin": { "lz-string": "bin/bin.js" } }, "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ=="],
|
||||||
|
|
||||||
"magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="],
|
"magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="],
|
||||||
|
|
||||||
"mimic-response": ["mimic-response@3.1.0", "", {}, "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ=="],
|
"mimic-response": ["mimic-response@3.1.0", "", {}, "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ=="],
|
||||||
@@ -503,6 +508,8 @@
|
|||||||
|
|
||||||
"rollup": ["rollup@4.57.1", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.57.1", "@rollup/rollup-android-arm64": "4.57.1", "@rollup/rollup-darwin-arm64": "4.57.1", "@rollup/rollup-darwin-x64": "4.57.1", "@rollup/rollup-freebsd-arm64": "4.57.1", "@rollup/rollup-freebsd-x64": "4.57.1", "@rollup/rollup-linux-arm-gnueabihf": "4.57.1", "@rollup/rollup-linux-arm-musleabihf": "4.57.1", "@rollup/rollup-linux-arm64-gnu": "4.57.1", "@rollup/rollup-linux-arm64-musl": "4.57.1", "@rollup/rollup-linux-loong64-gnu": "4.57.1", "@rollup/rollup-linux-loong64-musl": "4.57.1", "@rollup/rollup-linux-ppc64-gnu": "4.57.1", "@rollup/rollup-linux-ppc64-musl": "4.57.1", "@rollup/rollup-linux-riscv64-gnu": "4.57.1", "@rollup/rollup-linux-riscv64-musl": "4.57.1", "@rollup/rollup-linux-s390x-gnu": "4.57.1", "@rollup/rollup-linux-x64-gnu": "4.57.1", "@rollup/rollup-linux-x64-musl": "4.57.1", "@rollup/rollup-openbsd-x64": "4.57.1", "@rollup/rollup-openharmony-arm64": "4.57.1", "@rollup/rollup-win32-arm64-msvc": "4.57.1", "@rollup/rollup-win32-ia32-msvc": "4.57.1", "@rollup/rollup-win32-x64-gnu": "4.57.1", "@rollup/rollup-win32-x64-msvc": "4.57.1", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-oQL6lgK3e2QZeQ7gcgIkS2YZPg5slw37hYufJ3edKlfQSGGm8ICoxswK15ntSzF/a8+h7ekRy7k7oWc3BQ7y8A=="],
|
"rollup": ["rollup@4.57.1", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.57.1", "@rollup/rollup-android-arm64": "4.57.1", "@rollup/rollup-darwin-arm64": "4.57.1", "@rollup/rollup-darwin-x64": "4.57.1", "@rollup/rollup-freebsd-arm64": "4.57.1", "@rollup/rollup-freebsd-x64": "4.57.1", "@rollup/rollup-linux-arm-gnueabihf": "4.57.1", "@rollup/rollup-linux-arm-musleabihf": "4.57.1", "@rollup/rollup-linux-arm64-gnu": "4.57.1", "@rollup/rollup-linux-arm64-musl": "4.57.1", "@rollup/rollup-linux-loong64-gnu": "4.57.1", "@rollup/rollup-linux-loong64-musl": "4.57.1", "@rollup/rollup-linux-ppc64-gnu": "4.57.1", "@rollup/rollup-linux-ppc64-musl": "4.57.1", "@rollup/rollup-linux-riscv64-gnu": "4.57.1", "@rollup/rollup-linux-riscv64-musl": "4.57.1", "@rollup/rollup-linux-s390x-gnu": "4.57.1", "@rollup/rollup-linux-x64-gnu": "4.57.1", "@rollup/rollup-linux-x64-musl": "4.57.1", "@rollup/rollup-openbsd-x64": "4.57.1", "@rollup/rollup-openharmony-arm64": "4.57.1", "@rollup/rollup-win32-arm64-msvc": "4.57.1", "@rollup/rollup-win32-ia32-msvc": "4.57.1", "@rollup/rollup-win32-x64-gnu": "4.57.1", "@rollup/rollup-win32-x64-msvc": "4.57.1", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-oQL6lgK3e2QZeQ7gcgIkS2YZPg5slw37hYufJ3edKlfQSGGm8ICoxswK15ntSzF/a8+h7ekRy7k7oWc3BQ7y8A=="],
|
||||||
|
|
||||||
|
"runed": ["runed@0.37.1", "", { "dependencies": { "dequal": "^2.0.3", "esm-env": "^1.0.0", "lz-string": "^1.5.0" }, "peerDependencies": { "@sveltejs/kit": "^2.21.0", "svelte": "^5.7.0", "zod": "^4.1.0" }, "optionalPeers": ["@sveltejs/kit", "zod"] }, "sha512-MeFY73xBW8IueWBm012nNFIGy19WUGPLtknavyUPMpnyt350M47PhGSGrGoSLbidwn+Zlt/O0cp8/OZE3LASWA=="],
|
||||||
|
|
||||||
"sade": ["sade@1.8.1", "", { "dependencies": { "mri": "^1.1.0" } }, "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A=="],
|
"sade": ["sade@1.8.1", "", { "dependencies": { "mri": "^1.1.0" } }, "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A=="],
|
||||||
|
|
||||||
"safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="],
|
"safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="],
|
||||||
|
|||||||
@@ -29,6 +29,7 @@
|
|||||||
"drizzle-kit": "^0.31.8",
|
"drizzle-kit": "^0.31.8",
|
||||||
"drizzle-orm": "^0.45.1",
|
"drizzle-orm": "^0.45.1",
|
||||||
"lefthook": "^2.1.0",
|
"lefthook": "^2.1.0",
|
||||||
|
"runed": "^0.37.1",
|
||||||
"sqlocal": "^0.17.0",
|
"sqlocal": "^0.17.0",
|
||||||
"svelte": "^5.48.2",
|
"svelte": "^5.48.2",
|
||||||
"svelte-check": "^4.3.5",
|
"svelte-check": "^4.3.5",
|
||||||
|
|||||||
17
src/lib/types/search/amq-browse.ts
Normal file
17
src/lib/types/search/amq-browse.ts
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* URL search params for the AMQ browse page.
|
||||||
|
*
|
||||||
|
* Intended usage:
|
||||||
|
* - Client: `useSearchParams(AmqBrowseSearchSchema, ...)`
|
||||||
|
* - Server (optional): `validateSearchParams(url, AmqBrowseSearchSchema, ...)`
|
||||||
|
*/
|
||||||
|
export const AmqBrowseSearchSchema = z.object({
|
||||||
|
/**
|
||||||
|
* Free-text query for anime name search.
|
||||||
|
*/
|
||||||
|
q: z.string().default(""),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type AmqBrowseSearch = z.infer<typeof AmqBrowseSearchSchema>;
|
||||||
@@ -1,4 +1,6 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import { Debounced } from "runed";
|
||||||
|
import { useSearchParams } from "runed/kit";
|
||||||
import { onMount } from "svelte";
|
import { onMount } from "svelte";
|
||||||
import {
|
import {
|
||||||
db,
|
db,
|
||||||
@@ -6,23 +8,48 @@
|
|||||||
getAnimeList,
|
getAnimeList,
|
||||||
searchAnimeByName,
|
searchAnimeByName,
|
||||||
} from "$lib/db/client-db";
|
} from "$lib/db/client-db";
|
||||||
|
import { AmqBrowseSearchSchema } from "$lib/types/search/amq-browse";
|
||||||
|
|
||||||
|
const params = useSearchParams(AmqBrowseSearchSchema, {
|
||||||
|
debounce: 250,
|
||||||
|
pushHistory: false,
|
||||||
|
showDefaults: false,
|
||||||
|
});
|
||||||
|
|
||||||
let status = $state<"idle" | "loading" | "ready" | "error">("idle");
|
let status = $state<"idle" | "loading" | "ready" | "error">("idle");
|
||||||
let error = $state<string | null>(null);
|
let error = $state<string | null>(null);
|
||||||
|
|
||||||
let query = $state("");
|
|
||||||
let isSearching = $state(false);
|
let isSearching = $state(false);
|
||||||
|
|
||||||
|
// Debounce the actual DB query updates (separate from URL debounce)
|
||||||
|
const debouncedQuery = new Debounced(() => params.q, 250);
|
||||||
|
|
||||||
type AnimeItem = Awaited<ReturnType<typeof getAnimeList>>[number];
|
type AnimeItem = Awaited<ReturnType<typeof getAnimeList>>[number];
|
||||||
let anime = $state<AnimeItem[]>([]);
|
let anime = $state<AnimeItem[]>([]);
|
||||||
|
|
||||||
|
async function loadListFor(query: string) {
|
||||||
|
const q = query.trim();
|
||||||
|
|
||||||
|
try {
|
||||||
|
isSearching = true;
|
||||||
|
|
||||||
|
if (!q) {
|
||||||
|
anime = await getAnimeList(db, 20);
|
||||||
|
} else {
|
||||||
|
anime = await searchAnimeByName(db, q, 20);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
isSearching = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function loadInitial() {
|
async function loadInitial() {
|
||||||
status = "loading";
|
status = "loading";
|
||||||
error = null;
|
error = null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await ensureSeeded();
|
await ensureSeeded();
|
||||||
anime = await getAnimeList(db, 20);
|
await loadListFor(params.q);
|
||||||
status = "ready";
|
status = "ready";
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
error = e instanceof Error ? e.message : String(e);
|
error = e instanceof Error ? e.message : String(e);
|
||||||
@@ -34,35 +61,22 @@
|
|||||||
void loadInitial();
|
void loadInitial();
|
||||||
});
|
});
|
||||||
|
|
||||||
// Debounced search
|
// React to debounced query changes (URL updates + user typing)
|
||||||
let debounceTimer: ReturnType<typeof setTimeout> | null = null;
|
$effect(() => {
|
||||||
|
if (status !== "ready") return;
|
||||||
|
|
||||||
function scheduleSearch(nextQuery: string) {
|
// Track debounced query to avoid hammering the DB while typing
|
||||||
query = nextQuery;
|
const q = debouncedQuery.current;
|
||||||
|
|
||||||
if (debounceTimer) clearTimeout(debounceTimer);
|
|
||||||
|
|
||||||
debounceTimer = setTimeout(async () => {
|
|
||||||
if (status !== "ready") return;
|
|
||||||
|
|
||||||
const q = query.trim();
|
|
||||||
|
|
||||||
|
void (async () => {
|
||||||
try {
|
try {
|
||||||
isSearching = true;
|
await loadListFor(q);
|
||||||
|
|
||||||
if (!q) {
|
|
||||||
anime = await getAnimeList(db, 20);
|
|
||||||
} else {
|
|
||||||
anime = await searchAnimeByName(db, q, 20);
|
|
||||||
}
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
error = e instanceof Error ? e.message : String(e);
|
error = e instanceof Error ? e.message : String(e);
|
||||||
status = "error";
|
status = "error";
|
||||||
} finally {
|
|
||||||
isSearching = false;
|
|
||||||
}
|
}
|
||||||
}, 200);
|
})();
|
||||||
}
|
});
|
||||||
|
|
||||||
function seasonName(seasonId: number) {
|
function seasonName(seasonId: number) {
|
||||||
switch (seasonId) {
|
switch (seasonId) {
|
||||||
@@ -95,9 +109,8 @@
|
|||||||
id="anime-search"
|
id="anime-search"
|
||||||
class="rounded border px-3 py-2 text-sm"
|
class="rounded border px-3 py-2 text-sm"
|
||||||
placeholder="Type to search by name…"
|
placeholder="Type to search by name…"
|
||||||
value={query}
|
value={params.q}
|
||||||
oninput={(e) =>
|
oninput={(e) => (params.q = (e.currentTarget as HTMLInputElement).value)}
|
||||||
scheduleSearch((e.currentTarget as HTMLInputElement).value)}
|
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
spellcheck={false}
|
spellcheck={false}
|
||||||
/>
|
/>
|
||||||
|
|||||||
Reference in New Issue
Block a user