diff --git a/src/pages/SearchPage.tsx b/src/pages/SearchPage.tsx
index d8fdb336..e9e135c0 100644
--- a/src/pages/SearchPage.tsx
+++ b/src/pages/SearchPage.tsx
@@ -17,6 +17,8 @@ import {
BookmarkPlus,
Check,
Loader2,
+ ChevronDown,
+ Hash,
} from 'lucide-react';
import { useState, useMemo, useEffect, useCallback } from 'react';
import { Link, useNavigate, useSearchParams } from 'react-router-dom';
@@ -630,18 +632,7 @@ export function SearchPage() {
Kind
-
+
@@ -1067,6 +1058,159 @@ function SaveDestinationRow({
);
}
+type KindOption = {
+ value: string;
+ label: string;
+ description: string;
+ parentId: string;
+ icon: React.ComponentType<{ className?: string }> | undefined;
+};
+
+function KindPicker({
+ value,
+ options,
+ onChange,
+}: {
+ value: string;
+ options: KindOption[];
+ onChange: (v: string) => void;
+}) {
+ const [open, setOpen] = useState(false);
+ const [search, setSearch] = useState('');
+
+ const filtered = useMemo(() => {
+ const q = search.toLowerCase().trim();
+ if (!q) return options;
+ return options.filter(
+ (o) =>
+ o.label.toLowerCase().includes(q) ||
+ o.description.toLowerCase().includes(q) ||
+ o.value.includes(q),
+ );
+ }, [options, search]);
+
+ const selected = value === 'all' ? null : value === 'custom' ? null : options.find((o) => o.value === value);
+ const SelectedIcon = selected?.icon;
+
+ const handleSelect = (v: string) => {
+ onChange(v);
+ setOpen(false);
+ setSearch('');
+ };
+
+ return (
+ { setOpen(o); if (!o) setSearch(''); }}>
+
+
+
+
+ {/* Search input */}
+
+
+ setSearch(e.target.value)}
+ autoFocus
+ />
+ {search && (
+
+ )}
+
+
+ {/* Scrollable list */}
+
+ {/* All option */}
+ {!search && (
+
handleSelect('all')}
+ />
+ )}
+
+ {filtered.map((opt) => (
+ handleSelect(opt.value)}
+ />
+ ))}
+
+ {/* Custom option */}
+ {(!search || 'custom'.includes(search.toLowerCase())) && (
+ handleSelect('custom')}
+ />
+ )}
+
+ {filtered.length === 0 && search && (
+ No kinds match
+ )}
+
+
+
+ );
+}
+
+function KindPickerItem({
+ icon: Icon,
+ label,
+ active,
+ onClick,
+}: {
+ icon: React.ComponentType<{ className?: string }> | null;
+ label: string;
+ active: boolean;
+ onClick: () => void;
+}) {
+ return (
+
+ );
+}
+
function AuthorFilterDropdown({ onCommit }: { onCommit: (pubkey: string, label: string) => void }) {
const handleSelect = useCallback((profile: SearchProfile) => {
const npub = nip19.npubEncode(profile.pubkey);