remove mindless dev comments
This commit is contained in:
		
							parent
							
								
									3973479ae4
								
							
						
					
					
						commit
						57c507915f
					
				@ -211,12 +211,6 @@ const domainAgnosticSoftware = data['domain-agnostic-software'] || [];
 | 
			
		||||
</section>
 | 
			
		||||
 | 
			
		||||
<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 {
 | 
			
		||||
  constructor() {
 | 
			
		||||
@ -234,7 +228,6 @@ class AIQueryInterface {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  initializeElements() {
 | 
			
		||||
    // Core elements
 | 
			
		||||
    this.elements = {
 | 
			
		||||
      input: document.getElementById('ai-query-input'),
 | 
			
		||||
      submitBtn: document.getElementById('ai-submit-btn'),
 | 
			
		||||
@ -247,13 +240,11 @@ class AIQueryInterface {
 | 
			
		||||
      results: document.getElementById('ai-results'),
 | 
			
		||||
      charCounter: document.getElementById('ai-char-counter'),
 | 
			
		||||
      
 | 
			
		||||
      // Mode toggle
 | 
			
		||||
      toggleSwitch: document.querySelector('.toggle-switch'),
 | 
			
		||||
      toggleSlider: document.querySelector('.toggle-slider'),
 | 
			
		||||
      workflowLabel: document.getElementById('workflow-label'),
 | 
			
		||||
      toolLabel: document.getElementById('tool-label'),
 | 
			
		||||
      
 | 
			
		||||
      // Smart prompting
 | 
			
		||||
      promptingContainer: document.getElementById('smart-prompting-container'),
 | 
			
		||||
      promptingStatus: document.getElementById('prompting-status'),
 | 
			
		||||
      promptingSpinner: document.getElementById('prompting-spinner'),
 | 
			
		||||
@ -262,7 +253,6 @@ class AIQueryInterface {
 | 
			
		||||
      dismissSuggestions: document.getElementById('dismiss-suggestions'),
 | 
			
		||||
      smartHint: document.getElementById('smart-prompting-hint'),
 | 
			
		||||
      
 | 
			
		||||
      // Queue status
 | 
			
		||||
      queueStatus: document.getElementById('queue-status'),
 | 
			
		||||
      queueLength: document.getElementById('queue-length'),
 | 
			
		||||
      estimatedTime: document.getElementById('estimated-time'),
 | 
			
		||||
@ -273,7 +263,6 @@ class AIQueryInterface {
 | 
			
		||||
      microTaskCounter: document.getElementById('micro-task-counter')
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    // Validate critical elements
 | 
			
		||||
    if (!this.elements.input || !this.elements.submitBtn || !this.elements.results) {
 | 
			
		||||
      console.error('[AI Interface] Critical elements not found');
 | 
			
		||||
      return false;
 | 
			
		||||
@ -282,12 +271,10 @@ class AIQueryInterface {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  setupEventListeners() {
 | 
			
		||||
    // Mode toggle
 | 
			
		||||
    this.elements.toggleSwitch?.addEventListener('click', () => this.toggleMode());
 | 
			
		||||
    this.elements.workflowLabel?.addEventListener('click', () => this.setMode('workflow'));
 | 
			
		||||
    this.elements.toolLabel?.addEventListener('click', () => this.setMode('tool'));
 | 
			
		||||
 | 
			
		||||
    // Input handling
 | 
			
		||||
    this.elements.input?.addEventListener('input', () => this.handleInputChange());
 | 
			
		||||
    this.elements.input?.addEventListener('keydown', (e) => {
 | 
			
		||||
      if (e.key === 'Enter' && (e.ctrlKey || e.metaKey)) {
 | 
			
		||||
@ -296,17 +283,11 @@ class AIQueryInterface {
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    // Submit
 | 
			
		||||
    this.elements.submitBtn?.addEventListener('click', () => this.handleSubmit());
 | 
			
		||||
 | 
			
		||||
    // Smart prompting
 | 
			
		||||
    this.elements.dismissSuggestions?.addEventListener('click', () => this.hideSmartPrompting());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // ===================================================================
 | 
			
		||||
  // MODE MANAGEMENT
 | 
			
		||||
  // ===================================================================
 | 
			
		||||
 | 
			
		||||
  getModeConfig() {
 | 
			
		||||
    return {
 | 
			
		||||
      workflow: {
 | 
			
		||||
@ -345,7 +326,6 @@ class AIQueryInterface {
 | 
			
		||||
    if (this.elements.submitBtnText) this.elements.submitBtnText.textContent = config.submitText;
 | 
			
		||||
    if (this.elements.loadingText) this.elements.loadingText.textContent = config.loadingText;
 | 
			
		||||
    
 | 
			
		||||
    // Update toggle UI
 | 
			
		||||
    if (this.currentMode === 'workflow') {
 | 
			
		||||
      if (this.elements.toggleSlider) this.elements.toggleSlider.style.transform = 'translateX(0)';
 | 
			
		||||
      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() {
 | 
			
		||||
    this.updateCharacterCount();
 | 
			
		||||
    
 | 
			
		||||
@ -493,10 +471,6 @@ class AIQueryInterface {
 | 
			
		||||
    if (this.elements.smartHint) this.showElement(this.elements.smartHint);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // ===================================================================
 | 
			
		||||
  // MAIN SUBMIT HANDLER
 | 
			
		||||
  // ===================================================================
 | 
			
		||||
 | 
			
		||||
  async handleSubmit() {
 | 
			
		||||
    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)}`;
 | 
			
		||||
    
 | 
			
		||||
    // Reset state
 | 
			
		||||
    this.hideSmartPrompting();
 | 
			
		||||
    this.hideResults();
 | 
			
		||||
    this.hideError();
 | 
			
		||||
    this.showLoading();
 | 
			
		||||
    this.startQueueMonitoring(taskId);
 | 
			
		||||
    
 | 
			
		||||
    // Disable submit
 | 
			
		||||
    this.elements.submitBtn.disabled = true;
 | 
			
		||||
    this.elements.submitBtnText.textContent = this.currentMode === 'workflow' ? 
 | 
			
		||||
      'Generiere Empfehlungen...' : 'Suche passende Methode...';
 | 
			
		||||
@ -545,7 +517,6 @@ class AIQueryInterface {
 | 
			
		||||
        throw new Error(data.error || 'Unknown error');
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      // Store and display results
 | 
			
		||||
      this.currentRecommendation = data.recommendation;
 | 
			
		||||
      this.displayResults(data.recommendation, query);
 | 
			
		||||
      
 | 
			
		||||
@ -579,8 +550,6 @@ class AIQueryInterface {
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // [Queue monitoring methods remain the same...]
 | 
			
		||||
 | 
			
		||||
  startQueueMonitoring(taskId) {
 | 
			
		||||
    if (this.elements.queueStatus) {
 | 
			
		||||
      this.showElement(this.elements.queueStatus);
 | 
			
		||||
@ -592,7 +561,6 @@ class AIQueryInterface {
 | 
			
		||||
    
 | 
			
		||||
    this.startMicroTaskProgress();
 | 
			
		||||
    
 | 
			
		||||
    // Start polling queue status
 | 
			
		||||
    setTimeout(() => {
 | 
			
		||||
      this.updateQueueStatus(taskId);
 | 
			
		||||
      this.statusInterval = setInterval(() => this.updateQueueStatus(taskId), 1000);
 | 
			
		||||
@ -606,14 +574,12 @@ class AIQueryInterface {
 | 
			
		||||
      
 | 
			
		||||
      const data = await response.json();
 | 
			
		||||
      
 | 
			
		||||
      // Update queue display
 | 
			
		||||
      if (this.elements.queueLength) this.elements.queueLength.textContent = data.queueLength || 0;
 | 
			
		||||
      if (this.elements.estimatedTime) {
 | 
			
		||||
        this.elements.estimatedTime.textContent = data.estimatedWaitTime > 0 ? 
 | 
			
		||||
          this.formatDuration(data.estimatedWaitTime) : 'Verarbeitung läuft...';
 | 
			
		||||
      }
 | 
			
		||||
      
 | 
			
		||||
      // Update position
 | 
			
		||||
      if (this.elements.positionBadge) {
 | 
			
		||||
        if (data.currentPosition) {
 | 
			
		||||
          this.elements.positionBadge.textContent = data.currentPosition;
 | 
			
		||||
@ -622,7 +588,6 @@ class AIQueryInterface {
 | 
			
		||||
            this.elements.progressBar.style.width = `${progress}%`;
 | 
			
		||||
          }
 | 
			
		||||
        } else {
 | 
			
		||||
          // Handle processing states
 | 
			
		||||
          const stateIcons = {
 | 
			
		||||
            processing: '⚡',
 | 
			
		||||
            completed: '✅', 
 | 
			
		||||
@ -664,14 +629,12 @@ class AIQueryInterface {
 | 
			
		||||
    const steps = ['scenario', 'approach', 'considerations', 'tools', 'knowledge', 'final'];
 | 
			
		||||
    const stepElements = this.elements.microTaskProgress.querySelectorAll('.micro-step');
 | 
			
		||||
    
 | 
			
		||||
    // Reset all steps
 | 
			
		||||
    stepElements.forEach(step => {
 | 
			
		||||
      step.classList.remove('active', 'completed', 'failed');
 | 
			
		||||
    });
 | 
			
		||||
    
 | 
			
		||||
    this.microTaskInterval = setInterval(() => {
 | 
			
		||||
      if (this.currentMicroTaskStep < steps.length) {
 | 
			
		||||
        // Complete previous step
 | 
			
		||||
        if (this.currentMicroTaskStep > 0) {
 | 
			
		||||
          const prevStep = this.elements.microTaskProgress.querySelector(`[data-step="${steps[this.currentMicroTaskStep - 1]}"]`);
 | 
			
		||||
          if (prevStep) {
 | 
			
		||||
@ -680,20 +643,17 @@ class AIQueryInterface {
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        // Activate current step
 | 
			
		||||
        const currentStep = this.elements.microTaskProgress.querySelector(`[data-step="${steps[this.currentMicroTaskStep]}"]`);
 | 
			
		||||
        if (currentStep) {
 | 
			
		||||
          currentStep.classList.add('active');
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        // Update counter
 | 
			
		||||
        if (this.elements.microTaskCounter) {
 | 
			
		||||
          this.elements.microTaskCounter.textContent = `${this.currentMicroTaskStep + 1}/${steps.length}`;
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        this.currentMicroTaskStep++;
 | 
			
		||||
      } else {
 | 
			
		||||
        // Complete final step
 | 
			
		||||
        const lastStep = this.elements.microTaskProgress.querySelector(`[data-step="${steps[steps.length - 1]}"]`);
 | 
			
		||||
        if (lastStep) {
 | 
			
		||||
          lastStep.classList.remove('active');
 | 
			
		||||
@ -710,10 +670,6 @@ class AIQueryInterface {
 | 
			
		||||
    }, 2000);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // ===================================================================
 | 
			
		||||
  // RESULTS DISPLAY - ENHANCED with proper text formatting
 | 
			
		||||
  // ===================================================================
 | 
			
		||||
 | 
			
		||||
  displayResults(recommendation, originalQuery) {
 | 
			
		||||
    if (this.currentMode === 'workflow') {
 | 
			
		||||
      this.displayWorkflowResults(recommendation, originalQuery);
 | 
			
		||||
@ -725,7 +681,6 @@ class AIQueryInterface {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  displayWorkflowResults(recommendation, originalQuery) {
 | 
			
		||||
    // Group tools by phase
 | 
			
		||||
    const toolsByPhase = {};
 | 
			
		||||
    const phaseOrder = phases.map(phase => phase.id);
 | 
			
		||||
    const phaseNames = phases.reduce((acc, phase) => {
 | 
			
		||||
@ -787,7 +742,6 @@ class AIQueryInterface {
 | 
			
		||||
    `;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // ENHANCED: Fixed text rendering to prevent markdown/list interpretation
 | 
			
		||||
  renderContextualAnalysis(recommendation, mode) {
 | 
			
		||||
    let html = '';
 | 
			
		||||
    
 | 
			
		||||
@ -1052,15 +1006,9 @@ class AIQueryInterface {
 | 
			
		||||
    return badges;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // ===================================================================
 | 
			
		||||
  // ENHANCED UTILITY METHODS with proper text sanitization
 | 
			
		||||
  // ===================================================================
 | 
			
		||||
 | 
			
		||||
  // CRITICAL: Sanitize text to prevent markdown interpretation
 | 
			
		||||
  sanitizeText(text) {
 | 
			
		||||
    if (typeof text !== 'string') return '';
 | 
			
		||||
    
 | 
			
		||||
    // Remove/escape potential markdown and HTML
 | 
			
		||||
    return text
 | 
			
		||||
      // Remove markdown headers
 | 
			
		||||
      .replace(/^#{1,6}\s+/gm, '')
 | 
			
		||||
@ -1096,13 +1044,11 @@ class AIQueryInterface {
 | 
			
		||||
    const hasValidProjectUrl = this.isToolHosted(tool);
 | 
			
		||||
    
 | 
			
		||||
    if (context === 'recommendation') {
 | 
			
		||||
      // For tool-recommendation elements, use simple class names
 | 
			
		||||
      if (isMethod) return 'method';
 | 
			
		||||
      if (hasValidProjectUrl) return 'hosted';
 | 
			
		||||
      if (tool.license !== 'Proprietary') return 'oss';
 | 
			
		||||
      return '';
 | 
			
		||||
    } else {
 | 
			
		||||
      // For card elements, use card- prefixed class names
 | 
			
		||||
      if (isMethod) return 'card-method';
 | 
			
		||||
      if (hasValidProjectUrl) return 'card-hosted';
 | 
			
		||||
      if (tool.license !== 'Proprietary') return 'card-oss';
 | 
			
		||||
@ -1137,7 +1083,6 @@ class AIQueryInterface {
 | 
			
		||||
    return `${Math.ceil(ms / 60000)}m`;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // State management
 | 
			
		||||
  showElement(element) {
 | 
			
		||||
    if (element) {
 | 
			
		||||
      element.style.display = 'block';
 | 
			
		||||
@ -1179,14 +1124,9 @@ class AIQueryInterface {
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ===================================================================
 | 
			
		||||
// INITIALIZATION
 | 
			
		||||
// ===================================================================
 | 
			
		||||
 | 
			
		||||
document.addEventListener('DOMContentLoaded', () => {
 | 
			
		||||
  const aiInterface = new AIQueryInterface();
 | 
			
		||||
  
 | 
			
		||||
  // Global restore function for compatibility
 | 
			
		||||
  window.restoreAIResults = () => {
 | 
			
		||||
    if (aiInterface.currentRecommendation && aiInterface.elements.results) {
 | 
			
		||||
      aiInterface.showResults();
 | 
			
		||||
 | 
			
		||||
@ -77,41 +77,33 @@ const displayedScenarios = scenarios.slice(0, maxDisplayed);
 | 
			
		||||
    
 | 
			
		||||
    if (!mainSearchInput) return;
 | 
			
		||||
    
 | 
			
		||||
    // Check if this scenario is already active (allow deselection)
 | 
			
		||||
    if (clickedChip && clickedChip.classList.contains('active')) {
 | 
			
		||||
      // Deselect: clear search and remove active state
 | 
			
		||||
      mainSearchInput.value = '';
 | 
			
		||||
      document.querySelectorAll('.suggestion-chip').forEach(chip => {
 | 
			
		||||
        chip.classList.remove('active');
 | 
			
		||||
      });
 | 
			
		||||
      
 | 
			
		||||
      // Clear the targeted search input too
 | 
			
		||||
      const targetedInput = document.getElementById('targeted-search-input');
 | 
			
		||||
      if (targetedInput) {
 | 
			
		||||
        targetedInput.value = '';
 | 
			
		||||
      }
 | 
			
		||||
      
 | 
			
		||||
      // Trigger search to show all results
 | 
			
		||||
      const inputEvent = new Event('input', { bubbles: true });
 | 
			
		||||
      mainSearchInput.dispatchEvent(inputEvent);
 | 
			
		||||
      
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    // Apply new search
 | 
			
		||||
    mainSearchInput.value = scenarioId;
 | 
			
		||||
    
 | 
			
		||||
    // Trigger existing search functionality
 | 
			
		||||
    const inputEvent = new Event('input', { bubbles: true });
 | 
			
		||||
    mainSearchInput.dispatchEvent(inputEvent);
 | 
			
		||||
    
 | 
			
		||||
    // Switch to grid view
 | 
			
		||||
    const gridToggle = document.querySelector('.view-toggle[data-view="grid"]');
 | 
			
		||||
    if (gridToggle && !gridToggle.classList.contains('active')) {
 | 
			
		||||
      gridToggle.click();
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    // Visual feedback
 | 
			
		||||
    document.querySelectorAll('.suggestion-chip').forEach(chip => {
 | 
			
		||||
      chip.classList.remove('active');
 | 
			
		||||
    });
 | 
			
		||||
@ -119,17 +111,14 @@ const displayedScenarios = scenarios.slice(0, maxDisplayed);
 | 
			
		||||
      clickedChip.classList.add('active');
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    // Scroll to results with better positioning
 | 
			
		||||
    window.scrollToElementById('tools-grid');
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  // Toggle showing all scenarios
 | 
			
		||||
  window.toggleAllScenarios = function() {
 | 
			
		||||
    const suggestionsContainer = document.getElementById('scenario-suggestions');
 | 
			
		||||
    const moreBtn = document.getElementById('more-scenarios-btn');
 | 
			
		||||
    
 | 
			
		||||
    if (!showingAllScenarios) {
 | 
			
		||||
      // Show additional scenarios
 | 
			
		||||
      const additionalScenarios = allScenarios.slice(maxDisplay);
 | 
			
		||||
      additionalScenarios.forEach(scenario => {
 | 
			
		||||
        const chip = document.createElement('div');
 | 
			
		||||
@ -146,14 +135,12 @@ const displayedScenarios = scenarios.slice(0, maxDisplayed);
 | 
			
		||||
      moreBtn.textContent = 'Weniger anzeigen';
 | 
			
		||||
      showingAllScenarios = true;
 | 
			
		||||
    } else {
 | 
			
		||||
      // Hide additional scenarios
 | 
			
		||||
      document.querySelectorAll('.additional-scenario').forEach(chip => chip.remove());
 | 
			
		||||
      moreBtn.textContent = `+ ${allScenarios.length - maxDisplay} weitere Szenarien`;
 | 
			
		||||
      showingAllScenarios = false;
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  // Handle targeted search input
 | 
			
		||||
  document.addEventListener('DOMContentLoaded', () => {
 | 
			
		||||
    const targetedInput = document.getElementById('targeted-search-input');
 | 
			
		||||
    if (targetedInput) {
 | 
			
		||||
 | 
			
		||||
@ -5,7 +5,6 @@ const data = await getToolsData();
 | 
			
		||||
const domains = data.domains;
 | 
			
		||||
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 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();
 | 
			
		||||
@ -287,7 +286,6 @@ const sortedTags = Object.entries(tagFrequency)
 | 
			
		||||
  window.toolsData = toolsData;
 | 
			
		||||
  
 | 
			
		||||
  document.addEventListener('DOMContentLoaded', () => {
 | 
			
		||||
    // Cache DOM elements
 | 
			
		||||
    const elements = {
 | 
			
		||||
      searchInput: document.getElementById('search-input'),
 | 
			
		||||
      clearSearch: document.getElementById('clear-search'),
 | 
			
		||||
@ -312,25 +310,21 @@ const sortedTags = Object.entries(tagFrequency)
 | 
			
		||||
        tags: document.getElementById('reset-tags'),
 | 
			
		||||
        all: document.getElementById('reset-all-filters')
 | 
			
		||||
      },
 | 
			
		||||
      // Collapsible elements
 | 
			
		||||
      toggleAdvanced: document.getElementById('toggle-advanced'),
 | 
			
		||||
      toggleTags: document.getElementById('toggle-tags'),
 | 
			
		||||
      advancedContent: document.getElementById('advanced-filters-content'),
 | 
			
		||||
      tagContent: document.getElementById('tag-filters-content')
 | 
			
		||||
    };
 | 
			
		||||
    
 | 
			
		||||
    // Verify critical elements exist
 | 
			
		||||
    if (!elements.searchInput || !elements.domainSelect) {
 | 
			
		||||
      console.error('Critical filter elements not found');
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    // State management
 | 
			
		||||
    let selectedTags = new Set();
 | 
			
		||||
    let selectedPhase = '';
 | 
			
		||||
    let isTagCloudExpanded = false;
 | 
			
		||||
    
 | 
			
		||||
    // Collapsible functionality
 | 
			
		||||
    function toggleCollapsible(toggleBtn, content, storageKey) {
 | 
			
		||||
      const isCollapsed = toggleBtn.getAttribute('data-collapsed') === 'true';
 | 
			
		||||
      const newState = !isCollapsed;
 | 
			
		||||
@ -338,22 +332,17 @@ const sortedTags = Object.entries(tagFrequency)
 | 
			
		||||
      toggleBtn.setAttribute('data-collapsed', newState.toString());
 | 
			
		||||
      
 | 
			
		||||
      if (newState) {
 | 
			
		||||
        // Collapse
 | 
			
		||||
        content.classList.add('hidden');
 | 
			
		||||
        toggleBtn.style.transform = 'rotate(0deg)';
 | 
			
		||||
      } else {
 | 
			
		||||
        // Expand
 | 
			
		||||
        content.classList.remove('hidden');
 | 
			
		||||
        toggleBtn.style.transform = 'rotate(180deg)';
 | 
			
		||||
      }
 | 
			
		||||
      
 | 
			
		||||
      // Store state in sessionStorage
 | 
			
		||||
      sessionStorage.setItem(storageKey, newState.toString());
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    // Initialize collapsible sections (collapsed by default)
 | 
			
		||||
    function initializeCollapsible() {
 | 
			
		||||
      // Advanced filters
 | 
			
		||||
      const advancedCollapsed = sessionStorage.getItem('advanced-collapsed') !== 'false';
 | 
			
		||||
      elements.toggleAdvanced.setAttribute('data-collapsed', advancedCollapsed.toString());
 | 
			
		||||
      if (advancedCollapsed) {
 | 
			
		||||
@ -364,7 +353,6 @@ const sortedTags = Object.entries(tagFrequency)
 | 
			
		||||
        elements.toggleAdvanced.style.transform = 'rotate(180deg)';
 | 
			
		||||
      }
 | 
			
		||||
      
 | 
			
		||||
      // Tag filters
 | 
			
		||||
      const tagsCollapsed = sessionStorage.getItem('tags-collapsed') !== 'false';
 | 
			
		||||
      elements.toggleTags.setAttribute('data-collapsed', tagsCollapsed.toString());
 | 
			
		||||
      if (tagsCollapsed) {
 | 
			
		||||
@ -376,7 +364,6 @@ const sortedTags = Object.entries(tagFrequency)
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    // Helper function to check if tool is hosted
 | 
			
		||||
    function isToolHosted(tool) {
 | 
			
		||||
      return tool.projectUrl !== undefined && 
 | 
			
		||||
             tool.projectUrl !== null && 
 | 
			
		||||
@ -384,7 +371,6 @@ const sortedTags = Object.entries(tagFrequency)
 | 
			
		||||
             tool.projectUrl.trim() !== "";
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    // Initialize tag cloud
 | 
			
		||||
    function initTagCloud() {
 | 
			
		||||
      const visibleCount = 20;
 | 
			
		||||
      elements.tagCloudItems.forEach((item, index) => {
 | 
			
		||||
@ -394,7 +380,6 @@ const sortedTags = Object.entries(tagFrequency)
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    // Toggle tag cloud expansion
 | 
			
		||||
    function toggleTagCloud() {
 | 
			
		||||
      isTagCloudExpanded = !isTagCloudExpanded;
 | 
			
		||||
      const visibleCount = 20;
 | 
			
		||||
@ -428,7 +413,6 @@ const sortedTags = Object.entries(tagFrequency)
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    // Filter tag cloud based on search
 | 
			
		||||
    function filterTagCloud() {
 | 
			
		||||
      const searchTerm = elements.searchInput.value.toLowerCase();
 | 
			
		||||
      let visibleCount = 0;
 | 
			
		||||
@ -458,7 +442,6 @@ const sortedTags = Object.entries(tagFrequency)
 | 
			
		||||
      elements.tagCloudToggle.style.display = hasHiddenTags ? 'block' : 'none';
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    // Update selected tags display
 | 
			
		||||
    function updateSelectedTags() {
 | 
			
		||||
      if (selectedTags.size === 0) {
 | 
			
		||||
        elements.selectedTags.style.display = 'none';
 | 
			
		||||
@ -478,7 +461,6 @@ const sortedTags = Object.entries(tagFrequency)
 | 
			
		||||
        </div>
 | 
			
		||||
      `;
 | 
			
		||||
      
 | 
			
		||||
      // Add event listeners for remove buttons
 | 
			
		||||
      elements.selectedTags.querySelectorAll('.remove-tag').forEach(btn => {
 | 
			
		||||
        btn.addEventListener('click', () => {
 | 
			
		||||
          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) {
 | 
			
		||||
      selectedTags.add(tag);
 | 
			
		||||
      // FIXED: Use querySelectorAll to update ALL matching tag elements
 | 
			
		||||
      document.querySelectorAll(`[data-tag="${tag}"]`).forEach(element => {
 | 
			
		||||
        element.classList.add('active');
 | 
			
		||||
      });
 | 
			
		||||
@ -500,7 +480,6 @@ const sortedTags = Object.entries(tagFrequency)
 | 
			
		||||
    
 | 
			
		||||
    function removeTag(tag) {
 | 
			
		||||
      selectedTags.delete(tag);
 | 
			
		||||
      // FIXED: Use querySelectorAll to update ALL matching tag elements
 | 
			
		||||
      document.querySelectorAll(`[data-tag="${tag}"]`).forEach(element => {
 | 
			
		||||
        element.classList.remove('active');
 | 
			
		||||
      });
 | 
			
		||||
@ -508,7 +487,6 @@ const sortedTags = Object.entries(tagFrequency)
 | 
			
		||||
      filterTools();
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    // Update results counter
 | 
			
		||||
    function updateResultsCounter(count) {
 | 
			
		||||
      const total = window.toolsData.length;
 | 
			
		||||
      elements.resultsCounter.textContent = count === total 
 | 
			
		||||
@ -516,7 +494,6 @@ const sortedTags = Object.entries(tagFrequency)
 | 
			
		||||
        : `${count} von ${total} Tools`;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    // Main filter function
 | 
			
		||||
    function filterTools() {
 | 
			
		||||
      const searchTerm = elements.searchInput.value.trim().toLowerCase();
 | 
			
		||||
      const selectedDomain = elements.domainSelect.value;
 | 
			
		||||
@ -529,11 +506,9 @@ const sortedTags = Object.entries(tagFrequency)
 | 
			
		||||
      const hostedOnly = elements.hostedOnly.checked;
 | 
			
		||||
      const knowledgebaseOnly = elements.knowledgebaseOnly.checked;
 | 
			
		||||
      
 | 
			
		||||
      // Use phase from either dropdown or button selection
 | 
			
		||||
      const activePhase = selectedPhaseFromSelect || selectedPhase;
 | 
			
		||||
      
 | 
			
		||||
      const filtered = window.toolsData.filter(tool => {
 | 
			
		||||
        // Search filter
 | 
			
		||||
        if (searchTerm && !(
 | 
			
		||||
          tool.name.toLowerCase().includes(searchTerm) ||
 | 
			
		||||
          tool.description.toLowerCase().includes(searchTerm) ||
 | 
			
		||||
@ -542,52 +517,42 @@ const sortedTags = Object.entries(tagFrequency)
 | 
			
		||||
          return false;
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        // Domain filter
 | 
			
		||||
        if (selectedDomain && !(tool.domains || []).includes(selectedDomain)) {
 | 
			
		||||
          return false;
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        // Phase filter
 | 
			
		||||
        if (activePhase && !(tool.phases || []).includes(activePhase)) {
 | 
			
		||||
          return false;
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        // Type filter
 | 
			
		||||
        if (selectedType && tool.type !== selectedType) {
 | 
			
		||||
          return false;
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        // Skill level filter
 | 
			
		||||
        if (selectedSkill && tool.skillLevel !== selectedSkill) {
 | 
			
		||||
          return false;
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        // Platform filter
 | 
			
		||||
        if (selectedPlatform && !(tool.platforms || []).includes(selectedPlatform)) {
 | 
			
		||||
          return false;
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        // License filter - NO MORE HARD-CODED LOGIC
 | 
			
		||||
        if (selectedLicense && tool.license !== selectedLicense) {
 | 
			
		||||
          return false;
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        // Access type filter
 | 
			
		||||
        if (selectedAccess && tool.accessType !== selectedAccess) {
 | 
			
		||||
          return false;
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        // Hosted only filter (CC24-Server tools)
 | 
			
		||||
        if (hostedOnly && !isToolHosted(tool)) {
 | 
			
		||||
          return false;
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        // Knowledgebase only filter
 | 
			
		||||
        if (knowledgebaseOnly && !tool.knowledgebase) {
 | 
			
		||||
          return false;
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        // Tag filter
 | 
			
		||||
        if (selectedTags.size > 0 && !Array.from(selectedTags).every(tag => (tool.tags || []).includes(tag))) {
 | 
			
		||||
          return false;
 | 
			
		||||
        }
 | 
			
		||||
@ -595,18 +560,15 @@ const sortedTags = Object.entries(tagFrequency)
 | 
			
		||||
        return true;
 | 
			
		||||
      });
 | 
			
		||||
      
 | 
			
		||||
      // Apply search prioritization if there's a search term
 | 
			
		||||
      const finalResults = searchTerm && window.prioritizeSearchResults 
 | 
			
		||||
        ? window.prioritizeSearchResults(filtered, searchTerm)
 | 
			
		||||
        : filtered;
 | 
			
		||||
      
 | 
			
		||||
      updateResultsCounter(finalResults.length);
 | 
			
		||||
      
 | 
			
		||||
      // Dispatch event for other components
 | 
			
		||||
      window.dispatchEvent(new CustomEvent('toolsFiltered', { detail: finalResults }));
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    // Reset functions
 | 
			
		||||
    function resetPrimaryFilters() {
 | 
			
		||||
      elements.domainSelect.value = '';
 | 
			
		||||
      elements.phaseSelect.value = '';
 | 
			
		||||
@ -627,7 +589,6 @@ const sortedTags = Object.entries(tagFrequency)
 | 
			
		||||
    
 | 
			
		||||
    function resetTags() {
 | 
			
		||||
      selectedTags.clear();
 | 
			
		||||
      // FIXED: Update ALL tag elements
 | 
			
		||||
      document.querySelectorAll('.tag-cloud-item').forEach(item => {
 | 
			
		||||
        item.classList.remove('active');
 | 
			
		||||
      });
 | 
			
		||||
@ -644,7 +605,6 @@ const sortedTags = Object.entries(tagFrequency)
 | 
			
		||||
      filterTagCloud();
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    // Event listeners
 | 
			
		||||
    elements.searchInput.addEventListener('input', (e) => {
 | 
			
		||||
      const hasValue = e.target.value.length > 0;
 | 
			
		||||
      elements.clearSearch.classList.toggle('hidden', !hasValue);
 | 
			
		||||
@ -685,7 +645,6 @@ const sortedTags = Object.entries(tagFrequency)
 | 
			
		||||
      btn.addEventListener('click', () => {
 | 
			
		||||
        const view = btn.getAttribute('data-view');
 | 
			
		||||
        
 | 
			
		||||
        // Simple toggle like the old version
 | 
			
		||||
        elements.viewToggles.forEach(b => {
 | 
			
		||||
          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.advanced.addEventListener('click', resetAdvancedFilters);
 | 
			
		||||
    elements.resetButtons.tags.addEventListener('click', resetTags);
 | 
			
		||||
    elements.resetButtons.all.addEventListener('click', resetAllFilters);
 | 
			
		||||
    
 | 
			
		||||
    // Collapsible toggle listeners
 | 
			
		||||
    elements.toggleAdvanced.addEventListener('click', () => {
 | 
			
		||||
      toggleCollapsible(elements.toggleAdvanced, elements.advancedContent, 'advanced-collapsed');
 | 
			
		||||
    });
 | 
			
		||||
@ -716,11 +673,9 @@ const sortedTags = Object.entries(tagFrequency)
 | 
			
		||||
      toggleCollapsible(elements.toggleTags, elements.tagContent, 'tags-collapsed');
 | 
			
		||||
    });
 | 
			
		||||
    
 | 
			
		||||
    // Expose functions globally for backwards compatibility
 | 
			
		||||
    window.clearTagFilters = resetTags;
 | 
			
		||||
    window.clearAllFilters = resetAllFilters;
 | 
			
		||||
    
 | 
			
		||||
    // Initialize
 | 
			
		||||
    initializeCollapsible();
 | 
			
		||||
    initTagCloud();
 | 
			
		||||
    filterTagCloud();
 | 
			
		||||
 | 
			
		||||
@ -716,7 +716,6 @@ domains.forEach((domain: any) => {
 | 
			
		||||
    const primaryModal = document.getElementById('tool-details-primary');
 | 
			
		||||
    const secondaryModal = document.getElementById('tool-details-secondary');
 | 
			
		||||
    
 | 
			
		||||
    // Debounce rapid calls
 | 
			
		||||
    if (window.modalHideInProgress) return;
 | 
			
		||||
    window.modalHideInProgress = true;
 | 
			
		||||
    
 | 
			
		||||
@ -753,19 +752,15 @@ domains.forEach((domain: any) => {
 | 
			
		||||
      if (contributeButtonSecondary) contributeButtonSecondary.style.display = 'none';
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    // Consolidated state checking with safety checks
 | 
			
		||||
    const primaryActive = primaryModal && primaryModal.classList.contains('active');
 | 
			
		||||
    const secondaryActive = secondaryModal && secondaryModal.classList.contains('active');
 | 
			
		||||
    
 | 
			
		||||
    // Update overlay and body classes atomically
 | 
			
		||||
    if (!primaryActive && !secondaryActive) {
 | 
			
		||||
      if (overlay) overlay.classList.remove('active');
 | 
			
		||||
      document.body.classList.remove('modals-side-by-side');
 | 
			
		||||
    } else if (primaryActive && secondaryActive) {
 | 
			
		||||
      // Both active - ensure side-by-side class
 | 
			
		||||
      document.body.classList.add('modals-side-by-side');
 | 
			
		||||
    } else {
 | 
			
		||||
      // Only one active - remove side-by-side class
 | 
			
		||||
      document.body.classList.remove('modals-side-by-side');
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										2
									
								
								src/env.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								src/env.d.ts
									
									
									
									
										vendored
									
									
								
							@ -27,12 +27,10 @@ declare global {
 | 
			
		||||
    showIfAuthenticated: (selector: string, context?: string) => Promise<void>;
 | 
			
		||||
    setupAuthButtons: (selector?: string) => void;
 | 
			
		||||
 | 
			
		||||
    // Consolidated scroll utilities
 | 
			
		||||
    scrollToElement: (element: Element | null, options?: ScrollIntoViewOptions) => void;
 | 
			
		||||
    scrollToElementById: (elementId: string, options?: ScrollIntoViewOptions) => void;
 | 
			
		||||
    scrollToElementBySelector: (selector: string, options?: ScrollIntoViewOptions) => void;
 | 
			
		||||
 | 
			
		||||
    // Additional global functions that might be called
 | 
			
		||||
    applyScenarioSearch?: (scenarioId: string) => void;
 | 
			
		||||
    selectPhase?: (phase: string) => void;
 | 
			
		||||
    selectApproach?: (approach: string) => void;
 | 
			
		||||
 | 
			
		||||
@ -21,7 +21,6 @@ const { title, description = 'ForensicPathways - A comprehensive directory of di
 | 
			
		||||
  <link rel="icon" type="image/x-icon" href="/favicon.ico">
 | 
			
		||||
  
 | 
			
		||||
<script>
 | 
			
		||||
  // Move utility functions OUTSIDE DOMContentLoaded to avoid race conditions
 | 
			
		||||
  function createToolSlug(toolName) {
 | 
			
		||||
    if (!toolName || typeof toolName !== 'string') {
 | 
			
		||||
      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() !== "";
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Consolidated scrolling utility - also moved outside DOMContentLoaded
 | 
			
		||||
  function scrollToElement(element, options = {}) {
 | 
			
		||||
    if (!element) return;
 | 
			
		||||
    
 | 
			
		||||
    // Calculate target position manually to avoid double-scroll
 | 
			
		||||
    setTimeout(() => {
 | 
			
		||||
      const headerHeight = document.querySelector('nav')?.offsetHeight || 80;
 | 
			
		||||
      const elementRect = element.getBoundingClientRect();
 | 
			
		||||
      const absoluteElementTop = elementRect.top + window.pageYOffset;
 | 
			
		||||
      const targetPosition = absoluteElementTop - headerHeight - 20; // Adjust this 20 as needed
 | 
			
		||||
      const targetPosition = absoluteElementTop - headerHeight - 20;
 | 
			
		||||
      
 | 
			
		||||
      window.scrollTo({
 | 
			
		||||
        top: targetPosition,
 | 
			
		||||
@ -69,7 +66,6 @@ const { title, description = 'ForensicPathways - A comprehensive directory of di
 | 
			
		||||
    }, 100);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Convenience functions for common scroll targets
 | 
			
		||||
  function scrollToElementById(elementId, options = {}) {
 | 
			
		||||
    const element = document.getElementById(elementId);
 | 
			
		||||
    scrollToElement(element, options);
 | 
			
		||||
@ -80,7 +76,6 @@ const { title, description = 'ForensicPathways - A comprehensive directory of di
 | 
			
		||||
    scrollToElement(element, options);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Simple search prioritization - exact tag matches first
 | 
			
		||||
  function prioritizeSearchResults(tools, searchTerm) {
 | 
			
		||||
    if (!searchTerm || !searchTerm.trim()) {
 | 
			
		||||
      return tools;
 | 
			
		||||
@ -95,16 +90,13 @@ const { title, description = 'ForensicPathways - A comprehensive directory of di
 | 
			
		||||
      const aExactTag = aTagsLower.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;
 | 
			
		||||
      
 | 
			
		||||
      // Otherwise maintain original order (or sort by name as secondary)
 | 
			
		||||
      return a.name.localeCompare(b.name);
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Attach to window immediately - BEFORE DOMContentLoaded
 | 
			
		||||
  (window as any).createToolSlug = createToolSlug;
 | 
			
		||||
  (window as any).findToolByIdentifier = findToolByIdentifier;
 | 
			
		||||
  (window as any).isToolHosted = isToolHosted;
 | 
			
		||||
 | 
			
		||||
@ -14,14 +14,13 @@ function getEnv(key: string): string {
 | 
			
		||||
  return value;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Use the analyzer AI for smart prompting (smaller, faster model)
 | 
			
		||||
const AI_ENDPOINT = getEnv('AI_ANALYZER_ENDPOINT');
 | 
			
		||||
const AI_API_KEY = getEnv('AI_ANALYZER_API_KEY');
 | 
			
		||||
const AI_MODEL = getEnv('AI_ANALYZER_MODEL');
 | 
			
		||||
 | 
			
		||||
const rateLimitStore = new Map<string, { count: number; resetTime: number }>();
 | 
			
		||||
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 {
 | 
			
		||||
  return input
 | 
			
		||||
@ -30,7 +29,7 @@ function sanitizeInput(input: string): string {
 | 
			
		||||
    .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]')
 | 
			
		||||
    .trim()
 | 
			
		||||
    .slice(0, 1000); // Shorter limit for enhancement
 | 
			
		||||
    .slice(0, 1000); 
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function checkRateLimit(userId: string): boolean {
 | 
			
		||||
@ -59,7 +58,6 @@ function cleanupExpiredRateLimits() {
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Clean up expired limits every 5 minutes
 | 
			
		||||
setInterval(cleanupExpiredRateLimits, 5 * 60 * 1000);
 | 
			
		||||
 | 
			
		||||
function createEnhancementPrompt(input: string): string {
 | 
			
		||||
@ -140,7 +138,6 @@ export const POST: APIRoute = async ({ request }) => {
 | 
			
		||||
          ],
 | 
			
		||||
          max_tokens: 300,
 | 
			
		||||
          temperature: 0.7,
 | 
			
		||||
          // Enhanced: Better parameters for consistent forensics output
 | 
			
		||||
          top_p: 0.9,
 | 
			
		||||
          frequency_penalty: 0.2,
 | 
			
		||||
          presence_penalty: 0.1
 | 
			
		||||
@ -171,27 +168,23 @@ export const POST: APIRoute = async ({ request }) => {
 | 
			
		||||
        throw new Error('Response is not an array');
 | 
			
		||||
      }
 | 
			
		||||
      
 | 
			
		||||
      // Enhanced validation and cleaning for forensics context
 | 
			
		||||
      questions = questions
 | 
			
		||||
        .filter(q => typeof q === 'string' && q.length > 20 && q.length < 200) // More appropriate length for forensics questions
 | 
			
		||||
        .filter(q => q.includes('?')) // Must be a question
 | 
			
		||||
        .filter(q => typeof q === 'string' && q.length > 20 && q.length < 200)
 | 
			
		||||
        .filter(q => q.includes('?')) 
 | 
			
		||||
        .filter(q => {
 | 
			
		||||
          // Enhanced: Filter for forensics-relevant questions
 | 
			
		||||
          const forensicsTerms = ['forensisch', 'log', 'dump', 'image', 'artefakt', 'evidence', 'incident', 'system', 'netzwerk', 'zeitraum', 'verfügbar'];
 | 
			
		||||
          const lowerQ = q.toLowerCase();
 | 
			
		||||
          return forensicsTerms.some(term => lowerQ.includes(term));
 | 
			
		||||
        })
 | 
			
		||||
        .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) {
 | 
			
		||||
        questions = [];
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
    } catch (error) {
 | 
			
		||||
      console.error('Failed to parse enhancement response:', aiContent);
 | 
			
		||||
      // If parsing fails, assume input is complete enough
 | 
			
		||||
      questions = [];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -43,7 +43,7 @@ interface KnowledgebaseContributionData {
 | 
			
		||||
 | 
			
		||||
const rateLimitStore = new Map<string, { count: number; resetTime: number }>();
 | 
			
		||||
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 {
 | 
			
		||||
  const now = Date.now();
 | 
			
		||||
 | 
			
		||||
@ -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 { withAPIAuth } from '../../../utils/auth.js';
 | 
			
		||||
import { apiResponse, apiError, apiServerError, apiSpecial, handleAPIRequest } from '../../../utils/api.js';
 | 
			
		||||
 | 
			
		||||
@ -173,29 +173,24 @@ const phases = data.phases;
 | 
			
		||||
<script define:vars={{ toolsData: data.tools, phases: data.phases }}>
 | 
			
		||||
  window.toolsData = toolsData;
 | 
			
		||||
  
 | 
			
		||||
  // Approach selection functionality
 | 
			
		||||
  window.selectApproach = function(approach) {
 | 
			
		||||
    console.log(`Selected approach: ${approach}`);
 | 
			
		||||
    
 | 
			
		||||
    // Hide any existing results
 | 
			
		||||
    const aiResults = document.getElementById('ai-results');
 | 
			
		||||
    if (aiResults) aiResults.style.display = 'none';
 | 
			
		||||
    
 | 
			
		||||
    // Visual feedback for selection
 | 
			
		||||
    document.querySelectorAll('.approach-card').forEach(card => {
 | 
			
		||||
      card.classList.remove('selected');
 | 
			
		||||
    });
 | 
			
		||||
    document.querySelector(`.approach-card.${approach}`).classList.add('selected');
 | 
			
		||||
    
 | 
			
		||||
    if (approach === 'methodology') {
 | 
			
		||||
      // Show NIST methodology section
 | 
			
		||||
      const methodologySection = document.getElementById('methodology-section');
 | 
			
		||||
      if (methodologySection) {
 | 
			
		||||
        methodologySection.classList.add('active');
 | 
			
		||||
        window.scrollToElementById('methodology-section');
 | 
			
		||||
      }
 | 
			
		||||
    } else if (approach === 'targeted') {
 | 
			
		||||
      // Show targeted scenarios section
 | 
			
		||||
      const targetedSection = document.getElementById('targeted-section');
 | 
			
		||||
      if (targetedSection) {
 | 
			
		||||
        targetedSection.classList.add('active');
 | 
			
		||||
@ -204,34 +199,28 @@ const phases = data.phases;
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  // Phase selection function (integrates with existing ToolFilters)
 | 
			
		||||
  window.selectPhase = function(phase) {
 | 
			
		||||
    console.log(`Selected NIST phase: ${phase}`);
 | 
			
		||||
    
 | 
			
		||||
    // Remove active class from all phase cards
 | 
			
		||||
    document.querySelectorAll('.phase-card').forEach(card => {
 | 
			
		||||
      card.classList.remove('active');
 | 
			
		||||
    });
 | 
			
		||||
    
 | 
			
		||||
    // Add active class to selected phase card
 | 
			
		||||
    const selectedCard = document.querySelector(`.phase-card.phase-${phase}`);
 | 
			
		||||
    if (selectedCard) {
 | 
			
		||||
      selectedCard.classList.add('active');
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    // Use existing phase filter functionality
 | 
			
		||||
    const existingPhaseButton = document.querySelector(`[data-phase="${phase}"]`);
 | 
			
		||||
    if (existingPhaseButton && !existingPhaseButton.classList.contains('active')) {
 | 
			
		||||
      existingPhaseButton.click();
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    // Switch to grid view to show results
 | 
			
		||||
    const gridToggle = document.querySelector('.view-toggle[data-view="grid"]');
 | 
			
		||||
    if (gridToggle && !gridToggle.classList.contains('active')) {
 | 
			
		||||
      gridToggle.click();
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    // Scroll to results using consolidated utility
 | 
			
		||||
    window.scrollToElementById('tools-grid');
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
@ -296,7 +285,6 @@ const phases = data.phases;
 | 
			
		||||
          break;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      // Add smooth scrolling to filters section after layout settles
 | 
			
		||||
      setTimeout(() => {
 | 
			
		||||
        window.scrollToElementById('filters-section');
 | 
			
		||||
      }, 150);
 | 
			
		||||
@ -369,7 +357,6 @@ const phases = data.phases;
 | 
			
		||||
            }, 2000);
 | 
			
		||||
          } else {
 | 
			
		||||
            console.warn('Tool card not found in grid:', toolName);
 | 
			
		||||
            // Fallback to tools grid
 | 
			
		||||
            window.scrollToElementById('tools-grid');
 | 
			
		||||
          }
 | 
			
		||||
        }, 300);
 | 
			
		||||
@ -407,7 +394,6 @@ const phases = data.phases;
 | 
			
		||||
          window.scrollToElement(firstMatch);
 | 
			
		||||
        } else {
 | 
			
		||||
          console.warn('Tool chip not found in matrix:', toolName);
 | 
			
		||||
          // Fallback to matrix container
 | 
			
		||||
          window.scrollToElementById('matrix-container');
 | 
			
		||||
        }
 | 
			
		||||
      }, 500);
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,4 @@
 | 
			
		||||
// src/utils/aiPipeline.ts - FIXED: Critical error corrections
 | 
			
		||||
// src/utils/aiPipeline.ts 
 | 
			
		||||
 | 
			
		||||
import { getCompressedToolsDataForAI } from './dataService.js';
 | 
			
		||||
import { embeddingsService, type EmbeddingData } from './embeddings.js';
 | 
			
		||||
@ -36,7 +36,6 @@ interface AnalysisContext {
 | 
			
		||||
  filteredData: any;
 | 
			
		||||
  contextHistory: string[];
 | 
			
		||||
  
 | 
			
		||||
  // FIXED: Add max context length tracking
 | 
			
		||||
  maxContextLength: number;
 | 
			
		||||
  currentContextLength: number;
 | 
			
		||||
  
 | 
			
		||||
@ -47,7 +46,6 @@ interface AnalysisContext {
 | 
			
		||||
  selectedTools?: Array<{tool: any, phase: string, priority: string, justification?: string}>;
 | 
			
		||||
  backgroundKnowledge?: Array<{concept: any, relevance: string}>;
 | 
			
		||||
  
 | 
			
		||||
  // FIXED: Add seen tools tracking to prevent duplicates
 | 
			
		||||
  seenToolNames: Set<string>;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -58,7 +56,6 @@ class ImprovedMicroTaskAIPipeline {
 | 
			
		||||
  private similarityThreshold: number;
 | 
			
		||||
  private microTaskDelay: number;
 | 
			
		||||
  
 | 
			
		||||
  // FIXED: Add proper token management
 | 
			
		||||
  private maxContextTokens: number;
 | 
			
		||||
  private maxPromptTokens: number;
 | 
			
		||||
 | 
			
		||||
@ -74,7 +71,6 @@ class ImprovedMicroTaskAIPipeline {
 | 
			
		||||
    this.similarityThreshold = 0.3; 
 | 
			
		||||
    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.maxPromptTokens = parseInt(process.env.AI_MAX_PROMPT_TOKENS || '1500', 10);
 | 
			
		||||
  }
 | 
			
		||||
@ -87,27 +83,22 @@ class ImprovedMicroTaskAIPipeline {
 | 
			
		||||
    return value;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // FIXED: Estimate token count (rough approximation)
 | 
			
		||||
  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 {
 | 
			
		||||
    const entryTokens = this.estimateTokens(newEntry);
 | 
			
		||||
    
 | 
			
		||||
    // Add new entry
 | 
			
		||||
    context.contextHistory.push(newEntry);
 | 
			
		||||
    context.currentContextLength += entryTokens;
 | 
			
		||||
    
 | 
			
		||||
    // Prune old entries if exceeding limits
 | 
			
		||||
    while (context.currentContextLength > this.maxContextTokens && context.contextHistory.length > 1) {
 | 
			
		||||
      const removed = context.contextHistory.shift()!;
 | 
			
		||||
      context.currentContextLength -= this.estimateTokens(removed);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // FIXED: Safe JSON parsing with validation
 | 
			
		||||
  private safeParseJSON(jsonString: string, fallback: any = null): any {
 | 
			
		||||
    try {
 | 
			
		||||
      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 {
 | 
			
		||||
    if (context.seenToolNames.has(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`);
 | 
			
		||||
      
 | 
			
		||||
      // FIXED: Use your expected flow - get full data of embeddings results
 | 
			
		||||
      if (toolNames.size >= 15) { // Reasonable threshold for quality
 | 
			
		||||
      if (toolNames.size >= 15) {
 | 
			
		||||
        candidateTools = toolsData.tools.filter((tool: any) => toolNames.has(tool.name));
 | 
			
		||||
        candidateConcepts = toolsData.concepts.filter((concept: any) => conceptNames.has(concept.name));
 | 
			
		||||
        selectionMethod = 'embeddings_candidates';
 | 
			
		||||
@ -186,7 +175,6 @@ class ImprovedMicroTaskAIPipeline {
 | 
			
		||||
      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`);
 | 
			
		||||
    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(
 | 
			
		||||
    userQuery: string, 
 | 
			
		||||
    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 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) => ({
 | 
			
		||||
      name: tool.name,
 | 
			
		||||
      type: tool.type,
 | 
			
		||||
@ -307,7 +292,7 @@ Respond with ONLY this JSON format:
 | 
			
		||||
}`;
 | 
			
		||||
 | 
			
		||||
    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);
 | 
			
		||||
      
 | 
			
		||||
@ -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 reasoning: ${result.reasoning}`);
 | 
			
		||||
 | 
			
		||||
      // Return the actual tool/concept objects
 | 
			
		||||
      const selectedTools = candidateTools.filter(tool => result.selectedTools.includes(tool.name));
 | 
			
		||||
      const selectedConcepts = candidateConcepts.filter(concept => result.selectedConcepts.includes(concept.name));
 | 
			
		||||
      
 | 
			
		||||
@ -339,7 +323,6 @@ Respond with ONLY this JSON format:
 | 
			
		||||
    } catch (error) {
 | 
			
		||||
      console.error('[IMPROVED PIPELINE] AI selection failed:', error);
 | 
			
		||||
      
 | 
			
		||||
      // Emergency fallback with bias awareness
 | 
			
		||||
      console.log('[IMPROVED PIPELINE] Using emergency keyword-based selection');
 | 
			
		||||
      return this.emergencyKeywordSelection(userQuery, candidateTools, candidateConcepts, mode);
 | 
			
		||||
    }
 | 
			
		||||
@ -349,7 +332,6 @@ Respond with ONLY this JSON format:
 | 
			
		||||
    const queryLower = userQuery.toLowerCase();
 | 
			
		||||
    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 toolText = (
 | 
			
		||||
        tool.name + ' ' + 
 | 
			
		||||
@ -385,18 +367,15 @@ Respond with ONLY this JSON format:
 | 
			
		||||
  private async callMicroTaskAI(prompt: string, context: AnalysisContext, maxTokens: number = 300): Promise<MicroTaskResult> {
 | 
			
		||||
    const startTime = Date.now();
 | 
			
		||||
    
 | 
			
		||||
    // FIXED: Build context prompt with token management
 | 
			
		||||
    let contextPrompt = prompt;
 | 
			
		||||
    if (context.contextHistory.length > 0) {
 | 
			
		||||
      const contextSection = `BISHERIGE ANALYSE:\n${context.contextHistory.join('\n\n')}\n\nAKTUELLE AUFGABE:\n`;
 | 
			
		||||
      const combinedPrompt = contextSection + prompt;
 | 
			
		||||
      
 | 
			
		||||
      // Check if combined prompt exceeds limits
 | 
			
		||||
      if (this.estimateTokens(combinedPrompt) <= this.maxPromptTokens) {
 | 
			
		||||
        contextPrompt = combinedPrompt;
 | 
			
		||||
      } else {
 | 
			
		||||
        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;
 | 
			
		||||
      }
 | 
			
		||||
      
 | 
			
		||||
      // FIXED: Use new context management
 | 
			
		||||
      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);
 | 
			
		||||
    
 | 
			
		||||
    if (result.success) {
 | 
			
		||||
      // FIXED: Safe JSON parsing with validation
 | 
			
		||||
      const selections = this.safeParseJSON(result.content, []);
 | 
			
		||||
      
 | 
			
		||||
      if (Array.isArray(selections)) {
 | 
			
		||||
@ -570,7 +547,6 @@ Antworten Sie AUSSCHLIESSLICH mit diesem JSON-Format (kein zusätzlicher Text):
 | 
			
		||||
        validSelections.forEach((sel: any) => {
 | 
			
		||||
          const tool = phaseTools.find((t: any) => t.name === sel.toolName);
 | 
			
		||||
          if (tool) {
 | 
			
		||||
            // FIXED: Use deduplication helper
 | 
			
		||||
            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);
 | 
			
		||||
    
 | 
			
		||||
    if (result.success) {
 | 
			
		||||
      // FIXED: Safe JSON parsing
 | 
			
		||||
      const evaluation = this.safeParseJSON(result.content, {
 | 
			
		||||
        suitability_score: 'medium',
 | 
			
		||||
        detailed_explanation: 'Evaluation failed',
 | 
			
		||||
@ -613,7 +588,6 @@ Bewerten Sie nach forensischen Standards und antworten Sie AUSSCHLIESSLICH mit d
 | 
			
		||||
        alternatives: ''
 | 
			
		||||
      });
 | 
			
		||||
      
 | 
			
		||||
      // FIXED: Use deduplication helper
 | 
			
		||||
      this.addToolToSelection(context, {
 | 
			
		||||
        ...tool,
 | 
			
		||||
        evaluation: {
 | 
			
		||||
@ -661,7 +635,6 @@ Antworten Sie AUSSCHLIESSLICH mit diesem JSON-Format:
 | 
			
		||||
    const result = await this.callMicroTaskAI(prompt, context, 400);
 | 
			
		||||
    
 | 
			
		||||
    if (result.success) {
 | 
			
		||||
      // FIXED: Safe JSON parsing
 | 
			
		||||
      const selections = this.safeParseJSON(result.content, []);
 | 
			
		||||
      
 | 
			
		||||
      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 filteredData = await this.getIntelligentCandidates(userQuery, toolsData, mode);
 | 
			
		||||
      
 | 
			
		||||
      // FIXED: Initialize context with proper state management
 | 
			
		||||
      const context: AnalysisContext = {
 | 
			
		||||
        userQuery,
 | 
			
		||||
        mode,
 | 
			
		||||
@ -753,7 +725,7 @@ WICHTIG: Antworten Sie NUR in fließendem deutschen Text ohne Listen oder Markdo
 | 
			
		||||
        contextHistory: [],
 | 
			
		||||
        maxContextLength: this.maxContextTokens,
 | 
			
		||||
        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`);
 | 
			
		||||
@ -777,7 +749,6 @@ WICHTIG: Antworten Sie NUR in fließendem deutschen Text ohne Listen oder Markdo
 | 
			
		||||
 | 
			
		||||
      // Task 4: Tool Selection/Evaluation (mode-dependent)
 | 
			
		||||
      if (mode === 'workflow') {
 | 
			
		||||
        // Select tools for each phase
 | 
			
		||||
        const phases = toolsData.phases || [];
 | 
			
		||||
        for (const phase of phases) {
 | 
			
		||||
          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);
 | 
			
		||||
        }
 | 
			
		||||
      } else {
 | 
			
		||||
        // Evaluate top 3 tools for specific problem
 | 
			
		||||
        const topTools = filteredData.tools.slice(0, 3);
 | 
			
		||||
        for (let i = 0; i < topTools.length; i++) {
 | 
			
		||||
          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 {
 | 
			
		||||
    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();
 | 
			
		||||
 | 
			
		||||
export { aiPipeline, type AnalysisResult };
 | 
			
		||||
@ -111,7 +111,6 @@ export const apiSpecial = {
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const apiWithHeaders = {
 | 
			
		||||
  // Success with custom headers (e.g., Set-Cookie)
 | 
			
		||||
  successWithHeaders: (data: any, headers: Record<string, string>): Response => 
 | 
			
		||||
    createAPIResponse(data, 200, headers),
 | 
			
		||||
  
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user