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", "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",

View File

@ -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": {

View File

@ -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
View File

@ -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 {};

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 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>

View File

@ -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%;">

View File

@ -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,