This commit is contained in:
overcuriousity 2025-07-14 15:15:57 +02:00
parent 921abfb5b9
commit 47eb5ad72a
7 changed files with 99 additions and 21 deletions

21
package-lock.json generated
View File

@ -8,9 +8,11 @@
"name": "dfir-tools-hub",
"version": "1.0.0",
"dependencies": {
"astro": "^4.0.0"
"astro": "^4.0.0",
"js-yaml": "^4.1.0"
},
"devDependencies": {
"@types/node": "^24.0.13",
"terser": "^5.27.0"
},
"engines": {
@ -1673,6 +1675,16 @@
"@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": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz",
@ -5044,6 +5056,13 @@
"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": {
"version": "11.0.5",
"resolved": "https://registry.npmjs.org/unified/-/unified-11.0.5.tgz",

View File

@ -11,9 +11,11 @@
"astro": "astro"
},
"dependencies": {
"astro": "^4.0.0"
"astro": "^4.0.0",
"js-yaml": "^4.1.0"
},
"devDependencies": {
"@types/node": "^24.0.13",
"terser": "^5.27.0"
},
"engines": {

View File

@ -5,6 +5,7 @@
<button
class="btn-icon"
data-theme-toggle
data-current-theme="auto"
onclick="window.themeUtils.toggleTheme()"
title="Toggle theme"
>
@ -39,12 +40,26 @@
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="dark"] .theme-icon-dark,
[data-current-theme="auto"] .theme-icon-auto {
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 {
fill: currentColor;
}

27
src/env.d.ts vendored
View File

@ -1 +1,28 @@
/// <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 {};

View File

@ -22,7 +22,7 @@ const { title, description = 'DFIR Tools Hub - A comprehensive directory of digi
<script src="/src/scripts/theme.js"></script>
<script>
// Initialize theme immediately to prevent flash
window.themeUtils.initTheme();
(window as any).themeUtils?.initTheme();
</script>
</head>
<body>

View File

@ -54,12 +54,19 @@ const tools = data.tools;
const matrixContainer = document.getElementById('matrix-container');
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
const initialToolsHTML = toolsContainer.innerHTML;
// Handle filtered results
window.addEventListener('toolsFiltered', (event) => {
const filtered = event.detail;
window.addEventListener('toolsFiltered', (event: Event) => {
const customEvent = event as CustomEvent;
const filtered = customEvent.detail;
const currentView = document.querySelector('.view-toggle.active')?.getAttribute('data-view');
if (currentView === 'matrix') {
@ -76,7 +83,7 @@ const tools = data.tools;
noResults.style.display = 'none';
// Render filtered tools
filtered.forEach(tool => {
filtered.forEach((tool: any) => {
const toolCard = createToolCard(tool);
toolsContainer.appendChild(toolCard);
});
@ -84,8 +91,9 @@ const tools = data.tools;
});
// Handle view changes
window.addEventListener('viewChanged', (event) => {
const view = event.detail;
window.addEventListener('viewChanged', (event: Event) => {
const customEvent = event as CustomEvent;
const view = customEvent.detail;
if (view === 'matrix') {
toolsGrid.style.display = 'none';
@ -97,7 +105,7 @@ const tools = data.tools;
});
// Create tool card element
function createToolCard(tool) {
function createToolCard(tool: any): HTMLElement {
const cardDiv = document.createElement('div');
const cardClass = tool.isHosted ? 'card card-hosted' : (tool.license !== 'Proprietary' ? 'card card-oss' : 'card');
cardDiv.className = cardClass;
@ -149,7 +157,7 @@ const tools = data.tools;
</div>
<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>
<a href="${tool.url}" target="_blank" rel="noopener noreferrer" class="btn btn-primary" style="width: 100%;">

View File

@ -17,15 +17,20 @@ function applyTheme(theme) {
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
function initTheme() {
const storedTheme = getStoredTheme();
applyTheme(storedTheme);
// Update theme toggle buttons
document.querySelectorAll('[data-theme-toggle]').forEach(button => {
button.setAttribute('data-current-theme', storedTheme);
});
// Update theme toggle buttons immediately
updateThemeToggle(storedTheme);
}
// Handle theme toggle
@ -38,11 +43,7 @@ function toggleTheme() {
localStorage.setItem(THEME_KEY, nextTheme);
applyTheme(nextTheme);
// Update all theme toggle buttons
document.querySelectorAll('[data-theme-toggle]').forEach(button => {
button.setAttribute('data-current-theme', nextTheme);
});
updateThemeToggle(nextTheme);
}
// 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
window.themeUtils = {
initTheme,