From 6e10238fe03b28797f40978b797d628c0121ab47 Mon Sep 17 00:00:00 2001 From: Lukas Date: Mon, 16 Mar 2026 12:05:45 +0100 Subject: [PATCH] Add filter input to source manager for searching cached sources by name Co-Authored-By: Claude Opus 4.6 --- apps/web/src/components/source-manager.tsx | 32 ++++++++++++++++++++-- specs/004-bestiary/spec.md | 5 ++-- 2 files changed, 32 insertions(+), 5 deletions(-) diff --git a/apps/web/src/components/source-manager.tsx b/apps/web/src/components/source-manager.tsx index 07ca7e2..3a366ad 100644 --- a/apps/web/src/components/source-manager.tsx +++ b/apps/web/src/components/source-manager.tsx @@ -1,8 +1,15 @@ -import { Database, Trash2 } from "lucide-react"; -import { useCallback, useEffect, useOptimistic, useState } from "react"; +import { Database, Search, Trash2 } from "lucide-react"; +import { + useCallback, + useEffect, + useMemo, + useOptimistic, + useState, +} from "react"; import type { CachedSourceInfo } from "../adapters/bestiary-cache.js"; import * as bestiaryCache from "../adapters/bestiary-cache.js"; import { Button } from "./ui/button.js"; +import { Input } from "./ui/input.js"; interface SourceManagerProps { onCacheCleared: () => void; @@ -12,6 +19,7 @@ export function SourceManager({ onCacheCleared, }: Readonly) { const [sources, setSources] = useState([]); + const [filter, setFilter] = useState(""); const [optimisticSources, applyOptimistic] = useOptimistic( sources, ( @@ -46,6 +54,15 @@ export function SourceManager({ onCacheCleared(); }; + const filteredSources = useMemo(() => { + const term = filter.toLowerCase(); + return term + ? optimisticSources.filter((s) => + s.displayName.toLowerCase().includes(term), + ) + : optimisticSources; + }, [optimisticSources, filter]); + if (optimisticSources.length === 0) { return (
@@ -70,8 +87,17 @@ export function SourceManager({ Clear All
+
+ + setFilter(e.target.value)} + className="pl-8" + /> +
    - {optimisticSources.map((source) => ( + {filteredSources.map((source) => (