overcuriousity 507e57cdd9 fixes
2025-08-06 16:38:42 +02:00

628 lines
23 KiB
Plaintext
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
import BaseLayout from '../layouts/BaseLayout.astro';
import ToolCard from '../components/ToolCard.astro';
import ToolFilters from '../components/ToolFilters.astro';
import ToolMatrix from '../components/ToolMatrix.astro';
import AIQueryInterface from '../components/AIQueryInterface.astro';
import TargetedScenarios from '../components/TargetedScenarios.astro';
import { getToolsData } from '../utils/dataService.js';
const data = await getToolsData();
const tools = data.tools;
const phases = data.phases;
---
<BaseLayout title="~/">
<section class="approach-hero">
<div class="approach-content">
<h1>ForensicPathways</h1>
<p class="hero-tagline">Das richtige Werkzeug zur richtigen Zeit</p>
<p class="hero-subtitle">
Systematische digitale Forensik nach bewährter NIST SP 800-86 Methodik.<br>
Wählen Sie Ihren Ansatz für die Werkzeugauswahl:
</p>
<div class="ai-hero-spotlight">
<div class="ai-spotlight-content">
<div class="ai-spotlight-icon">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M9 11H5a2 2 0 0 0-2 2v7a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7a2 2 0 0 0-2-2h-4"/>
<path d="M9 11V7a3 3 0 0 1 6 0v4"/>
<circle cx="12" cy="12" r="2"/>
</svg>
</div>
<div class="ai-spotlight-text">
<h3>Forensic AI-Beratung</h3>
<p>Analyse des Untersuchungsszenarios mit Empfehlungen zum Vorgehen</p>
</div>
</div>
<button id="ai-query-btn" class="btn btn-accent btn-lg ai-primary-btn">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M9 11H5a2 2 0 0 0-2 2v7a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7a2 2 0 0 0-2-2h-4"/>
<path d="M9 11V7a3 3 0 0 1 6 0v4"/>
</svg>
KI-Beratung starten
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" class="ml-2">
<line x1="7" y1="17" x2="17" y2="7"/>
<polyline points="7,7 17,7 17,17"/>
</svg>
</button>
<div class="ai-features-mini">
<span class="badge badge-secondary">Workflow-Empfehlungen</span>
<span class="badge badge-secondary">Transparenz</span>
<span class="badge badge-secondary">Sofortige Analyse</span>
</div>
</div>
<div class="approach-selector">
<div class="approach-card methodology" onclick="selectApproach('methodology')">
<div class="approach-header">
<div class="approach-icon">🔍</div>
<h3>Vollständige Ermittlung</h3>
</div>
<p class="approach-description">
Systematisches Vorgehen durch alle vier NIST-Phasen einer forensischen Untersuchung
</p>
<ul class="approach-features">
<li>Methodische Schritt-für-Schritt Anleitung</li>
<li>Vollständige Dokumentationskette</li>
<li>Rechtssichere Beweisführung</li>
<li>Ideal für komplexe Fälle</li>
</ul>
</div>
<div class="approach-card targeted" onclick="selectApproach('targeted')">
<div class="approach-header">
<div class="approach-icon">🎯</div>
<h3>Gezieltes Problem lösen</h3>
</div>
<p class="approach-description">
Direkter Zugang zu spezifischen Tools und Methoden für bekannte Anforderungen
</p>
<ul class="approach-features">
<li>Schnelle Tool-Suche</li>
<li>Spezifische Problemlösungen</li>
<li>Erfahrene Anwender</li>
<li>Effizient für Einzelaufgaben</li>
</ul>
</div>
</div>
<div class="approach-actions">
<p class="approach-info">
<span class="info-icon"></span>
Die lila gekennzeichneten Einträge sind über das Single-Sign-On der CC24-Cloud direkt zugänglich.
Teilnehmer der Seminargruppe CC24-w1 (oder andere Berechtigte) können die gehostete Infrastruktur nutzen.
<a href="/about#support">Kontakt bei Problemen</a>
</p>
<div class="quick-actions">
<a href="/about" class="btn btn-secondary">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<circle cx="12" cy="12" r="10"></circle>
<path d="M12 16v-4"></path>
<path d="M12 8h.01"></path>
</svg>
Infos, SSO & Zugang
</a>
<a href="/contribute" class="btn" style="background-color: var(--color-warning); color: white; border-color: var(--color-warning);" data-contribute-button="new">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M16 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/>
<circle cx="8.5" cy="7" r="4"/>
<line x1="20" y1="8" x2="20" y2="14"/>
<line x1="23" y1="11" x2="17" y2="11"/>
</svg>
Beitragen
</a>
<button onclick="window.scrollToElementById('filters-section')" class="btn btn-secondary">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"></path>
<polyline points="3.27 6.96 12 12.01 20.73 6.96"></polyline>
<line x1="12" y1="22.08" x2="12" y2="12"></line>
</svg>
Direkt zur Suche
</a>
</div>
</div>
</div>
</section>
<!-- NIST Methodology Section -->
<section id="methodology-section" class="methodology-section">
<div class="methodology-header">
<h3>NIST SP 800-86 Forensische Methodik</h3>
<p class="methodology-subtitle">
Wählen Sie eine Phase aus dem bewährten Vier-Phasen-Modell
</p>
</div>
<div class="nist-workflow">
{phases.map((phase: any, index: number) => {
const phaseTools = tools.filter((tool: any) =>
tool.phases && tool.phases.includes(phase.id)
);
return (
<div class={`phase-card phase-${phase.id}`} onclick={`selectPhase('${phase.id}')`}>
<div class="phase-number">{index + 1}</div>
<div class="phase-title">{phase.name}</div>
<p class="phase-description">
{phase.description}
</p>
<span class="tool-count">{phaseTools.length} Tools</span>
</div>
);
})}
</div>
<div class="methodology-tip">
<p>
<strong>Tipp:</strong> Für komplexe Ermittlungen empfiehlt sich das sequenzielle Durchlaufen aller Phasen.
Jede Phase baut methodisch auf der vorherigen auf.
</p>
</div>
</section>
<TargetedScenarios />
<section id="filters-section" class="section">
<div class="content-center-lg">
<h3 style="color: var(--color-text); margin-bottom: 0.5rem;">Alle verfügbaren Werkzeuge durchsuchen</h3>
<p style="color: var(--color-text-secondary); margin: 0;">
Nutzen Sie die erweiterten Filter und Kategorien für eine detaillierte Suche
</p>
</div>
<ToolFilters data={data} />
</section>
<AIQueryInterface />
<section id="tools-grid" style="padding-bottom: 2rem;">
<div class="grid-auto-fit" id="tools-container">
{tools.map((tool: any) => (
<ToolCard tool={tool} />
))}
</div>
<div id="no-results" style="display: none; text-align: center; padding: 4rem 0;">
<p class="text-muted" style="font-size: 1.125rem;">No tools found matching your criteria.</p>
</div>
</section>
<ToolMatrix data={data} />
</BaseLayout>
<script define:vars={{ toolsData: data.tools, phases: data.phases }}>
window.toolsData = toolsData;
// CONSOLIDATED: Approach selection - Pure navigation aid
window.selectApproach = function(approach) {
console.log(`Selected approach: ${approach}`);
// Clear any existing AI results
const aiResults = document.getElementById('ai-results');
if (aiResults) aiResults.style.display = 'none';
// Update visual selection state
document.querySelectorAll('.approach-card').forEach(card => {
card.classList.remove('selected');
});
const selectedCard = document.querySelector(`.approach-card.${approach}`);
if (selectedCard) selectedCard.classList.add('selected');
// Hide all approach sections first (ensures mutual exclusivity)
const methodologySection = document.getElementById('methodology-section');
const targetedSection = document.getElementById('targeted-section');
if (methodologySection) methodologySection.classList.remove('active');
if (targetedSection) targetedSection.classList.remove('active');
// Show the selected approach section (navigation aid only)
if (approach === 'methodology') {
if (methodologySection) {
methodologySection.classList.add('active');
window.scrollToElementById('methodology-section');
}
} else if (approach === 'targeted') {
if (targetedSection) {
targetedSection.classList.add('active');
window.scrollToElementById('targeted-section');
}
}
};
// CONSOLIDATED: Phase selection - Sets unified filter dropdown
window.selectPhase = function(phase) {
console.log(`Selected NIST phase: ${phase}`);
// Update visual selection of phase cards
document.querySelectorAll('.phase-card').forEach(card => {
card.classList.remove('active');
});
const selectedCard = document.querySelector(`.phase-card.phase-${phase}`);
if (selectedCard) {
selectedCard.classList.add('active');
}
// CONSOLIDATED: Set the unified phase-select dropdown
const phaseSelect = document.getElementById('phase-select');
if (phaseSelect) {
phaseSelect.value = phase;
// Trigger the change event to activate unified filtering
const changeEvent = new Event('change', { bubbles: true });
phaseSelect.dispatchEvent(changeEvent);
}
// Switch to grid view to show filtered results
const gridToggle = document.querySelector('.view-toggle[data-view="grid"]');
if (gridToggle && !gridToggle.classList.contains('active')) {
gridToggle.click();
}
// Scroll to filtered results
setTimeout(() => {
window.scrollToElementById('tools-grid');
}, 200);
};
document.addEventListener('DOMContentLoaded', () => {
const toolsContainer = document.getElementById('tools-container');
const toolsGrid = document.getElementById('tools-grid');
const matrixContainer = document.getElementById('matrix-container');
const aiInterface = document.getElementById('ai-interface');
const filtersSection = document.getElementById('filters-section');
const noResults = document.getElementById('no-results');
const aiQueryBtn = document.getElementById('ai-query-btn');
if (!toolsContainer || !toolsGrid || !matrixContainer || !noResults || !aiInterface || !filtersSection) {
console.error('Required DOM elements not found');
return;
}
if (aiQueryBtn) {
aiQueryBtn.addEventListener('click', () => {
aiQueryBtn.classList.add('activated');
setTimeout(() => aiQueryBtn.classList.remove('activated'), 400);
switchToView('ai');
window.dispatchEvent(new CustomEvent('viewChanged', { detail: 'ai' }));
if (window.scrollToElementById) {
window.scrollToElementById('ai-interface');
} else {
aiInterface.scrollIntoView({ behavior: 'smooth', block: 'start' });
}
});
}
function switchToView(view) {
const toolsGrid = document.getElementById('tools-grid');
const matrixContainer = document.getElementById('matrix-container');
const aiInterface = document.getElementById('ai-interface');
const filtersSection = document.getElementById('filters-section');
const noResults = document.getElementById('no-results');
// FIXED: Hide approach sections when switching to ANY view mode
const methodologySection = document.getElementById('methodology-section');
const targetedSection = document.getElementById('targeted-section');
// Hide all main content areas
if (toolsGrid) toolsGrid.style.display = 'none';
if (matrixContainer) matrixContainer.style.display = 'none';
if (aiInterface) aiInterface.style.display = 'none';
if (noResults) noResults.style.display = 'none';
// FIXED: Hide approach sections when switching to view modes
if (methodologySection) methodologySection.classList.remove('active');
if (targetedSection) targetedSection.classList.remove('active');
switch (view) {
case 'grid':
if (toolsGrid) toolsGrid.style.display = 'block';
if (filtersSection) filtersSection.style.display = 'block';
break;
case 'matrix':
if (matrixContainer) matrixContainer.style.display = 'block';
if (filtersSection) filtersSection.style.display = 'block';
break;
case 'ai':
if (aiInterface) aiInterface.style.display = 'block';
// FIXED: Show filters but hide everything except view controls
if (filtersSection) {
filtersSection.style.display = 'block';
// Hide all filter sections except the last one (view controls)
const filterSections = filtersSection.querySelectorAll('.filter-section');
filterSections.forEach((section, index) => {
if (index === filterSections.length - 1) {
// Keep view controls visible
section.style.display = 'block';
} else {
// Hide other filter sections
section.style.display = 'none';
}
});
}
break;
}
// FIXED: Reset filter sections visibility when not in AI view
if (view !== 'ai' && filtersSection) {
const filterSections = filtersSection.querySelectorAll('.filter-section');
filterSections.forEach(section => {
section.style.display = 'block';
});
}
}
// Navigation functions for AI recommendations (unchanged)
window.navigateToGrid = function(toolName) {
console.log('Navigating to grid for tool:', toolName);
switchToView('grid');
setTimeout(() => {
if (window.clearAllFilters) {
window.clearAllFilters();
}
setTimeout(() => {
const toolCards = document.querySelectorAll('.tool-card');
let targetCard = null;
toolCards.forEach(card => {
const cardTitle = card.querySelector('h3');
if (cardTitle) {
const titleText = cardTitle.textContent?.replace(/[^\w\s\-\.]/g, '').trim();
if (titleText === toolName) {
targetCard = card;
}
}
});
if (targetCard) {
console.log('Found target card, scrolling...');
window.scrollToElement(targetCard, { block: 'center' });
targetCard.style.animation = 'highlight-flash 2s ease-out';
setTimeout(() => {
if (targetCard) {
targetCard.style.animation = '';
}
}, 2000);
} else {
console.warn('Tool card not found in grid:', toolName);
window.scrollToElementById('tools-grid');
}
}, 300);
}, 200);
};
window.navigateToMatrix = function(toolName) {
console.log('Navigating to matrix for tool:', toolName);
switchToView('matrix');
setTimeout(() => {
const toolChips = document.querySelectorAll('.tool-chip');
let firstMatch = null;
let matchCount = 0;
toolChips.forEach(chip => {
const chipText = chip.textContent?.replace(/📖/g, '').replace(/[^\w\s\-\.]/g, '').trim();
if (chipText === toolName) {
chip.style.animation = 'highlight-flash 2s ease-out';
matchCount++;
if (!firstMatch) {
firstMatch = chip;
}
setTimeout(() => {
chip.style.animation = '';
}, 8000);
}
});
if (firstMatch) {
console.log(`Found ${matchCount} occurrences of tool, highlighting all and scrolling to first`);
window.scrollToElement(firstMatch);
} else {
console.warn('Tool chip not found in matrix:', toolName);
window.scrollToElementById('matrix-container');
}
}, 500);
};
function handleSharedURL() {
const urlParams = new URLSearchParams(window.location.search);
const toolParam = urlParams.get('tool');
const viewParam = urlParams.get('view');
const modalParam = urlParams.get('modal');
if (!toolParam) {
if (viewParam === 'ai') {
switchToView('ai');
}
return;
}
const tool = window.findToolByIdentifier(window.toolsData, toolParam);
if (!tool) {
console.warn('Shared tool not found:', toolParam);
return;
}
const cleanUrl = window.location.protocol + "//" + window.location.host + window.location.pathname;
window.history.replaceState({}, document.title, cleanUrl);
setTimeout(() => {
switch (viewParam) {
case 'grid':
window.navigateToGrid(tool.name);
break;
case 'matrix':
window.navigateToMatrix(tool.name);
break;
case 'modal':
if (modalParam === 'secondary') {
window.showToolDetails(tool.name, 'secondary');
} else {
window.showToolDetails(tool.name, 'primary');
}
break;
default:
window.navigateToGrid(tool.name);
}
}, 100);
}
// REPLACE the existing toolsFiltered event listener in index.astro with this enhanced version:
window.addEventListener('toolsFiltered', (event) => {
const { tools: filtered, semanticSearch } = event.detail;
const currentView = document.querySelector('.view-toggle.active')?.getAttribute('data-view');
if (currentView === 'matrix' || currentView === 'ai') {
return;
}
const allToolCards = document.querySelectorAll('.tool-card');
const filteredNames = new Set(filtered.map(tool => tool.name.toLowerCase()));
const toolsContainer = document.getElementById('tools-container');
let visibleCount = 0;
if (semanticSearch && filtered.length > 0) {
console.log('[SEMANTIC] Reordering tools by semantic similarity');
// FIXED: Create ordered array of cards based on semantic similarity
const orderedCards = [];
const remainingCards = [];
// First pass: collect cards in semantic order
filtered.forEach(tool => {
const toolName = tool.name.toLowerCase();
const matchingCard = Array.from(allToolCards).find(card =>
card.getAttribute('data-tool-name') === toolName
);
if (matchingCard) {
matchingCard.style.display = 'block';
orderedCards.push(matchingCard);
visibleCount++;
// Add semantic indicators if available
if (tool._semanticSimilarity) {
matchingCard.setAttribute('data-semantic-similarity', tool._semanticSimilarity.toFixed(3));
matchingCard.setAttribute('data-semantic-rank', tool._semanticRank || '');
// Visual indication of semantic ranking (subtle)
const header = matchingCard.querySelector('.tool-card-header h3');
if (header && tool._semanticRank <= 3) {
const existingIndicator = header.querySelector('.semantic-rank-indicator');
if (existingIndicator) {
existingIndicator.remove();
}
const indicator = document.createElement('span');
indicator.className = 'semantic-rank-indicator';
indicator.style.cssText = `
display: inline-block;
width: 6px;
height: 6px;
background-color: var(--color-accent);
border-radius: 50%;
margin-left: 0.5rem;
opacity: ${1 - (tool._semanticRank - 1) * 0.3};
`;
indicator.title = `Semantische Relevanz: ${tool._semanticSimilarity.toFixed(3)}`;
header.appendChild(indicator);
}
}
}
});
// Second pass: hide non-matching cards and collect them
allToolCards.forEach(card => {
const toolName = card.getAttribute('data-tool-name');
if (!filteredNames.has(toolName)) {
card.style.display = 'none';
remainingCards.push(card);
}
});
// Reorder DOM: semantic results first, then hidden cards
const allCards = [...orderedCards, ...remainingCards];
allCards.forEach(card => {
toolsContainer.appendChild(card);
});
} else {
// FIXED: Standard filtering without semantic ordering
allToolCards.forEach(card => {
const toolName = card.getAttribute('data-tool-name');
// Clean up any semantic indicators
card.removeAttribute('data-semantic-similarity');
card.removeAttribute('data-semantic-rank');
const semanticIndicator = card.querySelector('.semantic-rank-indicator');
if (semanticIndicator) {
semanticIndicator.remove();
}
if (filteredNames.has(toolName)) {
card.style.display = 'block';
visibleCount++;
} else {
card.style.display = 'none';
}
});
// Restore original order when not using semantic search
if (!semanticSearch) {
const originalOrder = Array.from(allToolCards).sort((a, b) => {
// Get original indices from data attributes or DOM order
const aIndex = Array.from(allToolCards).indexOf(a);
const bIndex = Array.from(allToolCards).indexOf(b);
return aIndex - bIndex;
});
originalOrder.forEach(card => {
toolsContainer.appendChild(card);
});
}
}
// Show/hide no results message
if (visibleCount === 0) {
noResults.style.display = 'block';
} else {
noResults.style.display = 'none';
}
// Log semantic search info
if (semanticSearch) {
console.log(`[SEMANTIC] Displayed ${visibleCount} tools in semantic order`);
}
});
window.addEventListener('viewChanged', (event) => {
const view = event.detail;
switchToView(view);
});
window.switchToAIView = () => switchToView('ai');
handleSharedURL();
});
</script>
</BaseLayout>