progress
This commit is contained in:
		
							parent
							
								
									921abfb5b9
								
							
						
					
					
						commit
						47eb5ad72a
					
				
							
								
								
									
										21
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										21
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							@ -8,9 +8,11 @@
 | 
				
			|||||||
      "name": "dfir-tools-hub",
 | 
					      "name": "dfir-tools-hub",
 | 
				
			||||||
      "version": "1.0.0",
 | 
					      "version": "1.0.0",
 | 
				
			||||||
      "dependencies": {
 | 
					      "dependencies": {
 | 
				
			||||||
        "astro": "^4.0.0"
 | 
					        "astro": "^4.0.0",
 | 
				
			||||||
 | 
					        "js-yaml": "^4.1.0"
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      "devDependencies": {
 | 
					      "devDependencies": {
 | 
				
			||||||
 | 
					        "@types/node": "^24.0.13",
 | 
				
			||||||
        "terser": "^5.27.0"
 | 
					        "terser": "^5.27.0"
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      "engines": {
 | 
					      "engines": {
 | 
				
			||||||
@ -1673,6 +1675,16 @@
 | 
				
			|||||||
        "@types/unist": "*"
 | 
					        "@types/unist": "*"
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    "node_modules/@types/node": {
 | 
				
			||||||
 | 
					      "version": "24.0.13",
 | 
				
			||||||
 | 
					      "resolved": "https://registry.npmjs.org/@types/node/-/node-24.0.13.tgz",
 | 
				
			||||||
 | 
					      "integrity": "sha512-Qm9OYVOFHFYg3wJoTSrz80hoec5Lia/dPp84do3X7dZvLikQvM1YpmvTBEdIr/e+U8HTkFjLHLnl78K/qjf+jQ==",
 | 
				
			||||||
 | 
					      "devOptional": true,
 | 
				
			||||||
 | 
					      "license": "MIT",
 | 
				
			||||||
 | 
					      "dependencies": {
 | 
				
			||||||
 | 
					        "undici-types": "~7.8.0"
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    "node_modules/@types/unist": {
 | 
					    "node_modules/@types/unist": {
 | 
				
			||||||
      "version": "3.0.3",
 | 
					      "version": "3.0.3",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz",
 | 
				
			||||||
@ -5044,6 +5056,13 @@
 | 
				
			|||||||
        "node": ">=14.17"
 | 
					        "node": ">=14.17"
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    "node_modules/undici-types": {
 | 
				
			||||||
 | 
					      "version": "7.8.0",
 | 
				
			||||||
 | 
					      "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.8.0.tgz",
 | 
				
			||||||
 | 
					      "integrity": "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==",
 | 
				
			||||||
 | 
					      "devOptional": true,
 | 
				
			||||||
 | 
					      "license": "MIT"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    "node_modules/unified": {
 | 
					    "node_modules/unified": {
 | 
				
			||||||
      "version": "11.0.5",
 | 
					      "version": "11.0.5",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.5.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.5.tgz",
 | 
				
			||||||
 | 
				
			|||||||
@ -11,9 +11,11 @@
 | 
				
			|||||||
    "astro": "astro"
 | 
					    "astro": "astro"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "dependencies": {
 | 
					  "dependencies": {
 | 
				
			||||||
    "astro": "^4.0.0"
 | 
					    "astro": "^4.0.0",
 | 
				
			||||||
 | 
					    "js-yaml": "^4.1.0"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "devDependencies": {
 | 
					  "devDependencies": {
 | 
				
			||||||
 | 
					    "@types/node": "^24.0.13",
 | 
				
			||||||
    "terser": "^5.27.0"
 | 
					    "terser": "^5.27.0"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "engines": {
 | 
					  "engines": {
 | 
				
			||||||
 | 
				
			|||||||
@ -5,6 +5,7 @@
 | 
				
			|||||||
<button 
 | 
					<button 
 | 
				
			||||||
  class="btn-icon" 
 | 
					  class="btn-icon" 
 | 
				
			||||||
  data-theme-toggle
 | 
					  data-theme-toggle
 | 
				
			||||||
 | 
					  data-current-theme="auto"
 | 
				
			||||||
  onclick="window.themeUtils.toggleTheme()"
 | 
					  onclick="window.themeUtils.toggleTheme()"
 | 
				
			||||||
  title="Toggle theme"
 | 
					  title="Toggle theme"
 | 
				
			||||||
>
 | 
					>
 | 
				
			||||||
@ -39,12 +40,26 @@
 | 
				
			|||||||
    display: none;
 | 
					    display: none;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  
 | 
					  
 | 
				
			||||||
 | 
					  /* Default to auto icon if no attribute is set */
 | 
				
			||||||
 | 
					  [data-theme-toggle] .theme-icon-auto {
 | 
				
			||||||
 | 
					    display: block;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
  [data-current-theme="light"] .theme-icon-light,
 | 
					  [data-current-theme="light"] .theme-icon-light,
 | 
				
			||||||
  [data-current-theme="dark"] .theme-icon-dark,
 | 
					  [data-current-theme="dark"] .theme-icon-dark,
 | 
				
			||||||
  [data-current-theme="auto"] .theme-icon-auto {
 | 
					  [data-current-theme="auto"] .theme-icon-auto {
 | 
				
			||||||
    display: block;
 | 
					    display: block;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  
 | 
					  
 | 
				
			||||||
 | 
					  [data-current-theme="light"] .theme-icon-dark,
 | 
				
			||||||
 | 
					  [data-current-theme="light"] .theme-icon-auto,
 | 
				
			||||||
 | 
					  [data-current-theme="dark"] .theme-icon-light,
 | 
				
			||||||
 | 
					  [data-current-theme="dark"] .theme-icon-auto,
 | 
				
			||||||
 | 
					  [data-current-theme="auto"] .theme-icon-light,
 | 
				
			||||||
 | 
					  [data-current-theme="auto"] .theme-icon-dark {
 | 
				
			||||||
 | 
					    display: none;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
  [data-current-theme="auto"] path {
 | 
					  [data-current-theme="auto"] path {
 | 
				
			||||||
    fill: currentColor;
 | 
					    fill: currentColor;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										27
									
								
								src/env.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										27
									
								
								src/env.d.ts
									
									
									
									
										vendored
									
									
								
							@ -1 +1,28 @@
 | 
				
			|||||||
/// <reference path="../.astro/types.d.ts" />
 | 
					/// <reference path="../.astro/types.d.ts" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Extend the Window interface to include custom properties
 | 
				
			||||||
 | 
					declare global {
 | 
				
			||||||
 | 
					  interface Window {
 | 
				
			||||||
 | 
					    themeUtils: {
 | 
				
			||||||
 | 
					      initTheme: () => void;
 | 
				
			||||||
 | 
					      toggleTheme: () => void;
 | 
				
			||||||
 | 
					      getStoredTheme: () => string;
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					    toolsData: any[];
 | 
				
			||||||
 | 
					    showToolDetails: (toolName: string) => void;
 | 
				
			||||||
 | 
					    hideToolDetails: () => void;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  // Custom event types
 | 
				
			||||||
 | 
					  interface WindowEventMap {
 | 
				
			||||||
 | 
					    'toolsFiltered': CustomEvent<any[]>;
 | 
				
			||||||
 | 
					    'viewChanged': CustomEvent<string>;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Also declare the modules that might not be recognized
 | 
				
			||||||
 | 
					declare module 'js-yaml' {
 | 
				
			||||||
 | 
					  export function load(str: string): any;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export {};
 | 
				
			||||||
@ -22,7 +22,7 @@ const { title, description = 'DFIR Tools Hub - A comprehensive directory of digi
 | 
				
			|||||||
  <script src="/src/scripts/theme.js"></script>
 | 
					  <script src="/src/scripts/theme.js"></script>
 | 
				
			||||||
  <script>
 | 
					  <script>
 | 
				
			||||||
    // Initialize theme immediately to prevent flash
 | 
					    // Initialize theme immediately to prevent flash
 | 
				
			||||||
    window.themeUtils.initTheme();
 | 
					    (window as any).themeUtils?.initTheme();
 | 
				
			||||||
  </script>
 | 
					  </script>
 | 
				
			||||||
</head>
 | 
					</head>
 | 
				
			||||||
<body>
 | 
					<body>
 | 
				
			||||||
 | 
				
			|||||||
@ -54,12 +54,19 @@ const tools = data.tools;
 | 
				
			|||||||
    const matrixContainer = document.getElementById('matrix-container');
 | 
					    const matrixContainer = document.getElementById('matrix-container');
 | 
				
			||||||
    const noResults = document.getElementById('no-results');
 | 
					    const noResults = document.getElementById('no-results');
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
 | 
					    // Guard against null elements
 | 
				
			||||||
 | 
					    if (!toolsContainer || !toolsGrid || !matrixContainer || !noResults) {
 | 
				
			||||||
 | 
					      console.error('Required DOM elements not found');
 | 
				
			||||||
 | 
					      return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
    // Initial tools HTML
 | 
					    // Initial tools HTML
 | 
				
			||||||
    const initialToolsHTML = toolsContainer.innerHTML;
 | 
					    const initialToolsHTML = toolsContainer.innerHTML;
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    // Handle filtered results
 | 
					    // Handle filtered results
 | 
				
			||||||
    window.addEventListener('toolsFiltered', (event) => {
 | 
					    window.addEventListener('toolsFiltered', (event: Event) => {
 | 
				
			||||||
      const filtered = event.detail;
 | 
					      const customEvent = event as CustomEvent;
 | 
				
			||||||
 | 
					      const filtered = customEvent.detail;
 | 
				
			||||||
      const currentView = document.querySelector('.view-toggle.active')?.getAttribute('data-view');
 | 
					      const currentView = document.querySelector('.view-toggle.active')?.getAttribute('data-view');
 | 
				
			||||||
      
 | 
					      
 | 
				
			||||||
      if (currentView === 'matrix') {
 | 
					      if (currentView === 'matrix') {
 | 
				
			||||||
@ -76,7 +83,7 @@ const tools = data.tools;
 | 
				
			|||||||
        noResults.style.display = 'none';
 | 
					        noResults.style.display = 'none';
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        // Render filtered tools
 | 
					        // Render filtered tools
 | 
				
			||||||
        filtered.forEach(tool => {
 | 
					        filtered.forEach((tool: any) => {
 | 
				
			||||||
          const toolCard = createToolCard(tool);
 | 
					          const toolCard = createToolCard(tool);
 | 
				
			||||||
          toolsContainer.appendChild(toolCard);
 | 
					          toolsContainer.appendChild(toolCard);
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
@ -84,8 +91,9 @@ const tools = data.tools;
 | 
				
			|||||||
    });
 | 
					    });
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    // Handle view changes
 | 
					    // Handle view changes
 | 
				
			||||||
    window.addEventListener('viewChanged', (event) => {
 | 
					    window.addEventListener('viewChanged', (event: Event) => {
 | 
				
			||||||
      const view = event.detail;
 | 
					      const customEvent = event as CustomEvent;
 | 
				
			||||||
 | 
					      const view = customEvent.detail;
 | 
				
			||||||
      
 | 
					      
 | 
				
			||||||
      if (view === 'matrix') {
 | 
					      if (view === 'matrix') {
 | 
				
			||||||
        toolsGrid.style.display = 'none';
 | 
					        toolsGrid.style.display = 'none';
 | 
				
			||||||
@ -97,7 +105,7 @@ const tools = data.tools;
 | 
				
			|||||||
    });
 | 
					    });
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    // Create tool card element
 | 
					    // Create tool card element
 | 
				
			||||||
    function createToolCard(tool) {
 | 
					    function createToolCard(tool: any): HTMLElement {
 | 
				
			||||||
      const cardDiv = document.createElement('div');
 | 
					      const cardDiv = document.createElement('div');
 | 
				
			||||||
      const cardClass = tool.isHosted ? 'card card-hosted' : (tool.license !== 'Proprietary' ? 'card card-oss' : 'card');
 | 
					      const cardClass = tool.isHosted ? 'card card-hosted' : (tool.license !== 'Proprietary' ? 'card card-oss' : 'card');
 | 
				
			||||||
      cardDiv.className = cardClass;
 | 
					      cardDiv.className = cardClass;
 | 
				
			||||||
@ -149,7 +157,7 @@ const tools = data.tools;
 | 
				
			|||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        <div style="display: flex; flex-wrap: wrap; gap: 0.25rem; margin-bottom: 1rem;">
 | 
					        <div style="display: flex; flex-wrap: wrap; gap: 0.25rem; margin-bottom: 1rem;">
 | 
				
			||||||
          ${tool.tags.map(tag => `<span class="tag">${tag}</span>`).join('')}
 | 
					          ${tool.tags.map((tag: string) => `<span class="tag">${tag}</span>`).join('')}
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        <a href="${tool.url}" target="_blank" rel="noopener noreferrer" class="btn btn-primary" style="width: 100%;">
 | 
					        <a href="${tool.url}" target="_blank" rel="noopener noreferrer" class="btn btn-primary" style="width: 100%;">
 | 
				
			||||||
 | 
				
			|||||||
@ -17,15 +17,20 @@ function applyTheme(theme) {
 | 
				
			|||||||
  document.documentElement.setAttribute('data-theme', effectiveTheme);
 | 
					  document.documentElement.setAttribute('data-theme', effectiveTheme);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Update theme toggle button state
 | 
				
			||||||
 | 
					function updateThemeToggle(theme) {
 | 
				
			||||||
 | 
					  document.querySelectorAll('[data-theme-toggle]').forEach(button => {
 | 
				
			||||||
 | 
					    button.setAttribute('data-current-theme', theme);
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Initialize theme on page load
 | 
					// Initialize theme on page load
 | 
				
			||||||
function initTheme() {
 | 
					function initTheme() {
 | 
				
			||||||
  const storedTheme = getStoredTheme();
 | 
					  const storedTheme = getStoredTheme();
 | 
				
			||||||
  applyTheme(storedTheme);
 | 
					  applyTheme(storedTheme);
 | 
				
			||||||
  
 | 
					  
 | 
				
			||||||
  // Update theme toggle buttons
 | 
					  // Update theme toggle buttons immediately
 | 
				
			||||||
  document.querySelectorAll('[data-theme-toggle]').forEach(button => {
 | 
					  updateThemeToggle(storedTheme);
 | 
				
			||||||
    button.setAttribute('data-current-theme', storedTheme);
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Handle theme toggle
 | 
					// Handle theme toggle
 | 
				
			||||||
@ -38,11 +43,7 @@ function toggleTheme() {
 | 
				
			|||||||
  
 | 
					  
 | 
				
			||||||
  localStorage.setItem(THEME_KEY, nextTheme);
 | 
					  localStorage.setItem(THEME_KEY, nextTheme);
 | 
				
			||||||
  applyTheme(nextTheme);
 | 
					  applyTheme(nextTheme);
 | 
				
			||||||
  
 | 
					  updateThemeToggle(nextTheme);
 | 
				
			||||||
  // Update all theme toggle buttons
 | 
					 | 
				
			||||||
  document.querySelectorAll('[data-theme-toggle]').forEach(button => {
 | 
					 | 
				
			||||||
    button.setAttribute('data-current-theme', nextTheme);
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Listen for system theme changes
 | 
					// Listen for system theme changes
 | 
				
			||||||
@ -52,6 +53,12 @@ window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', ()
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Initialize immediately when script loads
 | 
				
			||||||
 | 
					initTheme();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Also initialize when DOM is ready (for safety)
 | 
				
			||||||
 | 
					document.addEventListener('DOMContentLoaded', initTheme);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Export functions for use in Astro components
 | 
					// Export functions for use in Astro components
 | 
				
			||||||
window.themeUtils = {
 | 
					window.themeUtils = {
 | 
				
			||||||
  initTheme,
 | 
					  initTheme,
 | 
				
			||||||
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user