main #11

Merged
mstoeck3 merged 66 commits from main into forensic-ai 2025-08-11 12:02:56 +00:00
4 changed files with 232 additions and 159 deletions
Showing only changes of commit 6918df9348 - Show all commits

View File

@ -193,6 +193,16 @@ domains.forEach((domain: any) => {
</div> </div>
<script define:vars={{ toolsData: tools, domainAgnosticSoftware, domainAgnosticTools }}> <script define:vars={{ toolsData: tools, domainAgnosticSoftware, domainAgnosticTools }}>
// Ensure isToolHosted is available
if (!window.isToolHosted) {
window.isToolHosted = function(tool) {
return tool.projectUrl !== undefined &&
tool.projectUrl !== null &&
tool.projectUrl !== "" &&
tool.projectUrl.trim() !== "";
};
}
function getSelectedPhase() { function getSelectedPhase() {
const activePhaseChip = document.querySelector('.phase-chip.active'); const activePhaseChip = document.querySelector('.phase-chip.active');
return activePhaseChip ? activePhaseChip.getAttribute('data-phase') : ''; return activePhaseChip ? activePhaseChip.getAttribute('data-phase') : '';
@ -216,9 +226,7 @@ domains.forEach((domain: any) => {
if (selectedDomain) { if (selectedDomain) {
const domainRow = matrixTable.querySelector(`tr[data-domain="${selectedDomain}"]`); const domainRow = matrixTable.querySelector(`tr[data-domain="${selectedDomain}"]`);
if (domainRow) { if (domainRow) domainRow.classList.add('highlight-row');
domainRow.classList.add('highlight-row');
}
} }
if (selectedPhase) { if (selectedPhase) {
@ -231,9 +239,7 @@ domains.forEach((domain: any) => {
const columnIndex = phaseIndex + 1; const columnIndex = phaseIndex + 1;
matrixTable.querySelectorAll(`tr`).forEach(row => { matrixTable.querySelectorAll(`tr`).forEach(row => {
const cell = row.children[columnIndex]; const cell = row.children[columnIndex];
if (cell) { if (cell) cell.classList.add('highlight-column');
cell.classList.add('highlight-column');
}
}); });
} }
} }
@ -267,9 +273,7 @@ domains.forEach((domain: any) => {
const params = new URLSearchParams(); const params = new URLSearchParams();
params.set('tool', toolSlug); params.set('tool', toolSlug);
params.set('view', view); params.set('view', view);
if (modal) { if (modal) params.set('modal', modal);
params.set('modal', modal);
}
return `${baseUrl}?${params.toString()}`; return `${baseUrl}?${params.toString()}`;
} }
@ -301,7 +305,7 @@ domains.forEach((domain: any) => {
} }
} }
window.showShareDialog = function(shareButton) { function showShareDialog(shareButton) {
const toolName = shareButton.getAttribute('data-tool-name'); const toolName = shareButton.getAttribute('data-tool-name');
const context = shareButton.getAttribute('data-context'); const context = shareButton.getAttribute('data-context');
@ -438,16 +442,11 @@ domains.forEach((domain: any) => {
copyToClipboard(url, btn); copyToClipboard(url, btn);
}); });
}); });
}; }
window.toggleDomainAgnosticSection = toggleDomainAgnosticSection; function showToolDetails(toolName, modalType = 'primary') {
window.showToolDetails = function(toolName, modalType = 'primary') {
const tool = toolsData.find(t => t.name === toolName); const tool = toolsData.find(t => t.name === toolName);
if (!tool) { if (!tool) return;
console.error('Tool not found:', toolName);
return;
}
const isMethod = tool.type === 'method'; const isMethod = tool.type === 'method';
const isConcept = tool.type === 'concept'; const isConcept = tool.type === 'concept';
@ -462,10 +461,7 @@ domains.forEach((domain: any) => {
}; };
for (const [key, element] of Object.entries(elements)) { for (const [key, element] of Object.entries(elements)) {
if (!element) { if (!element) return;
console.error(`Element not found: tool-${key}-${modalType}`);
return;
}
} }
const iconHtml = tool.icon ? `<span class="mr-3 text-xl">${tool.icon}</span>` : ''; const iconHtml = tool.icon ? `<span class="mr-3 text-xl">${tool.icon}</span>` : '';
@ -709,9 +705,9 @@ domains.forEach((domain: any) => {
if (primaryActive && secondaryActive) { if (primaryActive && secondaryActive) {
document.body.classList.add('modals-side-by-side'); document.body.classList.add('modals-side-by-side');
} }
}; }
window.hideToolDetails = function(modalType = 'both') { function hideToolDetails(modalType = 'both') {
const overlay = document.getElementById('modal-overlay'); const overlay = document.getElementById('modal-overlay');
const primaryModal = document.getElementById('tool-details-primary'); const primaryModal = document.getElementById('tool-details-primary');
const secondaryModal = document.getElementById('tool-details-secondary'); const secondaryModal = document.getElementById('tool-details-secondary');
@ -763,11 +759,22 @@ domains.forEach((domain: any) => {
} else { } else {
document.body.classList.remove('modals-side-by-side'); document.body.classList.remove('modals-side-by-side');
} }
}; }
window.hideAllToolDetails = function() { function hideAllToolDetails() {
window.hideToolDetails('both'); hideToolDetails('both');
}; }
// Register all functions globally
window.showToolDetails = showToolDetails;
window.hideToolDetails = hideToolDetails;
window.hideAllToolDetails = hideAllToolDetails;
window.toggleDomainAgnosticSection = toggleDomainAgnosticSection;
window.showShareDialog = showShareDialog;
// Register matrix-prefixed versions for delegation
window.matrixShowToolDetails = showToolDetails;
window.matrixHideToolDetails = hideToolDetails;
window.addEventListener('viewChanged', (event) => { window.addEventListener('viewChanged', (event) => {
const view = event.detail; const view = event.detail;
@ -798,13 +805,6 @@ domains.forEach((domain: any) => {
const domainAgnosticPhaseIds = domainAgnosticSoftware.map(section => section.id); const domainAgnosticPhaseIds = domainAgnosticSoftware.map(section => section.id);
const isDomainAgnosticPhase = domainAgnosticPhaseIds.includes(selectedPhase); const isDomainAgnosticPhase = domainAgnosticPhaseIds.includes(selectedPhase);
domainAgnosticSoftware.forEach(sectionData => {
const section = document.getElementById(`domain-agnostic-section-${sectionData.id}`);
const container = document.getElementById(`domain-agnostic-tools-${sectionData.id}`);
if (!section || !container) return;
});
if (!isDomainAgnosticPhase) { if (!isDomainAgnosticPhase) {
document.getElementById('dfir-matrix-section').style.display = 'block'; document.getElementById('dfir-matrix-section').style.display = 'block';
@ -813,9 +813,7 @@ domains.forEach((domain: any) => {
}); });
filtered.forEach(tool => { filtered.forEach(tool => {
if (tool.type === 'concept') { if (tool.type === 'concept') return;
return;
}
const isMethod = tool.type === 'method'; const isMethod = tool.type === 'method';
const hasValidProjectUrl = window.isToolHosted(tool); const hasValidProjectUrl = window.isToolHosted(tool);

3
src/env.d.ts vendored
View File

@ -11,6 +11,9 @@ declare global {
showToolDetails: (toolName: string, modalType?: string) => void; showToolDetails: (toolName: string, modalType?: string) => void;
hideToolDetails: (modalType?: string) => void; hideToolDetails: (modalType?: string) => void;
hideAllToolDetails: () => void; hideAllToolDetails: () => void;
matrixShowToolDetails?: (toolName: string, modalType?: string) => void;
matrixHideToolDetails?: (modalType?: string) => void;
toggleKbEntry: (entryId: string) => void; toggleKbEntry: (entryId: string) => void;
toggleDomainAgnosticSection: (sectionId: string) => void; toggleDomainAgnosticSection: (sectionId: string) => void;
restoreAIResults?: () => void; restoreAIResults?: () => void;

View File

@ -26,20 +26,41 @@ const { title, description = 'ForensicPathways - A comprehensive directory of di
<script> <script>
async function loadUtilityFunctions() { async function loadUtilityFunctions() {
try { try {
const { createToolSlug, findToolByIdentifier, isToolHosted } = await import('../utils/clientUtils.js'); const { createToolSlug, findToolByIdentifier, isToolHosted } = await import('../utils/clientUtils.js');
(window as any).createToolSlug = createToolSlug; (window as any).createToolSlug = createToolSlug;
(window as any).findToolByIdentifier = findToolByIdentifier; (window as any).findToolByIdentifier = findToolByIdentifier;
(window as any).isToolHosted = isToolHosted; (window as any).isToolHosted = isToolHosted;
} catch (error) {
console.error('Failed to load utility functions:', error); console.log('[UTILS] Utility functions loaded successfully');
(window as any).createToolSlug = (toolName: string) => { } catch (error) {
if (!toolName || typeof toolName !== 'string') return ''; console.error('Failed to load utility functions:', error);
return toolName.toLowerCase().replace(/[^a-z0-9\s-]/g, '').replace(/\s+/g, '-').replace(/-+/g, '-').replace(/^-|-$/g, '');
}; // Provide fallback implementations
(window as any).createToolSlug = (toolName: string) => {
if (!toolName || typeof toolName !== 'string') return '';
return toolName.toLowerCase().replace(/[^a-z0-9\s-]/g, '').replace(/\s+/g, '-').replace(/-+/g, '-').replace(/^-|-$/g, '');
};
(window as any).findToolByIdentifier = (tools: any[], identifier: string) => {
if (!identifier || !Array.isArray(tools)) return undefined;
return tools.find((tool: any) =>
tool.name === identifier ||
(window as any).createToolSlug(tool.name) === identifier.toLowerCase()
);
};
(window as any).isToolHosted = (tool: any) => {
return tool.projectUrl !== undefined &&
tool.projectUrl !== null &&
tool.projectUrl !== "" &&
tool.projectUrl.trim() !== "";
};
console.log('[UTILS] Fallback utility functions registered');
}
} }
}
function scrollToElement(element: Element | null, options = {}) { function scrollToElement(element: Element | null, options = {}) {
if (!element) return; if (!element) return;
@ -97,8 +118,9 @@ const { title, description = 'ForensicPathways - A comprehensive directory of di
(window as any).scrollToElementBySelector = scrollToElementBySelector; (window as any).scrollToElementBySelector = scrollToElementBySelector;
(window as any).prioritizeSearchResults = prioritizeSearchResults; (window as any).prioritizeSearchResults = prioritizeSearchResults;
document.addEventListener('DOMContentLoaded', () => { document.addEventListener('DOMContentLoaded', async () => {
loadUtilityFunctions(); // CRITICAL: Load utility functions FIRST before any URL handling
await loadUtilityFunctions();
const THEME_KEY = 'dfir-theme'; const THEME_KEY = 'dfir-theme';
@ -151,6 +173,44 @@ const { title, description = 'ForensicPathways - A comprehensive directory of di
getStoredTheme getStoredTheme
}; };
(window as any).showToolDetails = function(toolName: string, modalType: string = 'primary') {
let attempts = 0;
const maxAttempts = 50;
const tryDelegate = () => {
const matrixShowToolDetails = (window as any).matrixShowToolDetails;
if (matrixShowToolDetails && typeof matrixShowToolDetails === 'function') {
return matrixShowToolDetails(toolName, modalType);
}
const directShowToolDetails = (window as any).directShowToolDetails;
if (directShowToolDetails && typeof directShowToolDetails === 'function') {
return directShowToolDetails(toolName, modalType);
}
attempts++;
if (attempts < maxAttempts) {
setTimeout(tryDelegate, 100);
} else {
}
};
tryDelegate();
};
(window as any).hideToolDetails = function(modalType: string = 'both') {
const matrixHideToolDetails = (window as any).matrixHideToolDetails;
if (matrixHideToolDetails && typeof matrixHideToolDetails === 'function') {
return matrixHideToolDetails(modalType);
}
};
(window as any).hideAllToolDetails = function() {
(window as any).hideToolDetails('both');
};
async function checkClientAuth(context = 'general') { async function checkClientAuth(context = 'general') {
try { try {
const response = await fetch('/api/auth/status'); const response = await fetch('/api/auth/status');

View File

@ -510,6 +510,8 @@ if (aiAuthRequired) {
}; };
function handleSharedURL() { function handleSharedURL() {
console.log('[SHARE] Handling shared URL:', window.location.search);
const urlParams = new URLSearchParams(window.location.search); const urlParams = new URLSearchParams(window.location.search);
const toolParam = urlParams.get('tool'); const toolParam = urlParams.get('tool');
const viewParam = urlParams.get('view'); const viewParam = urlParams.get('view');
@ -522,12 +524,18 @@ if (aiAuthRequired) {
return; return;
} }
const tool = window.findToolByIdentifier(window.toolsData, toolParam); if (!window.findToolByIdentifier) {
if (!tool) { console.error('[SHARE] findToolByIdentifier not available, retrying...');
console.warn('Shared tool not found:', toolParam); setTimeout(() => handleSharedURL(), 200);
return; return;
} }
const tool = window.findToolByIdentifier(window.toolsData, toolParam);
if (!tool) {
return;
}
const cleanUrl = window.location.protocol + "//" + window.location.host + window.location.pathname; const cleanUrl = window.location.protocol + "//" + window.location.host + window.location.pathname;
window.history.replaceState({}, document.title, cleanUrl); window.history.replaceState({}, document.title, cleanUrl);
@ -549,124 +557,124 @@ if (aiAuthRequired) {
default: default:
window.navigateToGrid(tool.name); window.navigateToGrid(tool.name);
} }
}, 100); }, 300);
} }
window.addEventListener('toolsFiltered', (event) => { window.addEventListener('toolsFiltered', (event) => {
const { tools: filtered, semanticSearch } = event.detail; const { tools: filtered, semanticSearch } = event.detail;
const currentView = document.querySelector('.view-toggle.active')?.getAttribute('data-view'); const currentView = document.querySelector('.view-toggle.active')?.getAttribute('data-view');
if (currentView === 'matrix' || currentView === 'ai') { if (currentView === 'matrix' || currentView === 'ai') {
return; return;
} }
const allToolCards = document.querySelectorAll('.tool-card'); const allToolCards = document.querySelectorAll('.tool-card');
const filteredNames = new Set(filtered.map(tool => tool.name.toLowerCase())); const filteredNames = new Set(filtered.map(tool => tool.name.toLowerCase()));
const toolsContainer = document.getElementById('tools-container'); const toolsContainer = document.getElementById('tools-container');
let visibleCount = 0; let visibleCount = 0;
if (semanticSearch && filtered.length > 0) { if (semanticSearch && filtered.length > 0) {
console.log('[SEMANTIC] Reordering tools by semantic similarity'); console.log('[SEMANTIC] Reordering tools by semantic similarity');
const orderedCards = []; const orderedCards = [];
const remainingCards = []; const remainingCards = [];
filtered.forEach(tool => { filtered.forEach(tool => {
const toolName = tool.name.toLowerCase(); const toolName = tool.name.toLowerCase();
const matchingCard = Array.from(allToolCards).find(card => const matchingCard = Array.from(allToolCards).find(card =>
card.getAttribute('data-tool-name') === toolName card.getAttribute('data-tool-name') === toolName
); );
if (matchingCard) { if (matchingCard) {
matchingCard.style.display = 'block'; matchingCard.style.display = 'block';
orderedCards.push(matchingCard); orderedCards.push(matchingCard);
visibleCount++; visibleCount++;
if (tool._semanticSimilarity) { if (tool._semanticSimilarity) {
matchingCard.setAttribute('data-semantic-similarity', tool._semanticSimilarity.toFixed(3)); matchingCard.setAttribute('data-semantic-similarity', tool._semanticSimilarity.toFixed(3));
matchingCard.setAttribute('data-semantic-rank', tool._semanticRank || ''); matchingCard.setAttribute('data-semantic-rank', tool._semanticRank || '');
const header = matchingCard.querySelector('.tool-card-header h3'); const header = matchingCard.querySelector('.tool-card-header h3');
if (header && tool._semanticRank <= 3) { if (header && tool._semanticRank <= 3) {
const existingIndicator = header.querySelector('.semantic-rank-indicator'); const existingIndicator = header.querySelector('.semantic-rank-indicator');
if (existingIndicator) { if (existingIndicator) {
existingIndicator.remove(); 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);
} }
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);
} }
} }
}
});
allToolCards.forEach(card => {
const toolName = card.getAttribute('data-tool-name');
if (!filteredNames.has(toolName)) {
card.style.display = 'none';
remainingCards.push(card);
}
});
const allCards = [...orderedCards, ...remainingCards];
allCards.forEach(card => {
toolsContainer.appendChild(card);
});
} else {
allToolCards.forEach(card => {
const toolName = card.getAttribute('data-tool-name');
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';
}
});
if (!semanticSearch) {
const originalOrder = Array.from(allToolCards).sort((a, b) => {
const aIndex = Array.from(allToolCards).indexOf(a);
const bIndex = Array.from(allToolCards).indexOf(b);
return aIndex - bIndex;
}); });
originalOrder.forEach(card => { allToolCards.forEach(card => {
const toolName = card.getAttribute('data-tool-name');
if (!filteredNames.has(toolName)) {
card.style.display = 'none';
remainingCards.push(card);
}
});
const allCards = [...orderedCards, ...remainingCards];
allCards.forEach(card => {
toolsContainer.appendChild(card); toolsContainer.appendChild(card);
}); });
} else {
allToolCards.forEach(card => {
const toolName = card.getAttribute('data-tool-name');
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';
}
});
if (!semanticSearch) {
const originalOrder = Array.from(allToolCards).sort((a, b) => {
const aIndex = Array.from(allToolCards).indexOf(a);
const bIndex = Array.from(allToolCards).indexOf(b);
return aIndex - bIndex;
});
originalOrder.forEach(card => {
toolsContainer.appendChild(card);
});
}
} }
}
if (visibleCount === 0) { if (visibleCount === 0) {
noResults.style.display = 'block'; noResults.style.display = 'block';
} else { } else {
noResults.style.display = 'none'; noResults.style.display = 'none';
} }
if (semanticSearch) { if (semanticSearch) {
console.log(`[SEMANTIC] Displayed ${visibleCount} tools in semantic order`); console.log(`[SEMANTIC] Displayed ${visibleCount} tools in semantic order`);
} }
}); });
window.addEventListener('viewChanged', (event) => { window.addEventListener('viewChanged', (event) => {
const view = event.detail; const view = event.detail;
@ -678,7 +686,11 @@ if (aiAuthRequired) {
window.switchToAIView = () => switchToView('ai'); window.switchToAIView = () => switchToView('ai');
window.switchToView = switchToView; window.switchToView = switchToView;
handleSharedURL(); // CRITICAL: Handle shared URLs AFTER everything is set up
// Increased timeout to ensure all components and utility functions are loaded
setTimeout(() => {
handleSharedURL();
}, 1000);
}); });
</script> </script>
</BaseLayout> </BaseLayout>