diff --git a/src/components/KindsViewer.tsx b/src/components/KindsViewer.tsx index 56f267f..41fcea2 100644 --- a/src/components/KindsViewer.tsx +++ b/src/components/KindsViewer.tsx @@ -1,6 +1,10 @@ +import { useState, useRef, useEffect } from "react"; +import { Search, X } from "lucide-react"; import { getKindInfo } from "@/constants/kinds"; import { kindRenderers } from "./nostr/kinds"; import { NIPBadge } from "./NIPBadge"; +import { Input } from "@/components/ui/input"; +import { Button } from "@/components/ui/button"; import { CenteredContent } from "./ui/CenteredContent"; // Dynamically derive supported kinds from renderer registry @@ -11,62 +15,135 @@ const SUPPORTED_KINDS = Object.keys(kindRenderers).map(Number); * Shows all event kinds with rich rendering support */ export default function KindsViewer() { + const [search, setSearch] = useState(""); + const searchInputRef = useRef(null); + + // Autofocus on mount + useEffect(() => { + searchInputRef.current?.focus(); + }, []); + // Sort kinds in ascending order const sortedKinds = [...SUPPORTED_KINDS].sort((a, b) => a - b); + // Filter kinds by search term (matches kind number or name) + const filteredKinds = search + ? sortedKinds.filter((kind) => { + const kindInfo = getKindInfo(kind); + const name = kindInfo?.name || ""; + const description = kindInfo?.description || ""; + const searchLower = search.toLowerCase(); + return ( + kind.toString().includes(search) || + name.toLowerCase().includes(searchLower) || + description.toLowerCase().includes(searchLower) + ); + }) + : sortedKinds; + + // Clear search + const handleClear = () => { + setSearch(""); + searchInputRef.current?.focus(); + }; + + // Handle keyboard shortcuts + const handleKeyDown = (e: React.KeyboardEvent) => { + if (e.key === "Escape") { + handleClear(); + } + }; + return ( {/* Header */}

- Supported Event Kinds ({sortedKinds.length}) + {search + ? `Showing ${filteredKinds.length} of ${sortedKinds.length} Kinds` + : `Supported Event Kinds (${sortedKinds.length})`}

-

+

Event kinds with rich rendering support in Grimoire. Default kinds display raw content only.

+ + {/* Search Input */} +
+ + setSearch(e.target.value)} + onKeyDown={handleKeyDown} + className="pl-9 pr-9" + /> + {search && ( + + )} +
{/* Kind List */} -
- {sortedKinds.map((kind) => { - const kindInfo = getKindInfo(kind); - const Icon = kindInfo?.icon; + {filteredKinds.length > 0 ? ( +
+ {filteredKinds.map((kind) => { + const kindInfo = getKindInfo(kind); + const Icon = kindInfo?.icon; - return ( -
-
- {/* Icon */} -
- {Icon ? ( - - ) : ( - - {kind} - - )} -
- - {/* Content */} -
-
- - {kind} - - - {kindInfo?.name || `Kind ${kind}`} - + return ( +
+
+ {/* Icon */} +
+ {Icon ? ( + + ) : ( + + {kind} + + )} +
+ + {/* Content */} +
+
+ + {kind} + + + {kindInfo?.name || `Kind ${kind}`} + +
+

+ {kindInfo?.description || "No description available"} +

+ {kindInfo?.nip && }
-

- {kindInfo?.description || "No description available"} -

- {kindInfo?.nip && }
-
- ); - })} -
+ ); + })} +
+ ) : ( +
+

No kinds match "{search}"

+

Try searching for a different term

+
+ )} ); }