remove mindless dev comments

This commit is contained in:
overcuriousity 2025-08-02 12:13:16 +02:00
parent 3973479ae4
commit 57c507915f
12 changed files with 13 additions and 200 deletions

View File

@ -211,12 +211,6 @@ const domainAgnosticSoftware = data['domain-agnostic-software'] || [];
</section> </section>
<script define:vars={{ tools, phases, domainAgnosticSoftware }}> <script define:vars={{ tools, phases, domainAgnosticSoftware }}>
// ===================================================================
// SIMPLIFIED AI QUERY INTERFACE - CONSOLIDATED & MAINTAINABLE
// ===================================================================
// Enhanced AI Interface JavaScript - Fix for text rendering issues in AIQueryInterface.astro
// This code replaces the existing <script> section in AIQueryInterface.astro
class AIQueryInterface { class AIQueryInterface {
constructor() { constructor() {
@ -234,7 +228,6 @@ class AIQueryInterface {
} }
initializeElements() { initializeElements() {
// Core elements
this.elements = { this.elements = {
input: document.getElementById('ai-query-input'), input: document.getElementById('ai-query-input'),
submitBtn: document.getElementById('ai-submit-btn'), submitBtn: document.getElementById('ai-submit-btn'),
@ -247,13 +240,11 @@ class AIQueryInterface {
results: document.getElementById('ai-results'), results: document.getElementById('ai-results'),
charCounter: document.getElementById('ai-char-counter'), charCounter: document.getElementById('ai-char-counter'),
// Mode toggle
toggleSwitch: document.querySelector('.toggle-switch'), toggleSwitch: document.querySelector('.toggle-switch'),
toggleSlider: document.querySelector('.toggle-slider'), toggleSlider: document.querySelector('.toggle-slider'),
workflowLabel: document.getElementById('workflow-label'), workflowLabel: document.getElementById('workflow-label'),
toolLabel: document.getElementById('tool-label'), toolLabel: document.getElementById('tool-label'),
// Smart prompting
promptingContainer: document.getElementById('smart-prompting-container'), promptingContainer: document.getElementById('smart-prompting-container'),
promptingStatus: document.getElementById('prompting-status'), promptingStatus: document.getElementById('prompting-status'),
promptingSpinner: document.getElementById('prompting-spinner'), promptingSpinner: document.getElementById('prompting-spinner'),
@ -262,7 +253,6 @@ class AIQueryInterface {
dismissSuggestions: document.getElementById('dismiss-suggestions'), dismissSuggestions: document.getElementById('dismiss-suggestions'),
smartHint: document.getElementById('smart-prompting-hint'), smartHint: document.getElementById('smart-prompting-hint'),
// Queue status
queueStatus: document.getElementById('queue-status'), queueStatus: document.getElementById('queue-status'),
queueLength: document.getElementById('queue-length'), queueLength: document.getElementById('queue-length'),
estimatedTime: document.getElementById('estimated-time'), estimatedTime: document.getElementById('estimated-time'),
@ -273,7 +263,6 @@ class AIQueryInterface {
microTaskCounter: document.getElementById('micro-task-counter') microTaskCounter: document.getElementById('micro-task-counter')
}; };
// Validate critical elements
if (!this.elements.input || !this.elements.submitBtn || !this.elements.results) { if (!this.elements.input || !this.elements.submitBtn || !this.elements.results) {
console.error('[AI Interface] Critical elements not found'); console.error('[AI Interface] Critical elements not found');
return false; return false;
@ -282,12 +271,10 @@ class AIQueryInterface {
} }
setupEventListeners() { setupEventListeners() {
// Mode toggle
this.elements.toggleSwitch?.addEventListener('click', () => this.toggleMode()); this.elements.toggleSwitch?.addEventListener('click', () => this.toggleMode());
this.elements.workflowLabel?.addEventListener('click', () => this.setMode('workflow')); this.elements.workflowLabel?.addEventListener('click', () => this.setMode('workflow'));
this.elements.toolLabel?.addEventListener('click', () => this.setMode('tool')); this.elements.toolLabel?.addEventListener('click', () => this.setMode('tool'));
// Input handling
this.elements.input?.addEventListener('input', () => this.handleInputChange()); this.elements.input?.addEventListener('input', () => this.handleInputChange());
this.elements.input?.addEventListener('keydown', (e) => { this.elements.input?.addEventListener('keydown', (e) => {
if (e.key === 'Enter' && (e.ctrlKey || e.metaKey)) { if (e.key === 'Enter' && (e.ctrlKey || e.metaKey)) {
@ -296,17 +283,11 @@ class AIQueryInterface {
} }
}); });
// Submit
this.elements.submitBtn?.addEventListener('click', () => this.handleSubmit()); this.elements.submitBtn?.addEventListener('click', () => this.handleSubmit());
// Smart prompting
this.elements.dismissSuggestions?.addEventListener('click', () => this.hideSmartPrompting()); this.elements.dismissSuggestions?.addEventListener('click', () => this.hideSmartPrompting());
} }
// ===================================================================
// MODE MANAGEMENT
// ===================================================================
getModeConfig() { getModeConfig() {
return { return {
workflow: { workflow: {
@ -345,7 +326,6 @@ class AIQueryInterface {
if (this.elements.submitBtnText) this.elements.submitBtnText.textContent = config.submitText; if (this.elements.submitBtnText) this.elements.submitBtnText.textContent = config.submitText;
if (this.elements.loadingText) this.elements.loadingText.textContent = config.loadingText; if (this.elements.loadingText) this.elements.loadingText.textContent = config.loadingText;
// Update toggle UI
if (this.currentMode === 'workflow') { if (this.currentMode === 'workflow') {
if (this.elements.toggleSlider) this.elements.toggleSlider.style.transform = 'translateX(0)'; if (this.elements.toggleSlider) this.elements.toggleSlider.style.transform = 'translateX(0)';
if (this.elements.toggleSwitch) this.elements.toggleSwitch.style.backgroundColor = 'var(--color-primary)'; if (this.elements.toggleSwitch) this.elements.toggleSwitch.style.backgroundColor = 'var(--color-primary)';
@ -371,8 +351,6 @@ class AIQueryInterface {
} }
} }
// [Previous methods remain the same until displayResults...]
handleInputChange() { handleInputChange() {
this.updateCharacterCount(); this.updateCharacterCount();
@ -493,10 +471,6 @@ class AIQueryInterface {
if (this.elements.smartHint) this.showElement(this.elements.smartHint); if (this.elements.smartHint) this.showElement(this.elements.smartHint);
} }
// ===================================================================
// MAIN SUBMIT HANDLER
// ===================================================================
async handleSubmit() { async handleSubmit() {
const query = this.elements.input.value.trim(); const query = this.elements.input.value.trim();
@ -512,14 +486,12 @@ class AIQueryInterface {
const taskId = `ai_${Date.now()}_${Math.random().toString(36).substr(2, 6)}`; const taskId = `ai_${Date.now()}_${Math.random().toString(36).substr(2, 6)}`;
// Reset state
this.hideSmartPrompting(); this.hideSmartPrompting();
this.hideResults(); this.hideResults();
this.hideError(); this.hideError();
this.showLoading(); this.showLoading();
this.startQueueMonitoring(taskId); this.startQueueMonitoring(taskId);
// Disable submit
this.elements.submitBtn.disabled = true; this.elements.submitBtn.disabled = true;
this.elements.submitBtnText.textContent = this.currentMode === 'workflow' ? this.elements.submitBtnText.textContent = this.currentMode === 'workflow' ?
'Generiere Empfehlungen...' : 'Suche passende Methode...'; 'Generiere Empfehlungen...' : 'Suche passende Methode...';
@ -545,7 +517,6 @@ class AIQueryInterface {
throw new Error(data.error || 'Unknown error'); throw new Error(data.error || 'Unknown error');
} }
// Store and display results
this.currentRecommendation = data.recommendation; this.currentRecommendation = data.recommendation;
this.displayResults(data.recommendation, query); this.displayResults(data.recommendation, query);
@ -579,8 +550,6 @@ class AIQueryInterface {
} }
} }
// [Queue monitoring methods remain the same...]
startQueueMonitoring(taskId) { startQueueMonitoring(taskId) {
if (this.elements.queueStatus) { if (this.elements.queueStatus) {
this.showElement(this.elements.queueStatus); this.showElement(this.elements.queueStatus);
@ -592,7 +561,6 @@ class AIQueryInterface {
this.startMicroTaskProgress(); this.startMicroTaskProgress();
// Start polling queue status
setTimeout(() => { setTimeout(() => {
this.updateQueueStatus(taskId); this.updateQueueStatus(taskId);
this.statusInterval = setInterval(() => this.updateQueueStatus(taskId), 1000); this.statusInterval = setInterval(() => this.updateQueueStatus(taskId), 1000);
@ -606,14 +574,12 @@ class AIQueryInterface {
const data = await response.json(); const data = await response.json();
// Update queue display
if (this.elements.queueLength) this.elements.queueLength.textContent = data.queueLength || 0; if (this.elements.queueLength) this.elements.queueLength.textContent = data.queueLength || 0;
if (this.elements.estimatedTime) { if (this.elements.estimatedTime) {
this.elements.estimatedTime.textContent = data.estimatedWaitTime > 0 ? this.elements.estimatedTime.textContent = data.estimatedWaitTime > 0 ?
this.formatDuration(data.estimatedWaitTime) : 'Verarbeitung läuft...'; this.formatDuration(data.estimatedWaitTime) : 'Verarbeitung läuft...';
} }
// Update position
if (this.elements.positionBadge) { if (this.elements.positionBadge) {
if (data.currentPosition) { if (data.currentPosition) {
this.elements.positionBadge.textContent = data.currentPosition; this.elements.positionBadge.textContent = data.currentPosition;
@ -622,7 +588,6 @@ class AIQueryInterface {
this.elements.progressBar.style.width = `${progress}%`; this.elements.progressBar.style.width = `${progress}%`;
} }
} else { } else {
// Handle processing states
const stateIcons = { const stateIcons = {
processing: '⚡', processing: '⚡',
completed: '✅', completed: '✅',
@ -664,14 +629,12 @@ class AIQueryInterface {
const steps = ['scenario', 'approach', 'considerations', 'tools', 'knowledge', 'final']; const steps = ['scenario', 'approach', 'considerations', 'tools', 'knowledge', 'final'];
const stepElements = this.elements.microTaskProgress.querySelectorAll('.micro-step'); const stepElements = this.elements.microTaskProgress.querySelectorAll('.micro-step');
// Reset all steps
stepElements.forEach(step => { stepElements.forEach(step => {
step.classList.remove('active', 'completed', 'failed'); step.classList.remove('active', 'completed', 'failed');
}); });
this.microTaskInterval = setInterval(() => { this.microTaskInterval = setInterval(() => {
if (this.currentMicroTaskStep < steps.length) { if (this.currentMicroTaskStep < steps.length) {
// Complete previous step
if (this.currentMicroTaskStep > 0) { if (this.currentMicroTaskStep > 0) {
const prevStep = this.elements.microTaskProgress.querySelector(`[data-step="${steps[this.currentMicroTaskStep - 1]}"]`); const prevStep = this.elements.microTaskProgress.querySelector(`[data-step="${steps[this.currentMicroTaskStep - 1]}"]`);
if (prevStep) { if (prevStep) {
@ -680,20 +643,17 @@ class AIQueryInterface {
} }
} }
// Activate current step
const currentStep = this.elements.microTaskProgress.querySelector(`[data-step="${steps[this.currentMicroTaskStep]}"]`); const currentStep = this.elements.microTaskProgress.querySelector(`[data-step="${steps[this.currentMicroTaskStep]}"]`);
if (currentStep) { if (currentStep) {
currentStep.classList.add('active'); currentStep.classList.add('active');
} }
// Update counter
if (this.elements.microTaskCounter) { if (this.elements.microTaskCounter) {
this.elements.microTaskCounter.textContent = `${this.currentMicroTaskStep + 1}/${steps.length}`; this.elements.microTaskCounter.textContent = `${this.currentMicroTaskStep + 1}/${steps.length}`;
} }
this.currentMicroTaskStep++; this.currentMicroTaskStep++;
} else { } else {
// Complete final step
const lastStep = this.elements.microTaskProgress.querySelector(`[data-step="${steps[steps.length - 1]}"]`); const lastStep = this.elements.microTaskProgress.querySelector(`[data-step="${steps[steps.length - 1]}"]`);
if (lastStep) { if (lastStep) {
lastStep.classList.remove('active'); lastStep.classList.remove('active');
@ -710,10 +670,6 @@ class AIQueryInterface {
}, 2000); }, 2000);
} }
// ===================================================================
// RESULTS DISPLAY - ENHANCED with proper text formatting
// ===================================================================
displayResults(recommendation, originalQuery) { displayResults(recommendation, originalQuery) {
if (this.currentMode === 'workflow') { if (this.currentMode === 'workflow') {
this.displayWorkflowResults(recommendation, originalQuery); this.displayWorkflowResults(recommendation, originalQuery);
@ -725,7 +681,6 @@ class AIQueryInterface {
} }
displayWorkflowResults(recommendation, originalQuery) { displayWorkflowResults(recommendation, originalQuery) {
// Group tools by phase
const toolsByPhase = {}; const toolsByPhase = {};
const phaseOrder = phases.map(phase => phase.id); const phaseOrder = phases.map(phase => phase.id);
const phaseNames = phases.reduce((acc, phase) => { const phaseNames = phases.reduce((acc, phase) => {
@ -787,7 +742,6 @@ class AIQueryInterface {
`; `;
} }
// ENHANCED: Fixed text rendering to prevent markdown/list interpretation
renderContextualAnalysis(recommendation, mode) { renderContextualAnalysis(recommendation, mode) {
let html = ''; let html = '';
@ -1052,15 +1006,9 @@ class AIQueryInterface {
return badges; return badges;
} }
// ===================================================================
// ENHANCED UTILITY METHODS with proper text sanitization
// ===================================================================
// CRITICAL: Sanitize text to prevent markdown interpretation
sanitizeText(text) { sanitizeText(text) {
if (typeof text !== 'string') return ''; if (typeof text !== 'string') return '';
// Remove/escape potential markdown and HTML
return text return text
// Remove markdown headers // Remove markdown headers
.replace(/^#{1,6}\s+/gm, '') .replace(/^#{1,6}\s+/gm, '')
@ -1096,13 +1044,11 @@ class AIQueryInterface {
const hasValidProjectUrl = this.isToolHosted(tool); const hasValidProjectUrl = this.isToolHosted(tool);
if (context === 'recommendation') { if (context === 'recommendation') {
// For tool-recommendation elements, use simple class names
if (isMethod) return 'method'; if (isMethod) return 'method';
if (hasValidProjectUrl) return 'hosted'; if (hasValidProjectUrl) return 'hosted';
if (tool.license !== 'Proprietary') return 'oss'; if (tool.license !== 'Proprietary') return 'oss';
return ''; return '';
} else { } else {
// For card elements, use card- prefixed class names
if (isMethod) return 'card-method'; if (isMethod) return 'card-method';
if (hasValidProjectUrl) return 'card-hosted'; if (hasValidProjectUrl) return 'card-hosted';
if (tool.license !== 'Proprietary') return 'card-oss'; if (tool.license !== 'Proprietary') return 'card-oss';
@ -1137,7 +1083,6 @@ class AIQueryInterface {
return `${Math.ceil(ms / 60000)}m`; return `${Math.ceil(ms / 60000)}m`;
} }
// State management
showElement(element) { showElement(element) {
if (element) { if (element) {
element.style.display = 'block'; element.style.display = 'block';
@ -1179,14 +1124,9 @@ class AIQueryInterface {
} }
} }
// ===================================================================
// INITIALIZATION
// ===================================================================
document.addEventListener('DOMContentLoaded', () => { document.addEventListener('DOMContentLoaded', () => {
const aiInterface = new AIQueryInterface(); const aiInterface = new AIQueryInterface();
// Global restore function for compatibility
window.restoreAIResults = () => { window.restoreAIResults = () => {
if (aiInterface.currentRecommendation && aiInterface.elements.results) { if (aiInterface.currentRecommendation && aiInterface.elements.results) {
aiInterface.showResults(); aiInterface.showResults();

View File

@ -77,41 +77,33 @@ const displayedScenarios = scenarios.slice(0, maxDisplayed);
if (!mainSearchInput) return; if (!mainSearchInput) return;
// Check if this scenario is already active (allow deselection)
if (clickedChip && clickedChip.classList.contains('active')) { if (clickedChip && clickedChip.classList.contains('active')) {
// Deselect: clear search and remove active state
mainSearchInput.value = ''; mainSearchInput.value = '';
document.querySelectorAll('.suggestion-chip').forEach(chip => { document.querySelectorAll('.suggestion-chip').forEach(chip => {
chip.classList.remove('active'); chip.classList.remove('active');
}); });
// Clear the targeted search input too
const targetedInput = document.getElementById('targeted-search-input'); const targetedInput = document.getElementById('targeted-search-input');
if (targetedInput) { if (targetedInput) {
targetedInput.value = ''; targetedInput.value = '';
} }
// Trigger search to show all results
const inputEvent = new Event('input', { bubbles: true }); const inputEvent = new Event('input', { bubbles: true });
mainSearchInput.dispatchEvent(inputEvent); mainSearchInput.dispatchEvent(inputEvent);
return; return;
} }
// Apply new search
mainSearchInput.value = scenarioId; mainSearchInput.value = scenarioId;
// Trigger existing search functionality
const inputEvent = new Event('input', { bubbles: true }); const inputEvent = new Event('input', { bubbles: true });
mainSearchInput.dispatchEvent(inputEvent); mainSearchInput.dispatchEvent(inputEvent);
// Switch to grid view
const gridToggle = document.querySelector('.view-toggle[data-view="grid"]'); const gridToggle = document.querySelector('.view-toggle[data-view="grid"]');
if (gridToggle && !gridToggle.classList.contains('active')) { if (gridToggle && !gridToggle.classList.contains('active')) {
gridToggle.click(); gridToggle.click();
} }
// Visual feedback
document.querySelectorAll('.suggestion-chip').forEach(chip => { document.querySelectorAll('.suggestion-chip').forEach(chip => {
chip.classList.remove('active'); chip.classList.remove('active');
}); });
@ -119,17 +111,14 @@ const displayedScenarios = scenarios.slice(0, maxDisplayed);
clickedChip.classList.add('active'); clickedChip.classList.add('active');
} }
// Scroll to results with better positioning
window.scrollToElementById('tools-grid'); window.scrollToElementById('tools-grid');
}; };
// Toggle showing all scenarios
window.toggleAllScenarios = function() { window.toggleAllScenarios = function() {
const suggestionsContainer = document.getElementById('scenario-suggestions'); const suggestionsContainer = document.getElementById('scenario-suggestions');
const moreBtn = document.getElementById('more-scenarios-btn'); const moreBtn = document.getElementById('more-scenarios-btn');
if (!showingAllScenarios) { if (!showingAllScenarios) {
// Show additional scenarios
const additionalScenarios = allScenarios.slice(maxDisplay); const additionalScenarios = allScenarios.slice(maxDisplay);
additionalScenarios.forEach(scenario => { additionalScenarios.forEach(scenario => {
const chip = document.createElement('div'); const chip = document.createElement('div');
@ -146,14 +135,12 @@ const displayedScenarios = scenarios.slice(0, maxDisplayed);
moreBtn.textContent = 'Weniger anzeigen'; moreBtn.textContent = 'Weniger anzeigen';
showingAllScenarios = true; showingAllScenarios = true;
} else { } else {
// Hide additional scenarios
document.querySelectorAll('.additional-scenario').forEach(chip => chip.remove()); document.querySelectorAll('.additional-scenario').forEach(chip => chip.remove());
moreBtn.textContent = `+ ${allScenarios.length - maxDisplay} weitere Szenarien`; moreBtn.textContent = `+ ${allScenarios.length - maxDisplay} weitere Szenarien`;
showingAllScenarios = false; showingAllScenarios = false;
} }
}; };
// Handle targeted search input
document.addEventListener('DOMContentLoaded', () => { document.addEventListener('DOMContentLoaded', () => {
const targetedInput = document.getElementById('targeted-search-input'); const targetedInput = document.getElementById('targeted-search-input');
if (targetedInput) { if (targetedInput) {

View File

@ -5,7 +5,6 @@ const data = await getToolsData();
const domains = data.domains; const domains = data.domains;
const phases = data.phases; const phases = data.phases;
// Extract unique values dynamically - NO HARD-CODING
const skillLevels = [...new Set(data.tools.map(tool => tool.skillLevel))].filter(Boolean).sort(); const skillLevels = [...new Set(data.tools.map(tool => tool.skillLevel))].filter(Boolean).sort();
const platforms = [...new Set(data.tools.flatMap(tool => tool.platforms || []))].filter(Boolean).sort(); const platforms = [...new Set(data.tools.flatMap(tool => tool.platforms || []))].filter(Boolean).sort();
const licenses = [...new Set(data.tools.map(tool => tool.license))].filter(Boolean).sort(); const licenses = [...new Set(data.tools.map(tool => tool.license))].filter(Boolean).sort();
@ -287,7 +286,6 @@ const sortedTags = Object.entries(tagFrequency)
window.toolsData = toolsData; window.toolsData = toolsData;
document.addEventListener('DOMContentLoaded', () => { document.addEventListener('DOMContentLoaded', () => {
// Cache DOM elements
const elements = { const elements = {
searchInput: document.getElementById('search-input'), searchInput: document.getElementById('search-input'),
clearSearch: document.getElementById('clear-search'), clearSearch: document.getElementById('clear-search'),
@ -312,25 +310,21 @@ const sortedTags = Object.entries(tagFrequency)
tags: document.getElementById('reset-tags'), tags: document.getElementById('reset-tags'),
all: document.getElementById('reset-all-filters') all: document.getElementById('reset-all-filters')
}, },
// Collapsible elements
toggleAdvanced: document.getElementById('toggle-advanced'), toggleAdvanced: document.getElementById('toggle-advanced'),
toggleTags: document.getElementById('toggle-tags'), toggleTags: document.getElementById('toggle-tags'),
advancedContent: document.getElementById('advanced-filters-content'), advancedContent: document.getElementById('advanced-filters-content'),
tagContent: document.getElementById('tag-filters-content') tagContent: document.getElementById('tag-filters-content')
}; };
// Verify critical elements exist
if (!elements.searchInput || !elements.domainSelect) { if (!elements.searchInput || !elements.domainSelect) {
console.error('Critical filter elements not found'); console.error('Critical filter elements not found');
return; return;
} }
// State management
let selectedTags = new Set(); let selectedTags = new Set();
let selectedPhase = ''; let selectedPhase = '';
let isTagCloudExpanded = false; let isTagCloudExpanded = false;
// Collapsible functionality
function toggleCollapsible(toggleBtn, content, storageKey) { function toggleCollapsible(toggleBtn, content, storageKey) {
const isCollapsed = toggleBtn.getAttribute('data-collapsed') === 'true'; const isCollapsed = toggleBtn.getAttribute('data-collapsed') === 'true';
const newState = !isCollapsed; const newState = !isCollapsed;
@ -338,22 +332,17 @@ const sortedTags = Object.entries(tagFrequency)
toggleBtn.setAttribute('data-collapsed', newState.toString()); toggleBtn.setAttribute('data-collapsed', newState.toString());
if (newState) { if (newState) {
// Collapse
content.classList.add('hidden'); content.classList.add('hidden');
toggleBtn.style.transform = 'rotate(0deg)'; toggleBtn.style.transform = 'rotate(0deg)';
} else { } else {
// Expand
content.classList.remove('hidden'); content.classList.remove('hidden');
toggleBtn.style.transform = 'rotate(180deg)'; toggleBtn.style.transform = 'rotate(180deg)';
} }
// Store state in sessionStorage
sessionStorage.setItem(storageKey, newState.toString()); sessionStorage.setItem(storageKey, newState.toString());
} }
// Initialize collapsible sections (collapsed by default)
function initializeCollapsible() { function initializeCollapsible() {
// Advanced filters
const advancedCollapsed = sessionStorage.getItem('advanced-collapsed') !== 'false'; const advancedCollapsed = sessionStorage.getItem('advanced-collapsed') !== 'false';
elements.toggleAdvanced.setAttribute('data-collapsed', advancedCollapsed.toString()); elements.toggleAdvanced.setAttribute('data-collapsed', advancedCollapsed.toString());
if (advancedCollapsed) { if (advancedCollapsed) {
@ -364,7 +353,6 @@ const sortedTags = Object.entries(tagFrequency)
elements.toggleAdvanced.style.transform = 'rotate(180deg)'; elements.toggleAdvanced.style.transform = 'rotate(180deg)';
} }
// Tag filters
const tagsCollapsed = sessionStorage.getItem('tags-collapsed') !== 'false'; const tagsCollapsed = sessionStorage.getItem('tags-collapsed') !== 'false';
elements.toggleTags.setAttribute('data-collapsed', tagsCollapsed.toString()); elements.toggleTags.setAttribute('data-collapsed', tagsCollapsed.toString());
if (tagsCollapsed) { if (tagsCollapsed) {
@ -376,7 +364,6 @@ const sortedTags = Object.entries(tagFrequency)
} }
} }
// Helper function to check if tool is hosted
function isToolHosted(tool) { function isToolHosted(tool) {
return tool.projectUrl !== undefined && return tool.projectUrl !== undefined &&
tool.projectUrl !== null && tool.projectUrl !== null &&
@ -384,7 +371,6 @@ const sortedTags = Object.entries(tagFrequency)
tool.projectUrl.trim() !== ""; tool.projectUrl.trim() !== "";
} }
// Initialize tag cloud
function initTagCloud() { function initTagCloud() {
const visibleCount = 20; const visibleCount = 20;
elements.tagCloudItems.forEach((item, index) => { elements.tagCloudItems.forEach((item, index) => {
@ -394,7 +380,6 @@ const sortedTags = Object.entries(tagFrequency)
}); });
} }
// Toggle tag cloud expansion
function toggleTagCloud() { function toggleTagCloud() {
isTagCloudExpanded = !isTagCloudExpanded; isTagCloudExpanded = !isTagCloudExpanded;
const visibleCount = 20; const visibleCount = 20;
@ -428,7 +413,6 @@ const sortedTags = Object.entries(tagFrequency)
} }
} }
// Filter tag cloud based on search
function filterTagCloud() { function filterTagCloud() {
const searchTerm = elements.searchInput.value.toLowerCase(); const searchTerm = elements.searchInput.value.toLowerCase();
let visibleCount = 0; let visibleCount = 0;
@ -458,7 +442,6 @@ const sortedTags = Object.entries(tagFrequency)
elements.tagCloudToggle.style.display = hasHiddenTags ? 'block' : 'none'; elements.tagCloudToggle.style.display = hasHiddenTags ? 'block' : 'none';
} }
// Update selected tags display
function updateSelectedTags() { function updateSelectedTags() {
if (selectedTags.size === 0) { if (selectedTags.size === 0) {
elements.selectedTags.style.display = 'none'; elements.selectedTags.style.display = 'none';
@ -478,7 +461,6 @@ const sortedTags = Object.entries(tagFrequency)
</div> </div>
`; `;
// Add event listeners for remove buttons
elements.selectedTags.querySelectorAll('.remove-tag').forEach(btn => { elements.selectedTags.querySelectorAll('.remove-tag').forEach(btn => {
btn.addEventListener('click', () => { btn.addEventListener('click', () => {
const tag = btn.getAttribute('data-tag'); const tag = btn.getAttribute('data-tag');
@ -487,10 +469,8 @@ const sortedTags = Object.entries(tagFrequency)
}); });
} }
// Add/remove tags - FIXED: Update ALL matching elements
function addTag(tag) { function addTag(tag) {
selectedTags.add(tag); selectedTags.add(tag);
// FIXED: Use querySelectorAll to update ALL matching tag elements
document.querySelectorAll(`[data-tag="${tag}"]`).forEach(element => { document.querySelectorAll(`[data-tag="${tag}"]`).forEach(element => {
element.classList.add('active'); element.classList.add('active');
}); });
@ -500,7 +480,6 @@ const sortedTags = Object.entries(tagFrequency)
function removeTag(tag) { function removeTag(tag) {
selectedTags.delete(tag); selectedTags.delete(tag);
// FIXED: Use querySelectorAll to update ALL matching tag elements
document.querySelectorAll(`[data-tag="${tag}"]`).forEach(element => { document.querySelectorAll(`[data-tag="${tag}"]`).forEach(element => {
element.classList.remove('active'); element.classList.remove('active');
}); });
@ -508,7 +487,6 @@ const sortedTags = Object.entries(tagFrequency)
filterTools(); filterTools();
} }
// Update results counter
function updateResultsCounter(count) { function updateResultsCounter(count) {
const total = window.toolsData.length; const total = window.toolsData.length;
elements.resultsCounter.textContent = count === total elements.resultsCounter.textContent = count === total
@ -516,7 +494,6 @@ const sortedTags = Object.entries(tagFrequency)
: `${count} von ${total} Tools`; : `${count} von ${total} Tools`;
} }
// Main filter function
function filterTools() { function filterTools() {
const searchTerm = elements.searchInput.value.trim().toLowerCase(); const searchTerm = elements.searchInput.value.trim().toLowerCase();
const selectedDomain = elements.domainSelect.value; const selectedDomain = elements.domainSelect.value;
@ -529,11 +506,9 @@ const sortedTags = Object.entries(tagFrequency)
const hostedOnly = elements.hostedOnly.checked; const hostedOnly = elements.hostedOnly.checked;
const knowledgebaseOnly = elements.knowledgebaseOnly.checked; const knowledgebaseOnly = elements.knowledgebaseOnly.checked;
// Use phase from either dropdown or button selection
const activePhase = selectedPhaseFromSelect || selectedPhase; const activePhase = selectedPhaseFromSelect || selectedPhase;
const filtered = window.toolsData.filter(tool => { const filtered = window.toolsData.filter(tool => {
// Search filter
if (searchTerm && !( if (searchTerm && !(
tool.name.toLowerCase().includes(searchTerm) || tool.name.toLowerCase().includes(searchTerm) ||
tool.description.toLowerCase().includes(searchTerm) || tool.description.toLowerCase().includes(searchTerm) ||
@ -542,52 +517,42 @@ const sortedTags = Object.entries(tagFrequency)
return false; return false;
} }
// Domain filter
if (selectedDomain && !(tool.domains || []).includes(selectedDomain)) { if (selectedDomain && !(tool.domains || []).includes(selectedDomain)) {
return false; return false;
} }
// Phase filter
if (activePhase && !(tool.phases || []).includes(activePhase)) { if (activePhase && !(tool.phases || []).includes(activePhase)) {
return false; return false;
} }
// Type filter
if (selectedType && tool.type !== selectedType) { if (selectedType && tool.type !== selectedType) {
return false; return false;
} }
// Skill level filter
if (selectedSkill && tool.skillLevel !== selectedSkill) { if (selectedSkill && tool.skillLevel !== selectedSkill) {
return false; return false;
} }
// Platform filter
if (selectedPlatform && !(tool.platforms || []).includes(selectedPlatform)) { if (selectedPlatform && !(tool.platforms || []).includes(selectedPlatform)) {
return false; return false;
} }
// License filter - NO MORE HARD-CODED LOGIC
if (selectedLicense && tool.license !== selectedLicense) { if (selectedLicense && tool.license !== selectedLicense) {
return false; return false;
} }
// Access type filter
if (selectedAccess && tool.accessType !== selectedAccess) { if (selectedAccess && tool.accessType !== selectedAccess) {
return false; return false;
} }
// Hosted only filter (CC24-Server tools)
if (hostedOnly && !isToolHosted(tool)) { if (hostedOnly && !isToolHosted(tool)) {
return false; return false;
} }
// Knowledgebase only filter
if (knowledgebaseOnly && !tool.knowledgebase) { if (knowledgebaseOnly && !tool.knowledgebase) {
return false; return false;
} }
// Tag filter
if (selectedTags.size > 0 && !Array.from(selectedTags).every(tag => (tool.tags || []).includes(tag))) { if (selectedTags.size > 0 && !Array.from(selectedTags).every(tag => (tool.tags || []).includes(tag))) {
return false; return false;
} }
@ -595,18 +560,15 @@ const sortedTags = Object.entries(tagFrequency)
return true; return true;
}); });
// Apply search prioritization if there's a search term
const finalResults = searchTerm && window.prioritizeSearchResults const finalResults = searchTerm && window.prioritizeSearchResults
? window.prioritizeSearchResults(filtered, searchTerm) ? window.prioritizeSearchResults(filtered, searchTerm)
: filtered; : filtered;
updateResultsCounter(finalResults.length); updateResultsCounter(finalResults.length);
// Dispatch event for other components
window.dispatchEvent(new CustomEvent('toolsFiltered', { detail: finalResults })); window.dispatchEvent(new CustomEvent('toolsFiltered', { detail: finalResults }));
} }
// Reset functions
function resetPrimaryFilters() { function resetPrimaryFilters() {
elements.domainSelect.value = ''; elements.domainSelect.value = '';
elements.phaseSelect.value = ''; elements.phaseSelect.value = '';
@ -627,7 +589,6 @@ const sortedTags = Object.entries(tagFrequency)
function resetTags() { function resetTags() {
selectedTags.clear(); selectedTags.clear();
// FIXED: Update ALL tag elements
document.querySelectorAll('.tag-cloud-item').forEach(item => { document.querySelectorAll('.tag-cloud-item').forEach(item => {
item.classList.remove('active'); item.classList.remove('active');
}); });
@ -644,7 +605,6 @@ const sortedTags = Object.entries(tagFrequency)
filterTagCloud(); filterTagCloud();
} }
// Event listeners
elements.searchInput.addEventListener('input', (e) => { elements.searchInput.addEventListener('input', (e) => {
const hasValue = e.target.value.length > 0; const hasValue = e.target.value.length > 0;
elements.clearSearch.classList.toggle('hidden', !hasValue); elements.clearSearch.classList.toggle('hidden', !hasValue);
@ -685,7 +645,6 @@ const sortedTags = Object.entries(tagFrequency)
btn.addEventListener('click', () => { btn.addEventListener('click', () => {
const view = btn.getAttribute('data-view'); const view = btn.getAttribute('data-view');
// Simple toggle like the old version
elements.viewToggles.forEach(b => { elements.viewToggles.forEach(b => {
b.classList.toggle('active', b.getAttribute('data-view') === view); b.classList.toggle('active', b.getAttribute('data-view') === view);
}); });
@ -701,13 +660,11 @@ const sortedTags = Object.entries(tagFrequency)
}); });
}); });
// Reset button listeners
elements.resetButtons.primary.addEventListener('click', resetPrimaryFilters); elements.resetButtons.primary.addEventListener('click', resetPrimaryFilters);
elements.resetButtons.advanced.addEventListener('click', resetAdvancedFilters); elements.resetButtons.advanced.addEventListener('click', resetAdvancedFilters);
elements.resetButtons.tags.addEventListener('click', resetTags); elements.resetButtons.tags.addEventListener('click', resetTags);
elements.resetButtons.all.addEventListener('click', resetAllFilters); elements.resetButtons.all.addEventListener('click', resetAllFilters);
// Collapsible toggle listeners
elements.toggleAdvanced.addEventListener('click', () => { elements.toggleAdvanced.addEventListener('click', () => {
toggleCollapsible(elements.toggleAdvanced, elements.advancedContent, 'advanced-collapsed'); toggleCollapsible(elements.toggleAdvanced, elements.advancedContent, 'advanced-collapsed');
}); });
@ -716,11 +673,9 @@ const sortedTags = Object.entries(tagFrequency)
toggleCollapsible(elements.toggleTags, elements.tagContent, 'tags-collapsed'); toggleCollapsible(elements.toggleTags, elements.tagContent, 'tags-collapsed');
}); });
// Expose functions globally for backwards compatibility
window.clearTagFilters = resetTags; window.clearTagFilters = resetTags;
window.clearAllFilters = resetAllFilters; window.clearAllFilters = resetAllFilters;
// Initialize
initializeCollapsible(); initializeCollapsible();
initTagCloud(); initTagCloud();
filterTagCloud(); filterTagCloud();

View File

@ -716,7 +716,6 @@ domains.forEach((domain: any) => {
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');
// Debounce rapid calls
if (window.modalHideInProgress) return; if (window.modalHideInProgress) return;
window.modalHideInProgress = true; window.modalHideInProgress = true;
@ -753,19 +752,15 @@ domains.forEach((domain: any) => {
if (contributeButtonSecondary) contributeButtonSecondary.style.display = 'none'; if (contributeButtonSecondary) contributeButtonSecondary.style.display = 'none';
} }
// Consolidated state checking with safety checks
const primaryActive = primaryModal && primaryModal.classList.contains('active'); const primaryActive = primaryModal && primaryModal.classList.contains('active');
const secondaryActive = secondaryModal && secondaryModal.classList.contains('active'); const secondaryActive = secondaryModal && secondaryModal.classList.contains('active');
// Update overlay and body classes atomically
if (!primaryActive && !secondaryActive) { if (!primaryActive && !secondaryActive) {
if (overlay) overlay.classList.remove('active'); if (overlay) overlay.classList.remove('active');
document.body.classList.remove('modals-side-by-side'); document.body.classList.remove('modals-side-by-side');
} else if (primaryActive && secondaryActive) { } else if (primaryActive && secondaryActive) {
// Both active - ensure side-by-side class
document.body.classList.add('modals-side-by-side'); document.body.classList.add('modals-side-by-side');
} else { } else {
// Only one active - remove side-by-side class
document.body.classList.remove('modals-side-by-side'); document.body.classList.remove('modals-side-by-side');
} }
}; };

2
src/env.d.ts vendored
View File

@ -27,12 +27,10 @@ declare global {
showIfAuthenticated: (selector: string, context?: string) => Promise<void>; showIfAuthenticated: (selector: string, context?: string) => Promise<void>;
setupAuthButtons: (selector?: string) => void; setupAuthButtons: (selector?: string) => void;
// Consolidated scroll utilities
scrollToElement: (element: Element | null, options?: ScrollIntoViewOptions) => void; scrollToElement: (element: Element | null, options?: ScrollIntoViewOptions) => void;
scrollToElementById: (elementId: string, options?: ScrollIntoViewOptions) => void; scrollToElementById: (elementId: string, options?: ScrollIntoViewOptions) => void;
scrollToElementBySelector: (selector: string, options?: ScrollIntoViewOptions) => void; scrollToElementBySelector: (selector: string, options?: ScrollIntoViewOptions) => void;
// Additional global functions that might be called
applyScenarioSearch?: (scenarioId: string) => void; applyScenarioSearch?: (scenarioId: string) => void;
selectPhase?: (phase: string) => void; selectPhase?: (phase: string) => void;
selectApproach?: (approach: string) => void; selectApproach?: (approach: string) => void;

View File

@ -21,7 +21,6 @@ const { title, description = 'ForensicPathways - A comprehensive directory of di
<link rel="icon" type="image/x-icon" href="/favicon.ico"> <link rel="icon" type="image/x-icon" href="/favicon.ico">
<script> <script>
// Move utility functions OUTSIDE DOMContentLoaded to avoid race conditions
function createToolSlug(toolName) { function createToolSlug(toolName) {
if (!toolName || typeof toolName !== 'string') { if (!toolName || typeof toolName !== 'string') {
console.warn('[toolHelpers] Invalid toolName provided to createToolSlug:', toolName); console.warn('[toolHelpers] Invalid toolName provided to createToolSlug:', toolName);
@ -51,16 +50,14 @@ const { title, description = 'ForensicPathways - A comprehensive directory of di
tool.projectUrl.trim() !== ""; tool.projectUrl.trim() !== "";
} }
// Consolidated scrolling utility - also moved outside DOMContentLoaded
function scrollToElement(element, options = {}) { function scrollToElement(element, options = {}) {
if (!element) return; if (!element) return;
// Calculate target position manually to avoid double-scroll
setTimeout(() => { setTimeout(() => {
const headerHeight = document.querySelector('nav')?.offsetHeight || 80; const headerHeight = document.querySelector('nav')?.offsetHeight || 80;
const elementRect = element.getBoundingClientRect(); const elementRect = element.getBoundingClientRect();
const absoluteElementTop = elementRect.top + window.pageYOffset; const absoluteElementTop = elementRect.top + window.pageYOffset;
const targetPosition = absoluteElementTop - headerHeight - 20; // Adjust this 20 as needed const targetPosition = absoluteElementTop - headerHeight - 20;
window.scrollTo({ window.scrollTo({
top: targetPosition, top: targetPosition,
@ -69,7 +66,6 @@ const { title, description = 'ForensicPathways - A comprehensive directory of di
}, 100); }, 100);
} }
// Convenience functions for common scroll targets
function scrollToElementById(elementId, options = {}) { function scrollToElementById(elementId, options = {}) {
const element = document.getElementById(elementId); const element = document.getElementById(elementId);
scrollToElement(element, options); scrollToElement(element, options);
@ -80,7 +76,6 @@ const { title, description = 'ForensicPathways - A comprehensive directory of di
scrollToElement(element, options); scrollToElement(element, options);
} }
// Simple search prioritization - exact tag matches first
function prioritizeSearchResults(tools, searchTerm) { function prioritizeSearchResults(tools, searchTerm) {
if (!searchTerm || !searchTerm.trim()) { if (!searchTerm || !searchTerm.trim()) {
return tools; return tools;
@ -95,16 +90,13 @@ const { title, description = 'ForensicPathways - A comprehensive directory of di
const aExactTag = aTagsLower.includes(lowerSearchTerm); const aExactTag = aTagsLower.includes(lowerSearchTerm);
const bExactTag = bTagsLower.includes(lowerSearchTerm); const bExactTag = bTagsLower.includes(lowerSearchTerm);
// If one has exact tag match and other doesn't, prioritize the exact match
if (aExactTag && !bExactTag) return -1; if (aExactTag && !bExactTag) return -1;
if (!aExactTag && bExactTag) return 1; if (!aExactTag && bExactTag) return 1;
// Otherwise maintain original order (or sort by name as secondary)
return a.name.localeCompare(b.name); return a.name.localeCompare(b.name);
}); });
} }
// Attach to window immediately - BEFORE DOMContentLoaded
(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;

View File

@ -14,14 +14,13 @@ function getEnv(key: string): string {
return value; return value;
} }
// Use the analyzer AI for smart prompting (smaller, faster model)
const AI_ENDPOINT = getEnv('AI_ANALYZER_ENDPOINT'); const AI_ENDPOINT = getEnv('AI_ANALYZER_ENDPOINT');
const AI_API_KEY = getEnv('AI_ANALYZER_API_KEY'); const AI_API_KEY = getEnv('AI_ANALYZER_API_KEY');
const AI_MODEL = getEnv('AI_ANALYZER_MODEL'); const AI_MODEL = getEnv('AI_ANALYZER_MODEL');
const rateLimitStore = new Map<string, { count: number; resetTime: number }>(); const rateLimitStore = new Map<string, { count: number; resetTime: number }>();
const RATE_LIMIT_WINDOW = 60 * 1000; // 1 minute const RATE_LIMIT_WINDOW = 60 * 1000; // 1 minute
const RATE_LIMIT_MAX = 5; // 5 enhancement requests per minute per user const RATE_LIMIT_MAX = 5;
function sanitizeInput(input: string): string { function sanitizeInput(input: string): string {
return input return input
@ -30,7 +29,7 @@ function sanitizeInput(input: string): string {
.replace(/\b(system|assistant|user)\s*[:]/gi, '[ROLE_REMOVED]') .replace(/\b(system|assistant|user)\s*[:]/gi, '[ROLE_REMOVED]')
.replace(/\b(ignore|forget|disregard)\s+(previous|all|your)\s+(instructions?|context|rules?)/gi, '[INSTRUCTION_REMOVED]') .replace(/\b(ignore|forget|disregard)\s+(previous|all|your)\s+(instructions?|context|rules?)/gi, '[INSTRUCTION_REMOVED]')
.trim() .trim()
.slice(0, 1000); // Shorter limit for enhancement .slice(0, 1000);
} }
function checkRateLimit(userId: string): boolean { function checkRateLimit(userId: string): boolean {
@ -59,7 +58,6 @@ function cleanupExpiredRateLimits() {
} }
} }
// Clean up expired limits every 5 minutes
setInterval(cleanupExpiredRateLimits, 5 * 60 * 1000); setInterval(cleanupExpiredRateLimits, 5 * 60 * 1000);
function createEnhancementPrompt(input: string): string { function createEnhancementPrompt(input: string): string {
@ -140,7 +138,6 @@ export const POST: APIRoute = async ({ request }) => {
], ],
max_tokens: 300, max_tokens: 300,
temperature: 0.7, temperature: 0.7,
// Enhanced: Better parameters for consistent forensics output
top_p: 0.9, top_p: 0.9,
frequency_penalty: 0.2, frequency_penalty: 0.2,
presence_penalty: 0.1 presence_penalty: 0.1
@ -171,27 +168,23 @@ export const POST: APIRoute = async ({ request }) => {
throw new Error('Response is not an array'); throw new Error('Response is not an array');
} }
// Enhanced validation and cleaning for forensics context
questions = questions questions = questions
.filter(q => typeof q === 'string' && q.length > 20 && q.length < 200) // More appropriate length for forensics questions .filter(q => typeof q === 'string' && q.length > 20 && q.length < 200)
.filter(q => q.includes('?')) // Must be a question .filter(q => q.includes('?'))
.filter(q => { .filter(q => {
// Enhanced: Filter for forensics-relevant questions
const forensicsTerms = ['forensisch', 'log', 'dump', 'image', 'artefakt', 'evidence', 'incident', 'system', 'netzwerk', 'zeitraum', 'verfügbar']; const forensicsTerms = ['forensisch', 'log', 'dump', 'image', 'artefakt', 'evidence', 'incident', 'system', 'netzwerk', 'zeitraum', 'verfügbar'];
const lowerQ = q.toLowerCase(); const lowerQ = q.toLowerCase();
return forensicsTerms.some(term => lowerQ.includes(term)); return forensicsTerms.some(term => lowerQ.includes(term));
}) })
.map(q => q.trim()) .map(q => q.trim())
.slice(0, 3); // Max 3 questions .slice(0, 3);
// If no valid forensics questions, return empty array (means input is complete)
if (questions.length === 0) { if (questions.length === 0) {
questions = []; questions = [];
} }
} catch (error) { } catch (error) {
console.error('Failed to parse enhancement response:', aiContent); console.error('Failed to parse enhancement response:', aiContent);
// If parsing fails, assume input is complete enough
questions = []; questions = [];
} }

View File

@ -43,7 +43,7 @@ interface KnowledgebaseContributionData {
const rateLimitStore = new Map<string, { count: number; resetTime: number }>(); const rateLimitStore = new Map<string, { count: number; resetTime: number }>();
const RATE_LIMIT_WINDOW = 60 * 60 * 1000; // 1 hour const RATE_LIMIT_WINDOW = 60 * 60 * 1000; // 1 hour
const RATE_LIMIT_MAX = 3; // Max 3 submissions per hour per user const RATE_LIMIT_MAX = 3;
function checkRateLimit(userEmail: string): boolean { function checkRateLimit(userEmail: string): boolean {
const now = Date.now(); const now = Date.now();

View File

@ -1,4 +1,4 @@
// src/pages/api/upload/media.ts (UPDATED - Using consolidated API responses) // src/pages/api/upload/media.ts
import type { APIRoute } from 'astro'; import type { APIRoute } from 'astro';
import { withAPIAuth } from '../../../utils/auth.js'; import { withAPIAuth } from '../../../utils/auth.js';
import { apiResponse, apiError, apiServerError, apiSpecial, handleAPIRequest } from '../../../utils/api.js'; import { apiResponse, apiError, apiServerError, apiSpecial, handleAPIRequest } from '../../../utils/api.js';

View File

@ -173,29 +173,24 @@ const phases = data.phases;
<script define:vars={{ toolsData: data.tools, phases: data.phases }}> <script define:vars={{ toolsData: data.tools, phases: data.phases }}>
window.toolsData = toolsData; window.toolsData = toolsData;
// Approach selection functionality
window.selectApproach = function(approach) { window.selectApproach = function(approach) {
console.log(`Selected approach: ${approach}`); console.log(`Selected approach: ${approach}`);
// Hide any existing results
const aiResults = document.getElementById('ai-results'); const aiResults = document.getElementById('ai-results');
if (aiResults) aiResults.style.display = 'none'; if (aiResults) aiResults.style.display = 'none';
// Visual feedback for selection
document.querySelectorAll('.approach-card').forEach(card => { document.querySelectorAll('.approach-card').forEach(card => {
card.classList.remove('selected'); card.classList.remove('selected');
}); });
document.querySelector(`.approach-card.${approach}`).classList.add('selected'); document.querySelector(`.approach-card.${approach}`).classList.add('selected');
if (approach === 'methodology') { if (approach === 'methodology') {
// Show NIST methodology section
const methodologySection = document.getElementById('methodology-section'); const methodologySection = document.getElementById('methodology-section');
if (methodologySection) { if (methodologySection) {
methodologySection.classList.add('active'); methodologySection.classList.add('active');
window.scrollToElementById('methodology-section'); window.scrollToElementById('methodology-section');
} }
} else if (approach === 'targeted') { } else if (approach === 'targeted') {
// Show targeted scenarios section
const targetedSection = document.getElementById('targeted-section'); const targetedSection = document.getElementById('targeted-section');
if (targetedSection) { if (targetedSection) {
targetedSection.classList.add('active'); targetedSection.classList.add('active');
@ -204,34 +199,28 @@ const phases = data.phases;
} }
}; };
// Phase selection function (integrates with existing ToolFilters)
window.selectPhase = function(phase) { window.selectPhase = function(phase) {
console.log(`Selected NIST phase: ${phase}`); console.log(`Selected NIST phase: ${phase}`);
// Remove active class from all phase cards
document.querySelectorAll('.phase-card').forEach(card => { document.querySelectorAll('.phase-card').forEach(card => {
card.classList.remove('active'); card.classList.remove('active');
}); });
// Add active class to selected phase card
const selectedCard = document.querySelector(`.phase-card.phase-${phase}`); const selectedCard = document.querySelector(`.phase-card.phase-${phase}`);
if (selectedCard) { if (selectedCard) {
selectedCard.classList.add('active'); selectedCard.classList.add('active');
} }
// Use existing phase filter functionality
const existingPhaseButton = document.querySelector(`[data-phase="${phase}"]`); const existingPhaseButton = document.querySelector(`[data-phase="${phase}"]`);
if (existingPhaseButton && !existingPhaseButton.classList.contains('active')) { if (existingPhaseButton && !existingPhaseButton.classList.contains('active')) {
existingPhaseButton.click(); existingPhaseButton.click();
} }
// Switch to grid view to show results
const gridToggle = document.querySelector('.view-toggle[data-view="grid"]'); const gridToggle = document.querySelector('.view-toggle[data-view="grid"]');
if (gridToggle && !gridToggle.classList.contains('active')) { if (gridToggle && !gridToggle.classList.contains('active')) {
gridToggle.click(); gridToggle.click();
} }
// Scroll to results using consolidated utility
window.scrollToElementById('tools-grid'); window.scrollToElementById('tools-grid');
}; };
@ -296,7 +285,6 @@ const phases = data.phases;
break; break;
} }
// Add smooth scrolling to filters section after layout settles
setTimeout(() => { setTimeout(() => {
window.scrollToElementById('filters-section'); window.scrollToElementById('filters-section');
}, 150); }, 150);
@ -369,7 +357,6 @@ const phases = data.phases;
}, 2000); }, 2000);
} else { } else {
console.warn('Tool card not found in grid:', toolName); console.warn('Tool card not found in grid:', toolName);
// Fallback to tools grid
window.scrollToElementById('tools-grid'); window.scrollToElementById('tools-grid');
} }
}, 300); }, 300);
@ -407,7 +394,6 @@ const phases = data.phases;
window.scrollToElement(firstMatch); window.scrollToElement(firstMatch);
} else { } else {
console.warn('Tool chip not found in matrix:', toolName); console.warn('Tool chip not found in matrix:', toolName);
// Fallback to matrix container
window.scrollToElementById('matrix-container'); window.scrollToElementById('matrix-container');
} }
}, 500); }, 500);

View File

@ -1,4 +1,4 @@
// src/utils/aiPipeline.ts - FIXED: Critical error corrections // src/utils/aiPipeline.ts
import { getCompressedToolsDataForAI } from './dataService.js'; import { getCompressedToolsDataForAI } from './dataService.js';
import { embeddingsService, type EmbeddingData } from './embeddings.js'; import { embeddingsService, type EmbeddingData } from './embeddings.js';
@ -36,7 +36,6 @@ interface AnalysisContext {
filteredData: any; filteredData: any;
contextHistory: string[]; contextHistory: string[];
// FIXED: Add max context length tracking
maxContextLength: number; maxContextLength: number;
currentContextLength: number; currentContextLength: number;
@ -47,7 +46,6 @@ interface AnalysisContext {
selectedTools?: Array<{tool: any, phase: string, priority: string, justification?: string}>; selectedTools?: Array<{tool: any, phase: string, priority: string, justification?: string}>;
backgroundKnowledge?: Array<{concept: any, relevance: string}>; backgroundKnowledge?: Array<{concept: any, relevance: string}>;
// FIXED: Add seen tools tracking to prevent duplicates
seenToolNames: Set<string>; seenToolNames: Set<string>;
} }
@ -58,7 +56,6 @@ class ImprovedMicroTaskAIPipeline {
private similarityThreshold: number; private similarityThreshold: number;
private microTaskDelay: number; private microTaskDelay: number;
// FIXED: Add proper token management
private maxContextTokens: number; private maxContextTokens: number;
private maxPromptTokens: number; private maxPromptTokens: number;
@ -74,7 +71,6 @@ class ImprovedMicroTaskAIPipeline {
this.similarityThreshold = 0.3; this.similarityThreshold = 0.3;
this.microTaskDelay = parseInt(process.env.AI_MICRO_TASK_DELAY_MS || '500', 10); this.microTaskDelay = parseInt(process.env.AI_MICRO_TASK_DELAY_MS || '500', 10);
// FIXED: Token management
this.maxContextTokens = parseInt(process.env.AI_MAX_CONTEXT_TOKENS || '4000', 10); this.maxContextTokens = parseInt(process.env.AI_MAX_CONTEXT_TOKENS || '4000', 10);
this.maxPromptTokens = parseInt(process.env.AI_MAX_PROMPT_TOKENS || '1500', 10); this.maxPromptTokens = parseInt(process.env.AI_MAX_PROMPT_TOKENS || '1500', 10);
} }
@ -87,27 +83,22 @@ class ImprovedMicroTaskAIPipeline {
return value; return value;
} }
// FIXED: Estimate token count (rough approximation)
private estimateTokens(text: string): number { private estimateTokens(text: string): number {
return Math.ceil(text.length / 4); // Rough estimate: 4 chars per token return Math.ceil(text.length / 4);
} }
// FIXED: Manage context history with token limits
private addToContextHistory(context: AnalysisContext, newEntry: string): void { private addToContextHistory(context: AnalysisContext, newEntry: string): void {
const entryTokens = this.estimateTokens(newEntry); const entryTokens = this.estimateTokens(newEntry);
// Add new entry
context.contextHistory.push(newEntry); context.contextHistory.push(newEntry);
context.currentContextLength += entryTokens; context.currentContextLength += entryTokens;
// Prune old entries if exceeding limits
while (context.currentContextLength > this.maxContextTokens && context.contextHistory.length > 1) { while (context.currentContextLength > this.maxContextTokens && context.contextHistory.length > 1) {
const removed = context.contextHistory.shift()!; const removed = context.contextHistory.shift()!;
context.currentContextLength -= this.estimateTokens(removed); context.currentContextLength -= this.estimateTokens(removed);
} }
} }
// FIXED: Safe JSON parsing with validation
private safeParseJSON(jsonString: string, fallback: any = null): any { private safeParseJSON(jsonString: string, fallback: any = null): any {
try { try {
const cleaned = jsonString const cleaned = jsonString
@ -124,7 +115,6 @@ class ImprovedMicroTaskAIPipeline {
} }
} }
// FIXED: Add tool deduplication
private addToolToSelection(context: AnalysisContext, tool: any, phase: string, priority: string, justification?: string): boolean { private addToolToSelection(context: AnalysisContext, tool: any, phase: string, priority: string, justification?: string): boolean {
if (context.seenToolNames.has(tool.name)) { if (context.seenToolNames.has(tool.name)) {
console.log(`[AI PIPELINE] Skipping duplicate tool: ${tool.name}`); console.log(`[AI PIPELINE] Skipping duplicate tool: ${tool.name}`);
@ -166,8 +156,7 @@ class ImprovedMicroTaskAIPipeline {
console.log(`[IMPROVED PIPELINE] Embeddings found: ${toolNames.size} tools, ${conceptNames.size} concepts`); console.log(`[IMPROVED PIPELINE] Embeddings found: ${toolNames.size} tools, ${conceptNames.size} concepts`);
// FIXED: Use your expected flow - get full data of embeddings results if (toolNames.size >= 15) {
if (toolNames.size >= 15) { // Reasonable threshold for quality
candidateTools = toolsData.tools.filter((tool: any) => toolNames.has(tool.name)); candidateTools = toolsData.tools.filter((tool: any) => toolNames.has(tool.name));
candidateConcepts = toolsData.concepts.filter((concept: any) => conceptNames.has(concept.name)); candidateConcepts = toolsData.concepts.filter((concept: any) => conceptNames.has(concept.name));
selectionMethod = 'embeddings_candidates'; selectionMethod = 'embeddings_candidates';
@ -186,7 +175,6 @@ class ImprovedMicroTaskAIPipeline {
selectionMethod = 'full_dataset'; selectionMethod = 'full_dataset';
} }
// FIXED: NOW AI ANALYZES FULL DATA of the candidates
console.log(`[IMPROVED PIPELINE] AI will analyze FULL DATA of ${candidateTools.length} candidate tools`); console.log(`[IMPROVED PIPELINE] AI will analyze FULL DATA of ${candidateTools.length} candidate tools`);
const finalSelection = await this.aiSelectionWithFullData(userQuery, candidateTools, candidateConcepts, mode, selectionMethod); const finalSelection = await this.aiSelectionWithFullData(userQuery, candidateTools, candidateConcepts, mode, selectionMethod);
@ -199,8 +187,6 @@ class ImprovedMicroTaskAIPipeline {
}; };
} }
// src/utils/aiPipeline.ts - FIXED: De-biased AI selection prompt
private async aiSelectionWithFullData( private async aiSelectionWithFullData(
userQuery: string, userQuery: string,
candidateTools: any[], candidateTools: any[],
@ -212,7 +198,6 @@ class ImprovedMicroTaskAIPipeline {
? 'The user wants a COMPREHENSIVE WORKFLOW with multiple tools/methods across different phases. Select 15-25 tools that cover the full investigation lifecycle.' ? 'The user wants a COMPREHENSIVE WORKFLOW with multiple tools/methods across different phases. Select 15-25 tools that cover the full investigation lifecycle.'
: 'The user wants SPECIFIC TOOLS/METHODS that directly solve their particular problem. Select 3-8 tools that are most relevant and effective.'; : 'The user wants SPECIFIC TOOLS/METHODS that directly solve their particular problem. Select 3-8 tools that are most relevant and effective.';
// FIXED: Give AI the COMPLETE tool data, not truncated
const toolsWithFullData = candidateTools.map((tool: any) => ({ const toolsWithFullData = candidateTools.map((tool: any) => ({
name: tool.name, name: tool.name,
type: tool.type, type: tool.type,
@ -307,7 +292,7 @@ Respond with ONLY this JSON format:
}`; }`;
try { try {
const response = await this.callAI(prompt, 2500); // More tokens for bias prevention logic const response = await this.callAI(prompt, 2500);
const result = this.safeParseJSON(response, null); const result = this.safeParseJSON(response, null);
@ -325,7 +310,6 @@ Respond with ONLY this JSON format:
console.log(`[IMPROVED PIPELINE] AI selected: ${result.selectedTools.length} tools, ${result.selectedConcepts.length} concepts`); console.log(`[IMPROVED PIPELINE] AI selected: ${result.selectedTools.length} tools, ${result.selectedConcepts.length} concepts`);
console.log(`[IMPROVED PIPELINE] AI reasoning: ${result.reasoning}`); console.log(`[IMPROVED PIPELINE] AI reasoning: ${result.reasoning}`);
// Return the actual tool/concept objects
const selectedTools = candidateTools.filter(tool => result.selectedTools.includes(tool.name)); const selectedTools = candidateTools.filter(tool => result.selectedTools.includes(tool.name));
const selectedConcepts = candidateConcepts.filter(concept => result.selectedConcepts.includes(concept.name)); const selectedConcepts = candidateConcepts.filter(concept => result.selectedConcepts.includes(concept.name));
@ -339,7 +323,6 @@ Respond with ONLY this JSON format:
} catch (error) { } catch (error) {
console.error('[IMPROVED PIPELINE] AI selection failed:', error); console.error('[IMPROVED PIPELINE] AI selection failed:', error);
// Emergency fallback with bias awareness
console.log('[IMPROVED PIPELINE] Using emergency keyword-based selection'); console.log('[IMPROVED PIPELINE] Using emergency keyword-based selection');
return this.emergencyKeywordSelection(userQuery, candidateTools, candidateConcepts, mode); return this.emergencyKeywordSelection(userQuery, candidateTools, candidateConcepts, mode);
} }
@ -349,7 +332,6 @@ Respond with ONLY this JSON format:
const queryLower = userQuery.toLowerCase(); const queryLower = userQuery.toLowerCase();
const keywords = queryLower.split(/\s+/).filter(word => word.length > 3); const keywords = queryLower.split(/\s+/).filter(word => word.length > 3);
// Score tools based on keyword matches in full data
const scoredTools = candidateTools.map(tool => { const scoredTools = candidateTools.map(tool => {
const toolText = ( const toolText = (
tool.name + ' ' + tool.name + ' ' +
@ -385,18 +367,15 @@ Respond with ONLY this JSON format:
private async callMicroTaskAI(prompt: string, context: AnalysisContext, maxTokens: number = 300): Promise<MicroTaskResult> { private async callMicroTaskAI(prompt: string, context: AnalysisContext, maxTokens: number = 300): Promise<MicroTaskResult> {
const startTime = Date.now(); const startTime = Date.now();
// FIXED: Build context prompt with token management
let contextPrompt = prompt; let contextPrompt = prompt;
if (context.contextHistory.length > 0) { if (context.contextHistory.length > 0) {
const contextSection = `BISHERIGE ANALYSE:\n${context.contextHistory.join('\n\n')}\n\nAKTUELLE AUFGABE:\n`; const contextSection = `BISHERIGE ANALYSE:\n${context.contextHistory.join('\n\n')}\n\nAKTUELLE AUFGABE:\n`;
const combinedPrompt = contextSection + prompt; const combinedPrompt = contextSection + prompt;
// Check if combined prompt exceeds limits
if (this.estimateTokens(combinedPrompt) <= this.maxPromptTokens) { if (this.estimateTokens(combinedPrompt) <= this.maxPromptTokens) {
contextPrompt = combinedPrompt; contextPrompt = combinedPrompt;
} else { } else {
console.warn('[AI PIPELINE] Context too long, using prompt only'); console.warn('[AI PIPELINE] Context too long, using prompt only');
// Could implement smarter context truncation here
} }
} }
@ -451,7 +430,6 @@ WICHTIG: Antworten Sie NUR in fließendem deutschen Text ohne Listen, Aufzählun
context.problemAnalysis = result.content; context.problemAnalysis = result.content;
} }
// FIXED: Use new context management
this.addToContextHistory(context, `${isWorkflow ? 'Szenario' : 'Problem'}-Analyse: ${result.content.slice(0, 200)}...`); this.addToContextHistory(context, `${isWorkflow ? 'Szenario' : 'Problem'}-Analyse: ${result.content.slice(0, 200)}...`);
} }
@ -559,7 +537,6 @@ Antworten Sie AUSSCHLIESSLICH mit diesem JSON-Format (kein zusätzlicher Text):
const result = await this.callMicroTaskAI(prompt, context, 450); const result = await this.callMicroTaskAI(prompt, context, 450);
if (result.success) { if (result.success) {
// FIXED: Safe JSON parsing with validation
const selections = this.safeParseJSON(result.content, []); const selections = this.safeParseJSON(result.content, []);
if (Array.isArray(selections)) { if (Array.isArray(selections)) {
@ -570,7 +547,6 @@ Antworten Sie AUSSCHLIESSLICH mit diesem JSON-Format (kein zusätzlicher Text):
validSelections.forEach((sel: any) => { validSelections.forEach((sel: any) => {
const tool = phaseTools.find((t: any) => t.name === sel.toolName); const tool = phaseTools.find((t: any) => t.name === sel.toolName);
if (tool) { if (tool) {
// FIXED: Use deduplication helper
this.addToolToSelection(context, tool, phase.id, sel.priority, sel.justification); this.addToolToSelection(context, tool, phase.id, sel.priority, sel.justification);
} }
}); });
@ -603,7 +579,6 @@ Bewerten Sie nach forensischen Standards und antworten Sie AUSSCHLIESSLICH mit d
const result = await this.callMicroTaskAI(prompt, context, 650); const result = await this.callMicroTaskAI(prompt, context, 650);
if (result.success) { if (result.success) {
// FIXED: Safe JSON parsing
const evaluation = this.safeParseJSON(result.content, { const evaluation = this.safeParseJSON(result.content, {
suitability_score: 'medium', suitability_score: 'medium',
detailed_explanation: 'Evaluation failed', detailed_explanation: 'Evaluation failed',
@ -613,7 +588,6 @@ Bewerten Sie nach forensischen Standards und antworten Sie AUSSCHLIESSLICH mit d
alternatives: '' alternatives: ''
}); });
// FIXED: Use deduplication helper
this.addToolToSelection(context, { this.addToolToSelection(context, {
...tool, ...tool,
evaluation: { evaluation: {
@ -661,7 +635,6 @@ Antworten Sie AUSSCHLIESSLICH mit diesem JSON-Format:
const result = await this.callMicroTaskAI(prompt, context, 400); const result = await this.callMicroTaskAI(prompt, context, 400);
if (result.success) { if (result.success) {
// FIXED: Safe JSON parsing
const selections = this.safeParseJSON(result.content, []); const selections = this.safeParseJSON(result.content, []);
if (Array.isArray(selections)) { if (Array.isArray(selections)) {
@ -745,7 +718,6 @@ WICHTIG: Antworten Sie NUR in fließendem deutschen Text ohne Listen oder Markdo
const toolsData = await getCompressedToolsDataForAI(); const toolsData = await getCompressedToolsDataForAI();
const filteredData = await this.getIntelligentCandidates(userQuery, toolsData, mode); const filteredData = await this.getIntelligentCandidates(userQuery, toolsData, mode);
// FIXED: Initialize context with proper state management
const context: AnalysisContext = { const context: AnalysisContext = {
userQuery, userQuery,
mode, mode,
@ -753,7 +725,7 @@ WICHTIG: Antworten Sie NUR in fließendem deutschen Text ohne Listen oder Markdo
contextHistory: [], contextHistory: [],
maxContextLength: this.maxContextTokens, maxContextLength: this.maxContextTokens,
currentContextLength: 0, currentContextLength: 0,
seenToolNames: new Set<string>() // FIXED: Add deduplication tracking seenToolNames: new Set<string>()
}; };
console.log(`[IMPROVED PIPELINE] Starting micro-tasks with ${filteredData.tools.length} tools visible`); console.log(`[IMPROVED PIPELINE] Starting micro-tasks with ${filteredData.tools.length} tools visible`);
@ -777,7 +749,6 @@ WICHTIG: Antworten Sie NUR in fließendem deutschen Text ohne Listen oder Markdo
// Task 4: Tool Selection/Evaluation (mode-dependent) // Task 4: Tool Selection/Evaluation (mode-dependent)
if (mode === 'workflow') { if (mode === 'workflow') {
// Select tools for each phase
const phases = toolsData.phases || []; const phases = toolsData.phases || [];
for (const phase of phases) { for (const phase of phases) {
const toolSelectionResult = await this.selectToolsForPhase(context, phase); const toolSelectionResult = await this.selectToolsForPhase(context, phase);
@ -785,7 +756,6 @@ WICHTIG: Antworten Sie NUR in fließendem deutschen Text ohne Listen oder Markdo
await this.delay(this.microTaskDelay); await this.delay(this.microTaskDelay);
} }
} else { } else {
// Evaluate top 3 tools for specific problem
const topTools = filteredData.tools.slice(0, 3); const topTools = filteredData.tools.slice(0, 3);
for (let i = 0; i < topTools.length; i++) { for (let i = 0; i < topTools.length; i++) {
const evaluationResult = await this.evaluateSpecificTool(context, topTools[i], i + 1); const evaluationResult = await this.evaluateSpecificTool(context, topTools[i], i + 1);
@ -831,7 +801,6 @@ WICHTIG: Antworten Sie NUR in fließendem deutschen Text ohne Listen oder Markdo
} }
} }
// Build recommendation (same structure but using fixed context)
private buildRecommendation(context: AnalysisContext, mode: string, finalContent: string): any { private buildRecommendation(context: AnalysisContext, mode: string, finalContent: string): any {
const isWorkflow = mode === 'workflow'; const isWorkflow = mode === 'workflow';
@ -876,7 +845,6 @@ WICHTIG: Antworten Sie NUR in fließendem deutschen Text ohne Listen oder Markdo
} }
} }
// Global instance
const aiPipeline = new ImprovedMicroTaskAIPipeline(); const aiPipeline = new ImprovedMicroTaskAIPipeline();
export { aiPipeline, type AnalysisResult }; export { aiPipeline, type AnalysisResult };

View File

@ -111,7 +111,6 @@ export const apiSpecial = {
}; };
export const apiWithHeaders = { export const apiWithHeaders = {
// Success with custom headers (e.g., Set-Cookie)
successWithHeaders: (data: any, headers: Record<string, string>): Response => successWithHeaders: (data: any, headers: Record<string, string>): Response =>
createAPIResponse(data, 200, headers), createAPIResponse(data, 200, headers),