deduplication
This commit is contained in:
		
							parent
							
								
									050774ad99
								
							
						
					
					
						commit
						9ce2098439
					
				
							
								
								
									
										20110
									
								
								data/embeddings.json
									
									
									
									
									
								
							
							
						
						
									
										20110
									
								
								data/embeddings.json
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@ -2,7 +2,6 @@
 | 
				
			|||||||
// src/components/AIQueryInterface.astro
 | 
					// src/components/AIQueryInterface.astro
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { getToolsData } from '../utils/dataService.js';
 | 
					import { getToolsData } from '../utils/dataService.js';
 | 
				
			||||||
import { isToolHosted } from '../utils/toolHelpers.js';
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
const data = await getToolsData();
 | 
					const data = await getToolsData();
 | 
				
			||||||
const tools = data.tools;
 | 
					const tools = data.tools;
 | 
				
			||||||
 | 
				
			|||||||
@ -22,36 +22,26 @@ const { title, description = 'ForensicPathways - A comprehensive directory of di
 | 
				
			|||||||
  <link rel="icon" type="image/x-icon" href="/favicon.ico">
 | 
					  <link rel="icon" type="image/x-icon" href="/favicon.ico">
 | 
				
			||||||
  
 | 
					  
 | 
				
			||||||
<script>
 | 
					<script>
 | 
				
			||||||
  function createToolSlug(toolName) {
 | 
					  // Import utility functions from shared client module instead of duplicating
 | 
				
			||||||
    if (!toolName || typeof toolName !== 'string') {
 | 
					  async function loadUtilityFunctions() {
 | 
				
			||||||
      console.warn('[toolHelpers] Invalid toolName provided to createToolSlug:', toolName);
 | 
					    try {
 | 
				
			||||||
      return '';
 | 
					      const { createToolSlug, findToolByIdentifier, isToolHosted } = await import('../utils/clientUtils.js');
 | 
				
			||||||
 | 
					      
 | 
				
			||||||
 | 
					      // Make functions available globally for backward compatibility
 | 
				
			||||||
 | 
					      (window as any).createToolSlug = createToolSlug;
 | 
				
			||||||
 | 
					      (window as any).findToolByIdentifier = findToolByIdentifier;
 | 
				
			||||||
 | 
					      (window as any).isToolHosted = isToolHosted;
 | 
				
			||||||
 | 
					    } catch (error) {
 | 
				
			||||||
 | 
					      console.error('Failed to load utility functions:', error);
 | 
				
			||||||
 | 
					      // Minimal fallback for critical functionality only
 | 
				
			||||||
 | 
					      (window as any).createToolSlug = (toolName: string) => {
 | 
				
			||||||
 | 
					        if (!toolName || typeof toolName !== 'string') return '';
 | 
				
			||||||
 | 
					        return toolName.toLowerCase().replace(/[^a-z0-9\s-]/g, '').replace(/\s+/g, '-').replace(/-+/g, '-').replace(/^-|-$/g, '');
 | 
				
			||||||
 | 
					      };
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    return toolName.toLowerCase()
 | 
					 | 
				
			||||||
      .replace(/[^a-z0-9\s-]/g, '')     // Remove special characters
 | 
					 | 
				
			||||||
      .replace(/\s+/g, '-')             // Replace spaces with hyphens
 | 
					 | 
				
			||||||
      .replace(/-+/g, '-')              // Remove duplicate hyphens
 | 
					 | 
				
			||||||
      .replace(/^-|-$/g, '');           // Remove leading/trailing hyphens
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  function findToolByIdentifier(tools, identifier) {
 | 
					  function scrollToElement(element: Element | null, options = {}) {
 | 
				
			||||||
    if (!identifier || !Array.isArray(tools)) return undefined;
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    return tools.find(tool => 
 | 
					 | 
				
			||||||
      tool.name === identifier || 
 | 
					 | 
				
			||||||
      createToolSlug(tool.name) === identifier.toLowerCase()
 | 
					 | 
				
			||||||
    );
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  function isToolHosted(tool) {
 | 
					 | 
				
			||||||
    return tool.projectUrl !== undefined && 
 | 
					 | 
				
			||||||
           tool.projectUrl !== null && 
 | 
					 | 
				
			||||||
           tool.projectUrl !== "" && 
 | 
					 | 
				
			||||||
           tool.projectUrl.trim() !== "";
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  function scrollToElement(element, options = {}) {
 | 
					 | 
				
			||||||
    if (!element) return;
 | 
					    if (!element) return;
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    setTimeout(() => {
 | 
					    setTimeout(() => {
 | 
				
			||||||
@ -67,17 +57,21 @@ const { title, description = 'ForensicPathways - A comprehensive directory of di
 | 
				
			|||||||
    }, 100);
 | 
					    }, 100);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  function scrollToElementById(elementId, options = {}) {
 | 
					  function scrollToElementById(elementId: string, options = {}) {
 | 
				
			||||||
    const element = document.getElementById(elementId);
 | 
					    const element = document.getElementById(elementId);
 | 
				
			||||||
    scrollToElement(element, options);
 | 
					    if (element) {
 | 
				
			||||||
 | 
					      scrollToElement(element, options);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  function scrollToElementBySelector(selector, options = {}) {
 | 
					  function scrollToElementBySelector(selector: string, options = {}) {
 | 
				
			||||||
    const element = document.querySelector(selector);
 | 
					    const element = document.querySelector(selector);
 | 
				
			||||||
    scrollToElement(element, options);
 | 
					    if (element) {
 | 
				
			||||||
 | 
					      scrollToElement(element, options);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  function prioritizeSearchResults(tools, searchTerm) {
 | 
					  function prioritizeSearchResults(tools: any[], searchTerm: string) {
 | 
				
			||||||
    if (!searchTerm || !searchTerm.trim()) {
 | 
					    if (!searchTerm || !searchTerm.trim()) {
 | 
				
			||||||
      return tools;
 | 
					      return tools;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -85,8 +79,8 @@ const { title, description = 'ForensicPathways - A comprehensive directory of di
 | 
				
			|||||||
    const lowerSearchTerm = searchTerm.toLowerCase().trim();
 | 
					    const lowerSearchTerm = searchTerm.toLowerCase().trim();
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    return tools.sort((a, b) => {
 | 
					    return tools.sort((a, b) => {
 | 
				
			||||||
      const aTagsLower = (a.tags || []).map(tag => tag.toLowerCase());
 | 
					      const aTagsLower = (a.tags || []).map((tag: string) => tag.toLowerCase());
 | 
				
			||||||
      const bTagsLower = (b.tags || []).map(tag => tag.toLowerCase());
 | 
					      const bTagsLower = (b.tags || []).map((tag: string) => tag.toLowerCase());
 | 
				
			||||||
      
 | 
					      
 | 
				
			||||||
      const aExactTag = aTagsLower.includes(lowerSearchTerm);
 | 
					      const aExactTag = aTagsLower.includes(lowerSearchTerm);
 | 
				
			||||||
      const bExactTag = bTagsLower.includes(lowerSearchTerm);
 | 
					      const bExactTag = bTagsLower.includes(lowerSearchTerm);
 | 
				
			||||||
@ -98,15 +92,16 @@ const { title, description = 'ForensicPathways - A comprehensive directory of di
 | 
				
			|||||||
    });
 | 
					    });
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  (window as any).createToolSlug = createToolSlug;
 | 
					  // Set non-duplicated functions on window
 | 
				
			||||||
  (window as any).findToolByIdentifier = findToolByIdentifier;
 | 
					 | 
				
			||||||
  (window as any).isToolHosted = isToolHosted;
 | 
					 | 
				
			||||||
  (window as any).scrollToElement = scrollToElement;
 | 
					  (window as any).scrollToElement = scrollToElement;
 | 
				
			||||||
  (window as any).scrollToElementById = scrollToElementById;
 | 
					  (window as any).scrollToElementById = scrollToElementById;
 | 
				
			||||||
  (window as any).scrollToElementBySelector = scrollToElementBySelector;
 | 
					  (window as any).scrollToElementBySelector = scrollToElementBySelector;
 | 
				
			||||||
  (window as any).prioritizeSearchResults = prioritizeSearchResults;
 | 
					  (window as any).prioritizeSearchResults = prioritizeSearchResults;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  document.addEventListener('DOMContentLoaded', () => {
 | 
					  document.addEventListener('DOMContentLoaded', () => {
 | 
				
			||||||
 | 
					    // Load utility functions first
 | 
				
			||||||
 | 
					    loadUtilityFunctions();
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
    const THEME_KEY = 'dfir-theme';
 | 
					    const THEME_KEY = 'dfir-theme';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    function getSystemTheme() {
 | 
					    function getSystemTheme() {
 | 
				
			||||||
@ -117,12 +112,12 @@ const { title, description = 'ForensicPathways - A comprehensive directory of di
 | 
				
			|||||||
      return localStorage.getItem(THEME_KEY) || 'auto';
 | 
					      return localStorage.getItem(THEME_KEY) || 'auto';
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    function applyTheme(theme) {
 | 
					    function applyTheme(theme: string) {
 | 
				
			||||||
      const effectiveTheme = theme === 'auto' ? getSystemTheme() : theme;
 | 
					      const effectiveTheme = theme === 'auto' ? getSystemTheme() : theme;
 | 
				
			||||||
      document.documentElement.setAttribute('data-theme', effectiveTheme);
 | 
					      document.documentElement.setAttribute('data-theme', effectiveTheme);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    function updateThemeToggle(theme) {
 | 
					    function updateThemeToggle(theme: string) {
 | 
				
			||||||
      document.querySelectorAll('[data-theme-toggle]').forEach(button => {
 | 
					      document.querySelectorAll('[data-theme-toggle]').forEach(button => {
 | 
				
			||||||
        button.setAttribute('data-current-theme', theme);
 | 
					        button.setAttribute('data-current-theme', theme);
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
@ -192,7 +187,7 @@ const { title, description = 'ForensicPathways - A comprehensive directory of di
 | 
				
			|||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async function requireClientAuth(callback, returnUrl, context = 'general') {
 | 
					    async function requireClientAuth(callback: () => void, returnUrl: string, context = 'general') {
 | 
				
			||||||
      const authStatus = await checkClientAuth(context);
 | 
					      const authStatus = await checkClientAuth(context);
 | 
				
			||||||
      
 | 
					      
 | 
				
			||||||
      if (authStatus.authRequired && !authStatus.authenticated) {
 | 
					      if (authStatus.authRequired && !authStatus.authenticated) {
 | 
				
			||||||
@ -207,12 +202,12 @@ const { title, description = 'ForensicPathways - A comprehensive directory of di
 | 
				
			|||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async function showIfAuthenticated(selector, context = 'general') {
 | 
					    async function showIfAuthenticated(selector: string, context = 'general') {
 | 
				
			||||||
      const authStatus = await checkClientAuth(context);
 | 
					      const authStatus = await checkClientAuth(context);
 | 
				
			||||||
      const element = document.querySelector(selector);
 | 
					      const element = document.querySelector(selector);
 | 
				
			||||||
      
 | 
					      
 | 
				
			||||||
      if (element) {
 | 
					      if (element) {
 | 
				
			||||||
        element.style.display = (!authStatus.authRequired || authStatus.authenticated) 
 | 
					        (element as HTMLElement).style.display = (!authStatus.authRequired || authStatus.authenticated) 
 | 
				
			||||||
          ? 'inline-flex' 
 | 
					          ? 'inline-flex' 
 | 
				
			||||||
          : 'none';
 | 
					          : 'none';
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										31
									
								
								src/utils/clientUtils.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								src/utils/clientUtils.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,31 @@
 | 
				
			|||||||
 | 
					// src/utils/clientUtils.js
 | 
				
			||||||
 | 
					// Client-side utilities that mirror server-side toolHelpers.ts
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function createToolSlug(toolName) {
 | 
				
			||||||
 | 
					  if (!toolName || typeof toolName !== 'string') {
 | 
				
			||||||
 | 
					    console.warn('[toolHelpers] Invalid toolName provided to createToolSlug:', toolName);
 | 
				
			||||||
 | 
					    return '';
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  return toolName.toLowerCase()
 | 
				
			||||||
 | 
					    .replace(/[^a-z0-9\s-]/g, '')     // Remove special characters
 | 
				
			||||||
 | 
					    .replace(/\s+/g, '-')             // Replace spaces with hyphens
 | 
				
			||||||
 | 
					    .replace(/-+/g, '-')              // Remove duplicate hyphens
 | 
				
			||||||
 | 
					    .replace(/^-|-$/g, '');           // Remove leading/trailing hyphens
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function findToolByIdentifier(tools, identifier) {
 | 
				
			||||||
 | 
					  if (!identifier || !Array.isArray(tools)) return undefined;
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  return tools.find(tool => 
 | 
				
			||||||
 | 
					    tool.name === identifier || 
 | 
				
			||||||
 | 
					    createToolSlug(tool.name) === identifier.toLowerCase()
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function isToolHosted(tool) {
 | 
				
			||||||
 | 
					  return tool.projectUrl !== undefined && 
 | 
				
			||||||
 | 
					         tool.projectUrl !== null && 
 | 
				
			||||||
 | 
					         tool.projectUrl !== "" && 
 | 
				
			||||||
 | 
					         tool.projectUrl.trim() !== "";
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user