@ -17,10 +17,14 @@
@@ -17,10 +17,14 @@
selectedKind?: number | null; // Selected kind for filtering
onKindChange?: (kind: number | null) => void; // Callback when kind filter changes
hideDropdownResults?: boolean; // If true, don't show dropdown results (for /find page)
onSearchResults?: (results: { events : NostrEvent []; profiles : string [] } ) => void; // Callback for search results (events and profile pubkeys)
onSearchResults?: (results: { events : NostrEvent []; profiles : string []; relays? : string [] } ) => void; // Callback for search results (events and profile pubkeys, and relays used)
allowedKinds?: number[]; // Hard-coded list of kinds to search (hides kind dropdown)
}
let { mode = 'search' , placeholder = 'Search events, profiles, pubkeys, or enter event ID...' , onFilterChange , showKindFilter = false , selectedKind = null , onKindChange , hideDropdownResults = false , onSearchResults } : Props = $props();
let { mode = 'search' , placeholder = 'Search events, profiles, pubkeys, or enter event ID...' , onFilterChange , showKindFilter = false , selectedKind = null , onKindChange , hideDropdownResults = false , onSearchResults , allowedKinds } : Props = $props();
// Use allowedKinds if provided, otherwise use selectedKind
let effectiveKinds = $derived(allowedKinds & & allowedKinds.length > 0 ? allowedKinds : (selectedKind !== null ? [selectedKind] : null));
let searchQuery = $state('');
let searching = $state(false);
@ -119,7 +123,7 @@
@@ -119,7 +123,7 @@
if (!searchQuery.trim()) {
searchResults = [];
showResults = false;
filterResult = { type : null , value : null , kind : selectedKind } ;
filterResult = { type : null , value : null , kind : effectiveKinds?. [ 0 ] || selectedKind || null } ;
if (onFilterChange) onFilterChange(filterResult);
return;
}
@ -129,7 +133,10 @@
@@ -129,7 +133,10 @@
resolving = true;
searchResults = [];
showResults = true;
filterResult = { type : null , value : null , kind : selectedKind } ;
filterResult = { type : null , value : null , kind : effectiveKinds?. [ 0 ] || selectedKind || null } ;
// Get relays that will be used for search (used for empty result messages)
const relaysUsed = relayManager.getAllAvailableRelays();
try {
const query = searchQuery.trim();
@ -140,7 +147,7 @@
@@ -140,7 +147,7 @@
let event: NostrEvent | undefined = await getEvent(hexId);
if (!event) {
const relays = relayManager.getFeedRead Relays();
const relays = relayManager.getAllAvailable Relays();
const events = await nostrClient.fetchEvents(
[{ ids : [ hexId ] } ],
relays,
@ -154,68 +161,72 @@
@@ -154,68 +161,72 @@
}
if (event) {
if (mode === 'search') {
if (hideDropdownResults && onSearchResults) {
foundEvents = [event];
onSearchResults({ events : foundEvents , profiles : [] } );
// If kinds are specified, filter by kind
if (effectiveKinds && effectiveKinds.length > 0 && !effectiveKinds.includes(event.kind)) {
// Event found but doesn't match allowed kinds, continue to next check
} else {
if (mode === 'search') {
if (hideDropdownResults && onSearchResults) {
foundEvents = [event];
onSearchResults({ events : foundEvents , profiles : [], relays : relaysUsed } );
} else {
searchResults = [{ event , matchType : 'Event ID' } ];
showResults = true;
}
} else {
searchResults = [{ event , matchType : 'Event ID' } ];
showResults = true;
filterResult = { type : 'event' , value : event.id , kind : effectiveKinds?. [ 0 ] || selectedKind || null } ;
if (onFilterChange) onFilterChange(filterResult) ;
}
} else {
filterResult = { type : 'event' , value : event.id } ;
if (onFilterChange) onFilterChange(filterResult);
searching = false;
resolving = false ;
return ;
}
searching = false;
resolving = false;
return;
}
// Event not found, try as pubkey (step 2)
const hexPubkey = hexId.toLowerCase();
// If kind is selected, search for events with this pubkey in p/q tags
if (selectedKind !== null && mode === 'search' && hideDropdownResults && onSearchResults) {
const relays = relayManager.getProfileReadRelays();
const filters: any[] = [{
kinds: [selectedKind],
limit: 100
}];
// Search for events with pubkey in p or q tags
const eventsWithP = await nostrClient.fetchEvents(
filters.map(f => ({ ... f , '#p' : [ hexPubkey ] } )),
relays,
{ useCache : true , cacheResults : true , timeout : 10000 }
);
const eventsWithQ = await nostrClient.fetchEvents(
filters.map(f => ({ ... f , '#q' : [ hexPubkey ] } )),
relays,
{ useCache : true , cacheResults : true , timeout : 10000 }
);
// Also search by author
const eventsByAuthor = await nostrClient.fetchEvents(
filters.map(f => ({ ... f , authors : [ hexPubkey ] } )),
relays,
{ useCache : true , cacheResults : true , timeout : 10000 }
);
// Combine and deduplicate
// If kinds are specified, search for events with this pubkey in p/q tags
if (effectiveKinds && effectiveKinds.length > 0 && mode === 'search' && hideDropdownResults && onSearchResults) {
const relays = relayManager.getAllAvailableRelays();
const allEvents = new Map< string , NostrEvent > ();
for (const event of [...eventsWithP, ...eventsWithQ, ...eventsByAuthor]) {
allEvents.set(event.id, event);
// Search each allowed kind
for (const kind of effectiveKinds) {
// Search by author (most important for bookmarks and highlights)
const eventsByAuthor = await nostrClient.fetchEvents(
[{ kinds : [ kind ], authors : [ hexPubkey ], limit : 100 } ],
relays,
{ useCache : true , cacheResults : true , timeout : 10000 }
);
// Search for events with pubkey in p or q tags
const eventsWithP = await nostrClient.fetchEvents(
[{ kinds : [ kind ], '#p' : [ hexPubkey ], limit : 100 } ],
relays,
{ useCache : true , cacheResults : true , timeout : 10000 }
);
const eventsWithQ = await nostrClient.fetchEvents(
[{ kinds : [ kind ], '#q' : [ hexPubkey ], limit : 100 } ],
relays,
{ useCache : true , cacheResults : true , timeout : 10000 }
);
// Add to combined results
for (const event of [...eventsByAuthor, ...eventsWithP, ...eventsWithQ]) {
allEvents.set(event.id, event);
}
}
foundEvents = Array.from(allEvents.values());
onSearchResults({ events : foundEvents , profiles : [] } );
onSearchResults({ events : foundEvents , profiles : [], relays : relaysUsed } );
searching = false;
resolving = false;
return;
} else if (mode === 'search') {
if (hideDropdownResults && onSearchResults) {
foundProfiles = [hexPubkey];
onSearchResults({ events : [], profiles : foundProfiles } );
onSearchResults({ events : [], profiles : foundProfiles , relays : relaysUsed } );
} else {
// For search mode, navigate to profile
handleProfileClick(hexPubkey);
@ -224,7 +235,7 @@
@@ -224,7 +235,7 @@
resolving = false;
return;
} else {
filterResult = { type : 'pubkey' , value : hexPubkey , kind : selectedKind } ;
filterResult = { type : 'pubkey' , value : hexPubkey , kind : effectiveKinds?. [ 0 ] || selectedKind || null } ;
if (onFilterChange) onFilterChange(filterResult);
searching = false;
resolving = false;
@ -248,49 +259,48 @@
@@ -248,49 +259,48 @@
if (pubkey) {
const normalizedPubkey = pubkey.toLowerCase();
// If kind is selected, search for events with this pubkey in p/q tags
if (selectedKind !== null && mode === 'search' && hideDropdownResults && onSearchResults) {
const relays = relayManager.getProfileReadRelays();
const filters: any[] = [{
kinds: [selectedKind],
limit: 100
}];
// Search for events with pubkey in p or q tags
const eventsWithP = await nostrClient.fetchEvents(
filters.map(f => ({ ... f , '#p' : [ normalizedPubkey ] } )),
relays,
{ useCache : true , cacheResults : true , timeout : 10000 }
);
const eventsWithQ = await nostrClient.fetchEvents(
filters.map(f => ({ ... f , '#q' : [ normalizedPubkey ] } )),
relays,
{ useCache : true , cacheResults : true , timeout : 10000 }
);
// Also search by author
const eventsByAuthor = await nostrClient.fetchEvents(
filters.map(f => ({ ... f , authors : [ normalizedPubkey ] } )),
relays,
{ useCache : true , cacheResults : true , timeout : 10000 }
);
// Combine and deduplicate
// If kinds are specified, search for events with this pubkey in p/q tags
if (effectiveKinds && effectiveKinds.length > 0 && mode === 'search' && hideDropdownResults && onSearchResults) {
const relays = relayManager.getAllAvailableRelays();
const allEvents = new Map< string , NostrEvent > ();
for (const event of [...eventsWithP, ...eventsWithQ, ...eventsByAuthor]) {
allEvents.set(event.id, event);
// Search each allowed kind
for (const kind of effectiveKinds) {
// Search by author (most important for bookmarks and highlights)
const eventsByAuthor = await nostrClient.fetchEvents(
[{ kinds : [ kind ], authors : [ normalizedPubkey ], limit : 100 } ],
relays,
{ useCache : true , cacheResults : true , timeout : 10000 }
);
// Search for events with pubkey in p or q tags
const eventsWithP = await nostrClient.fetchEvents(
[{ kinds : [ kind ], '#p' : [ normalizedPubkey ], limit : 100 } ],
relays,
{ useCache : true , cacheResults : true , timeout : 10000 }
);
const eventsWithQ = await nostrClient.fetchEvents(
[{ kinds : [ kind ], '#q' : [ normalizedPubkey ], limit : 100 } ],
relays,
{ useCache : true , cacheResults : true , timeout : 10000 }
);
// Add to combined results
for (const event of [...eventsByAuthor, ...eventsWithP, ...eventsWithQ]) {
allEvents.set(event.id, event);
}
}
foundEvents = Array.from(allEvents.values());
onSearchResults({ events : foundEvents , profiles : [] } );
onSearchResults({ events : foundEvents , profiles : [], relays : relaysUsed } );
searching = false;
resolving = false;
return;
} else if (mode === 'search') {
if (hideDropdownResults && onSearchResults) {
foundProfiles = [normalizedPubkey];
onSearchResults({ events : [], profiles : foundProfiles } );
onSearchResults({ events : [], profiles : foundProfiles , relays : relaysUsed } );
} else {
handleProfileClick(normalizedPubkey);
}
@ -298,7 +308,7 @@
@@ -298,7 +308,7 @@
resolving = false;
return;
} else {
filterResult = { type : 'pubkey' , value : normalizedPubkey , kind : selectedKind } ;
filterResult = { type : 'pubkey' , value : normalizedPubkey , kind : effectiveKinds?. [ 0 ] || selectedKind || null } ;
if (onFilterChange) onFilterChange(filterResult);
searching = false;
resolving = false;
@ -318,49 +328,48 @@
@@ -318,49 +328,48 @@
const cachedPubkey = await searchCacheForNIP05(query);
if (cachedPubkey) {
const normalizedPubkey = cachedPubkey.toLowerCase();
// If kind is selected, search for events with this pubkey in p/q tags
if (selectedKind !== null && mode === 'search' && hideDropdownResults && onSearchResults) {
const relays = relayManager.getProfileReadRelays();
const filters: any[] = [{
kinds: [selectedKind],
limit: 100
}];
// Search for events with pubkey in p or q tags
const eventsWithP = await nostrClient.fetchEvents(
filters.map(f => ({ ... f , '#p' : [ normalizedPubkey ] } )),
relays,
{ useCache : true , cacheResults : true , timeout : 10000 }
);
const eventsWithQ = await nostrClient.fetchEvents(
filters.map(f => ({ ... f , '#q' : [ normalizedPubkey ] } )),
relays,
{ useCache : true , cacheResults : true , timeout : 10000 }
);
// Also search by author
const eventsByAuthor = await nostrClient.fetchEvents(
filters.map(f => ({ ... f , authors : [ normalizedPubkey ] } )),
relays,
{ useCache : true , cacheResults : true , timeout : 10000 }
);
// Combine and deduplicate
// If kinds are specified, search for events with this pubkey in p/q tags
if (effectiveKinds && effectiveKinds.length > 0 && mode === 'search' && hideDropdownResults && onSearchResults) {
const relays = relayManager.getAllAvailableRelays();
const allEvents = new Map< string , NostrEvent > ();
for (const event of [...eventsWithP, ...eventsWithQ, ...eventsByAuthor]) {
allEvents.set(event.id, event);
// Search each allowed kind
for (const kind of effectiveKinds) {
// Search by author (most important for bookmarks and highlights)
const eventsByAuthor = await nostrClient.fetchEvents(
[{ kinds : [ kind ], authors : [ normalizedPubkey ], limit : 100 } ],
relays,
{ useCache : true , cacheResults : true , timeout : 10000 }
);
// Search for events with pubkey in p or q tags
const eventsWithP = await nostrClient.fetchEvents(
[{ kinds : [ kind ], '#p' : [ normalizedPubkey ], limit : 100 } ],
relays,
{ useCache : true , cacheResults : true , timeout : 10000 }
);
const eventsWithQ = await nostrClient.fetchEvents(
[{ kinds : [ kind ], '#q' : [ normalizedPubkey ], limit : 100 } ],
relays,
{ useCache : true , cacheResults : true , timeout : 10000 }
);
// Add to combined results
for (const event of [...eventsByAuthor, ...eventsWithP, ...eventsWithQ]) {
allEvents.set(event.id, event);
}
}
foundEvents = Array.from(allEvents.values());
onSearchResults({ events : foundEvents , profiles : [] } );
onSearchResults({ events : foundEvents , profiles : [], relays : relaysUsed } );
searching = false;
resolving = false;
return;
} else if (mode === 'search') {
if (hideDropdownResults && onSearchResults) {
foundProfiles = [normalizedPubkey];
onSearchResults({ events : [], profiles : foundProfiles } );
onSearchResults({ events : [], profiles : foundProfiles , relays : relaysUsed } );
} else {
handleProfileClick(normalizedPubkey);
}
@ -368,7 +377,7 @@
@@ -368,7 +377,7 @@
resolving = false;
return;
} else {
filterResult = { type : 'pubkey' , value : normalizedPubkey , kind : selectedKind } ;
filterResult = { type : 'pubkey' , value : normalizedPubkey , kind : effectiveKinds?. [ 0 ] || selectedKind || null } ;
if (onFilterChange) onFilterChange(filterResult);
searching = false;
resolving = false;
@ -380,49 +389,48 @@
@@ -380,49 +389,48 @@
const wellKnownPubkey = await resolveNIP05FromWellKnown(query);
if (wellKnownPubkey) {
const normalizedPubkey = wellKnownPubkey.toLowerCase();
// If kind is selected, search for events with this pubkey in p/q tags
if (selectedKind !== null && mode === 'search' && hideDropdownResults && onSearchResults) {
const relays = relayManager.getProfileReadRelays();
const filters: any[] = [{
kinds: [selectedKind],
limit: 100
}];
// Search for events with pubkey in p or q tags
const eventsWithP = await nostrClient.fetchEvents(
filters.map(f => ({ ... f , '#p' : [ normalizedPubkey ] } )),
relays,
{ useCache : true , cacheResults : true , timeout : 10000 }
);
const eventsWithQ = await nostrClient.fetchEvents(
filters.map(f => ({ ... f , '#q' : [ normalizedPubkey ] } )),
relays,
{ useCache : true , cacheResults : true , timeout : 10000 }
);
// Also search by author
const eventsByAuthor = await nostrClient.fetchEvents(
filters.map(f => ({ ... f , authors : [ normalizedPubkey ] } )),
relays,
{ useCache : true , cacheResults : true , timeout : 10000 }
);
// Combine and deduplicate
// If kinds are specified, search for events with this pubkey in p/q tags
if (effectiveKinds && effectiveKinds.length > 0 && mode === 'search' && hideDropdownResults && onSearchResults) {
const relays = relayManager.getAllAvailableRelays();
const allEvents = new Map< string , NostrEvent > ();
for (const event of [...eventsWithP, ...eventsWithQ, ...eventsByAuthor]) {
allEvents.set(event.id, event);
// Search each allowed kind
for (const kind of effectiveKinds) {
// Search by author (most important for bookmarks and highlights)
const eventsByAuthor = await nostrClient.fetchEvents(
[{ kinds : [ kind ], authors : [ normalizedPubkey ], limit : 100 } ],
relays,
{ useCache : true , cacheResults : true , timeout : 10000 }
);
// Search for events with pubkey in p or q tags
const eventsWithP = await nostrClient.fetchEvents(
[{ kinds : [ kind ], '#p' : [ normalizedPubkey ], limit : 100 } ],
relays,
{ useCache : true , cacheResults : true , timeout : 10000 }
);
const eventsWithQ = await nostrClient.fetchEvents(
[{ kinds : [ kind ], '#q' : [ normalizedPubkey ], limit : 100 } ],
relays,
{ useCache : true , cacheResults : true , timeout : 10000 }
);
// Add to combined results
for (const event of [...eventsByAuthor, ...eventsWithP, ...eventsWithQ]) {
allEvents.set(event.id, event);
}
}
foundEvents = Array.from(allEvents.values());
onSearchResults({ events : foundEvents , profiles : [] } );
onSearchResults({ events : foundEvents , profiles : [], relays : relaysUsed } );
searching = false;
resolving = false;
return;
} else if (mode === 'search') {
if (hideDropdownResults && onSearchResults) {
foundProfiles = [normalizedPubkey];
onSearchResults({ events : [], profiles : foundProfiles } );
onSearchResults({ events : [], profiles : foundProfiles , relays : relaysUsed } );
} else {
handleProfileClick(normalizedPubkey);
}
@ -430,7 +438,7 @@
@@ -430,7 +438,7 @@
resolving = false;
return;
} else {
filterResult = { type : 'pubkey' , value : normalizedPubkey , kind : selectedKind } ;
filterResult = { type : 'pubkey' , value : normalizedPubkey , kind : effectiveKinds?. [ 0 ] || selectedKind || null } ;
if (onFilterChange) onFilterChange(filterResult);
searching = false;
resolving = false;
@ -464,7 +472,7 @@
@@ -464,7 +472,7 @@
let event: NostrEvent | undefined = await getEvent(eventId);
if (!event) {
const relays = relayManager.getFeedRead Relays();
const relays = relayManager.getAllAvailable Relays();
const events = await nostrClient.fetchEvents(
[{ ids : [ eventId ] } ],
relays,
@ -478,21 +486,26 @@
@@ -478,21 +486,26 @@
}
if (event) {
if (mode === 'search') {
if (hideDropdownResults && onSearchResults) {
foundEvents = [event];
onSearchResults({ events : foundEvents , profiles : [] } );
// If kinds are specified, filter by kind
if (effectiveKinds && effectiveKinds.length > 0 && !effectiveKinds.includes(event.kind)) {
// Event found but doesn't match allowed kinds, continue to next check
} else {
if (mode === 'search') {
if (hideDropdownResults && onSearchResults) {
foundEvents = [event];
onSearchResults({ events : foundEvents , profiles : [], relays : relaysUsed } );
} else {
searchResults = [{ event , matchType : 'Event ID' } ];
showResults = true;
}
} else {
searchResults = [{ event , matchType : 'Event ID' } ];
showResults = true;
filterResult = { type : 'event' , value : event.id , kind : effectiveKinds?. [ 0 ] || selectedKind || null } ;
if (onFilterChange) onFilterChange(filterResult) ;
}
} else {
filterResult = { type : 'event' , value : event.id , kind : selectedKind } ;
if (onFilterChange) onFilterChange(filterResult);
searching = false;
resolving = false ;
return ;
}
searching = false;
resolving = false;
return;
}
}
} catch (error) {
@ -502,40 +515,74 @@
@@ -502,40 +515,74 @@
// 6. Anything else is a full-text search
if (mode === 'search') {
// Text search in cached events (title, summary, content)
const allCached: NostrEvent[] = [];
let allEvents: NostrEvent[] = [];
// If kinds are specified, search from relays
if (effectiveKinds && effectiveKinds.length > 0) {
const relays = relayManager.getAllAvailableRelays();
const queryLower = query.toLowerCase();
// Search each allowed kind
for (const kind of effectiveKinds) {
const events = await nostrClient.fetchEvents(
[{ kinds : [ kind ], limit : 100 } ],
relays,
{ useCache : true , cacheResults : true , timeout : 10000 }
);
// Filter by text content
const matches = events.filter(event => {
const contentMatch = event.content.toLowerCase().includes(queryLower);
const titleTag = event.tags.find(t => t[0] === 'title');
const titleMatch = titleTag?.[1]?.toLowerCase().includes(queryLower) || false;
const summaryTag = event.tags.find(t => t[0] === 'summary');
const summaryMatch = summaryTag?.[1]?.toLowerCase().includes(queryLower) || false;
// If kind filter is selected, only search that kind
if (selectedKind !== null) {
const kindEvents = await getEventsByKind(selectedKind, 100);
allCached.push(...kindEvents);
return contentMatch || titleMatch || summaryMatch;
});
allEvents.push(...matches);
}
} else {
// Search all kinds we handle
const kindsToSearch = Object.keys(KIND_LOOKUP).map(k => parseInt(k)).filter(k => !KIND_LOOKUP[k].isSecondaryKind);
for (const kind of kindsToSearch) {
try {
const kindEvents = await getEventsByKind(kind, 50);
allCached.push(...kindEvents);
} catch (e) {
// Skip kinds that fail
// Text search in cached events (title, summary, content)
const allCached: NostrEvent[] = [];
// If kind filter is selected, only search that kind
if (selectedKind !== null) {
const kindEvents = await getEventsByKind(selectedKind, 100);
allCached.push(...kindEvents);
} else {
// Search all kinds we handle
const kindsToSearch = Object.keys(KIND_LOOKUP).map(k => parseInt(k)).filter(k => !KIND_LOOKUP[k].isSecondaryKind);
for (const kind of kindsToSearch) {
try {
const kindEvents = await getEventsByKind(kind, 50);
allCached.push(...kindEvents);
} catch (e) {
// Skip kinds that fail
}
}
}
}
const queryLower = query.toLowerCase();
const matches = allCached.filter(event => {
const contentMatch = event.content.toLowerCase().includes(queryLower);
const queryLower = query.toLowerCase();
allEvent s = allCached.filter(event => {
const contentMatch = event.content.toLowerCase().includes(queryLower);
const titleTag = event.tags.find(t => t[0] === 'title');
const titleMatch = titleTag?.[1]?.toLowerCase().includes(queryLower) || false;
const titleTag = event.tags.find(t => t[0] === 'title');
const titleMatch = titleTag?.[1]?.toLowerCase().includes(queryLower) || false;
const summaryTag = event.tags.find(t => t[0] === 'summary');
const summaryMatch = summaryTag?.[1]?.toLowerCase().includes(queryLower) || false;
const summaryTag = event.tags.find(t => t[0] === 'summary');
const summaryMatch = summaryTag?.[1]?.toLowerCase().includes(queryLower) || false;
return contentMatch || titleMatch || summaryMatch;
});
return contentMatch || titleMatch || summaryMatch;
});
}
const sorted = matches.sort((a, b) => {
// Sort and limit results
const queryLower = query.toLowerCase();
const sorted = allEvents.sort((a, b) => {
const aExact = a.content.toLowerCase() === queryLower;
const bExact = b.content.toLowerCase() === queryLower;
if (aExact & & !bExact) return -1;
@ -543,22 +590,32 @@
@@ -543,22 +590,32 @@
return b.created_at - a.created_at;
});
const limitedResults = sorted.slice(0, 20);
// Deduplicate by event ID
const uniqueEvents = new Map< string , NostrEvent > ();
for (const event of sorted) {
uniqueEvents.set(event.id, event);
}
const limitedResults = Array.from(uniqueEvents.values()).slice(0, 100);
if (hideDropdownResults && onSearchResults) {
foundEvents = limitedResults;
onSearchResults({ events : foundEvents , profiles : [] } );
onSearchResults({ events : foundEvents , profiles : [], relays : relaysUsed } );
} else {
searchResults = limitedResults.map(e => ({ event : e , matchType : 'Content' } ));
showResults = true;
}
} else {
// Filter mode: treat as text search
filterResult = { type : 'text' , value : query , kind : selectedKind } ;
filterResult = { type : 'text' , value : query , kind : effectiveKinds?. [ 0 ] || selectedKind || null } ;
if (onFilterChange) onFilterChange(filterResult);
}
} catch (error) {
console.error('Search error:', error);
// Ensure we reset state even on error
if (hideDropdownResults && onSearchResults) {
onSearchResults({ events : [], profiles : [], relays : relaysUsed } );
}
} finally {
searching = false;
resolving = false;
@ -675,7 +732,7 @@
@@ -675,7 +732,7 @@
< div class = "unified-search-container" >
< div class = "search-input-wrapper" >
{ #if showKindFilter }
{ #if showKindFilter && ! allowedKinds }
< select
value={ selectedKind ? . toString () || '' }
onchange={ handleKindChange }
@ -757,11 +814,34 @@
@@ -757,11 +814,34 @@
border: 1px solid var(--fog-border, #e5e7eb);
border-radius: 0.375rem;
background: var(--fog-post, #ffffff);
background-color: var(--fog-post, #ffffff);
color: var(--fog-text, #1f2937);
font-size: 0.875rem;
font-family: monospace;
cursor: pointer;
min-width: 150px;
appearance: none;
-webkit-appearance: none;
-moz-appearance: none;
}
.kind-filter-select option {
background: var(--fog-post, #ffffff);
background-color: var(--fog-post, #ffffff);
color: var(--fog-text, #1f2937);
}
.kind-filter-select option:checked {
background: var(--fog-accent, #64748b) !important;
background-color: var(--fog-accent, #64748b) !important;
color: var(--fog-post, #ffffff) !important;
}
/* Ensure selected option in dropdown has proper contrast */
.kind-filter-select option:checked:not(:disabled) {
background: var(--fog-accent, #64748b) !important;
background-color: var(--fog-accent, #64748b) !important;
color: var(--fog-post, #ffffff) !important;
}
.kind-filter-select:focus {
@ -773,9 +853,29 @@
@@ -773,9 +853,29 @@
:global(.dark) .kind-filter-select {
border-color: var(--fog-dark-border, #374151);
background: var(--fog-dark-post, #1f2937);
background-color: var(--fog-dark-post, #1f2937);
color: var(--fog-dark-text, #f9fafb);
}
:global(.dark) .kind-filter-select option {
background: var(--fog-dark-post, #1f2937);
background-color: var(--fog-dark-post, #1f2937);
color: var(--fog-dark-text, #f9fafb);
}
:global(.dark) .kind-filter-select option:checked {
background: var(--fog-dark-accent, #94a3b8) !important;
background-color: var(--fog-dark-accent, #94a3b8) !important;
color: #ffffff !important;
}
/* Ensure selected option in dropdown has proper contrast in dark mode */
:global(.dark) .kind-filter-select option:checked:not(:disabled) {
background: var(--fog-dark-accent, #94a3b8) !important;
background-color: var(--fog-dark-accent, #94a3b8) !important;
color: #ffffff !important;
}
:global(.dark) .kind-filter-select:focus {
border-color: var(--fog-dark-accent, #94a3b8);
box-shadow: 0 0 0 3px rgba(148, 163, 184, 0.1);