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