diff --git a/src/components/ToolFilters.astro b/src/components/ToolFilters.astro index 90f9a0e..15ec517 100644 --- a/src/components/ToolFilters.astro +++ b/src/components/ToolFilters.astro @@ -261,7 +261,7 @@ const sortedTags = Object.entries(tagFrequency) } function filterTools() { - const searchTerm = elements.searchInput.value.toLowerCase(); + const searchTerm = elements.searchInput.value.trim(); const selectedDomain = elements.domainSelect.value; const includeProprietary = elements.proprietaryCheckbox.checked; @@ -270,26 +270,31 @@ const sortedTags = Object.entries(tagFrequency) const phases = tool.phases || []; const tags = tool.tags || []; + // Search filter (keep existing logic) if (searchTerm && !( - tool.name.toLowerCase().includes(searchTerm) || - tool.description.toLowerCase().includes(searchTerm) || - tags.some(tag => tag.toLowerCase().includes(searchTerm)) + tool.name.toLowerCase().includes(searchTerm.toLowerCase()) || + tool.description.toLowerCase().includes(searchTerm.toLowerCase()) || + tags.some(tag => tag.toLowerCase().includes(searchTerm.toLowerCase())) )) { return false; } + // Domain filter if (selectedDomain && !domains.includes(selectedDomain)) { return false; } + // Phase filter if (selectedPhase && !phases.includes(selectedPhase)) { return false; } + // Proprietary filter if (!includeProprietary && !isMethod(tool) && tool.type !== 'concept' && tool.license === 'Proprietary') { return false; } + // Tag filter if (selectedTags.size > 0 && !Array.from(selectedTags).every(tag => tags.includes(tag))) { return false; } @@ -297,9 +302,14 @@ const sortedTags = Object.entries(tagFrequency) return true; }); + // Apply search prioritization if there's a search term + const finalResults = searchTerm && window.prioritizeSearchResults + ? window.prioritizeSearchResults(filtered, searchTerm) + : filtered; + updateMatrixHighlighting(); - window.dispatchEvent(new CustomEvent('toolsFiltered', { detail: filtered })); + window.dispatchEvent(new CustomEvent('toolsFiltered', { detail: finalResults })); } function handleTagClick(tagItem) { diff --git a/src/layouts/BaseLayout.astro b/src/layouts/BaseLayout.astro index e050f65..4698385 100644 --- a/src/layouts/BaseLayout.astro +++ b/src/layouts/BaseLayout.astro @@ -80,6 +80,30 @@ const { title, description = 'ForensicPathways - A comprehensive directory of di scrollToElement(element, options); } + // Simple search prioritization - exact tag matches first + function prioritizeSearchResults(tools, searchTerm) { + if (!searchTerm || !searchTerm.trim()) { + return tools; + } + + const lowerSearchTerm = searchTerm.toLowerCase().trim(); + + return tools.sort((a, b) => { + const aTagsLower = (a.tags || []).map(tag => tag.toLowerCase()); + const bTagsLower = (b.tags || []).map(tag => tag.toLowerCase()); + + const aExactTag = aTagsLower.includes(lowerSearchTerm); + const bExactTag = bTagsLower.includes(lowerSearchTerm); + + // If one has exact tag match and other doesn't, prioritize the exact match + if (aExactTag && !bExactTag) return -1; + if (!aExactTag && bExactTag) return 1; + + // Otherwise maintain original order (or sort by name as secondary) + return a.name.localeCompare(b.name); + }); + } + // Attach to window immediately - BEFORE DOMContentLoaded (window as any).createToolSlug = createToolSlug; (window as any).findToolByIdentifier = findToolByIdentifier; @@ -87,6 +111,7 @@ const { title, description = 'ForensicPathways - A comprehensive directory of di (window as any).scrollToElement = scrollToElement; (window as any).scrollToElementById = scrollToElementById; (window as any).scrollToElementBySelector = scrollToElementBySelector; + (window as any).prioritizeSearchResults = prioritizeSearchResults; document.addEventListener('DOMContentLoaded', () => { const THEME_KEY = 'dfir-theme';