phase 1
This commit is contained in:
		
							parent
							
								
									d6a9620758
								
							
						
					
					
						commit
						72d5f267f0
					
				@ -46,13 +46,29 @@ const domainAgnosticSoftware = data['domain-agnostic-software'] || [];
 | 
			
		||||
        </span>
 | 
			
		||||
      </div>
 | 
			
		||||
 | 
			
		||||
      <textarea 
 | 
			
		||||
<textarea 
 | 
			
		||||
        id="ai-query-input" 
 | 
			
		||||
        placeholder="Beschreiben Sie Ihr forensisches Szenario... z.B. 'Verdacht auf Ransomware-Angriff auf Windows-Domänencontroller mit verschlüsselten Dateien und verdächtigen Netzwerkverbindungen'"
 | 
			
		||||
        style="min-height: 120px; resize: vertical; font-size: 0.9375rem; line-height: 1.5;"
 | 
			
		||||
        maxlength="2000"
 | 
			
		||||
      ></textarea>
 | 
			
		||||
      
 | 
			
		||||
      <!-- Smart Prompting Container -->
 | 
			
		||||
      <div id="smart-prompting-container" style="display: none; margin-top: 1rem;">
 | 
			
		||||
        <div class="card" style="border-left: 4px solid var(--color-accent); padding: 1rem;">
 | 
			
		||||
          <div style="display: flex; align-items: center; gap: 0.5rem; margin-bottom: 0.75rem;">
 | 
			
		||||
            <span id="prompting-status">💡 KI analysiert Ihre Eingabe...</span>
 | 
			
		||||
            <div id="prompting-spinner" style="display: none; animation: pulse 2s ease-in-out infinite;">⏳</div>
 | 
			
		||||
          </div>
 | 
			
		||||
          <div id="suggested-questions" style="display: none;">
 | 
			
		||||
            <p style="font-size: 0.875rem; color: var(--color-text-secondary); margin-bottom: 0.5rem;">
 | 
			
		||||
              Klicken Sie auf eine Frage, um sie zu Ihrer Beschreibung hinzuzufügen:
 | 
			
		||||
            </p>
 | 
			
		||||
            <div id="questions-container" style="display: flex; flex-direction: column; gap: 0.375rem;"></div>
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
      
 | 
			
		||||
      <div style="margin-top: 0.5rem; margin-bottom: 1rem;">
 | 
			
		||||
        <p style="font-size: 0.75rem; color: var(--color-text-secondary); text-align: center; line-height: 1.4;">
 | 
			
		||||
          <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="margin-right: 0.25rem; vertical-align: middle;">
 | 
			
		||||
@ -131,6 +147,12 @@ function sanitizeHTML(html) {
 | 
			
		||||
  return div.innerHTML;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function formatDuration(ms) {
 | 
			
		||||
  if (ms < 1000) return '< 1s';
 | 
			
		||||
  if (ms < 60000) return `${Math.ceil(ms / 1000)}s`;
 | 
			
		||||
  return `${Math.ceil(ms / 60000)}m`;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
document.addEventListener('DOMContentLoaded', () => {
 | 
			
		||||
  const aiInterface = document.getElementById('ai-interface');
 | 
			
		||||
  const aiInput = document.getElementById('ai-query-input');
 | 
			
		||||
@ -245,6 +267,491 @@ document.addEventListener('DOMContentLoaded', () => {
 | 
			
		||||
  aiInput.addEventListener('input', updateCharacterCount);
 | 
			
		||||
  updateCharacterCount();
 | 
			
		||||
 | 
			
		||||
  function formatWorkflowSuggestion(text) {
 | 
			
		||||
    const numberedListPattern = /(\d+\.\s)/g;
 | 
			
		||||
    
 | 
			
		||||
    if (numberedListPattern.test(text)) {
 | 
			
		||||
      const items = text.split(/\d+\.\s/).filter(item => item.trim().length > 0);
 | 
			
		||||
      
 | 
			
		||||
      if (items.length > 1) {
 | 
			
		||||
        const listItems = items.map(item => 
 | 
			
		||||
          `<li style="margin-bottom: 0.5rem; line-height: 1.6;">${item.trim()}</li>`
 | 
			
		||||
        ).join('');
 | 
			
		||||
        
 | 
			
		||||
        return `<ol style="margin: 0; padding-left: 1.5rem; color: var(--color-text);">${listItems}</ol>`;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    const bulletPattern = /^[\s]*[-\*•]\s/gm;
 | 
			
		||||
    if (bulletPattern.test(text)) {
 | 
			
		||||
      const items = text.split(/^[\s]*[-\*•]\s/gm).filter(item => item.trim().length > 0);
 | 
			
		||||
      
 | 
			
		||||
      if (items.length > 1) {
 | 
			
		||||
        const listItems = items.map(item => 
 | 
			
		||||
          `<li style="margin-bottom: 0.5rem; line-height: 1.6;">${item.trim()}</li>`
 | 
			
		||||
        ).join('');
 | 
			
		||||
        
 | 
			
		||||
        return `<ul style="margin: 0; padding-left: 1.5rem; color: var(--color-text);">${listItems}</ul>`;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    if (text.includes('\n')) {
 | 
			
		||||
      const lines = text.split('\n').filter(line => line.trim().length > 0);
 | 
			
		||||
      if (lines.length > 1) {
 | 
			
		||||
        const listItems = lines.map(line => 
 | 
			
		||||
          `<li style="margin-bottom: 0.5rem; line-height: 1.6;">${line.trim()}</li>`
 | 
			
		||||
        ).join('');
 | 
			
		||||
        
 | 
			
		||||
        return `<ul style="margin: 0; padding-left: 1.5rem; color: var(--color-text);">${listItems}</ul>`;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    return `<p style="margin: 0; line-height: 1.6; color: var(--color-text);">${text}</p>`;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  function renderBackgroundKnowledge(backgroundKnowledge) {
 | 
			
		||||
    if (!backgroundKnowledge || backgroundKnowledge.length === 0) {
 | 
			
		||||
      return '';
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    const conceptLinks = backgroundKnowledge.map(concept => `
 | 
			
		||||
      <div class="concept-recommendation" style="background-color: var(--color-concept-bg); border: 1px solid var(--color-concept); border-radius: 0.5rem; padding: 1rem; margin-bottom: 0.75rem;">
 | 
			
		||||
        <div style="display: flex; align-items: center; gap: 0.75rem; margin-bottom: 0.5rem;">
 | 
			
		||||
          <button class="concept-link" 
 | 
			
		||||
                  onclick="event.stopPropagation(); window.showToolDetails('${concept.concept_name}', 'secondary')" 
 | 
			
		||||
                  style="background: none; border: none; color: var(--color-concept); font-weight: 600; cursor: pointer; text-decoration: underline; font-size: 0.875rem; padding: 0;"
 | 
			
		||||
                  onmouseover="this.style.color='var(--color-concept-hover)';"
 | 
			
		||||
                  onmouseout="this.style.color='var(--color-concept)';">
 | 
			
		||||
            📚 ${concept.concept_name}
 | 
			
		||||
          </button>
 | 
			
		||||
          <span class="badge" style="background-color: var(--color-concept); color: white; font-size: 0.625rem;">Hintergrundwissen</span>
 | 
			
		||||
        </div>
 | 
			
		||||
        <p style="margin: 0; font-size: 0.8125rem; line-height: 1.5; color: var(--color-text-secondary);">
 | 
			
		||||
          ${concept.relevance}
 | 
			
		||||
        </p>
 | 
			
		||||
      </div>
 | 
			
		||||
    `).join('');
 | 
			
		||||
    
 | 
			
		||||
    return `
 | 
			
		||||
      <div class="card" style="margin-bottom: 2rem; border-left: 4px solid var(--color-concept);">
 | 
			
		||||
        <h4 style="margin: 0 0 1rem 0; color: var(--color-concept); display: flex; align-items: center; gap: 0.5rem;">
 | 
			
		||||
          <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
 | 
			
		||||
            <path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"/>
 | 
			
		||||
            <path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z"/>
 | 
			
		||||
          </svg>
 | 
			
		||||
          Empfohlenes Hintergrundwissen
 | 
			
		||||
        </h4>
 | 
			
		||||
        ${conceptLinks}
 | 
			
		||||
      </div>
 | 
			
		||||
    `;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Enhanced function to render contextual analysis sections
 | 
			
		||||
  function renderContextualAnalysis(recommendation, mode) {
 | 
			
		||||
    let html = '';
 | 
			
		||||
    
 | 
			
		||||
    // Scenario/Problem Analysis Section
 | 
			
		||||
    const analysisField = mode === 'workflow' ? recommendation.scenario_analysis : recommendation.problem_analysis;
 | 
			
		||||
    if (analysisField) {
 | 
			
		||||
      html += `
 | 
			
		||||
        <div class="card" style="margin-bottom: 2rem; border-left: 4px solid var(--color-primary);">
 | 
			
		||||
          <h4 style="margin: 0 0 1rem 0; color: var(--color-primary); display: flex; align-items: center; gap: 0.5rem;">
 | 
			
		||||
            <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
 | 
			
		||||
              <path d="M12 20h9"/>
 | 
			
		||||
              <path d="M16.5 3.5a2.12 2.12 0 0 1 3 3L7 19l-4 1 1-4L16.5 3.5z"/>
 | 
			
		||||
            </svg>
 | 
			
		||||
            ${mode === 'workflow' ? 'Szenario-Analyse' : 'Problem-Analyse'}
 | 
			
		||||
          </h4>
 | 
			
		||||
          ${formatWorkflowSuggestion(analysisField)}
 | 
			
		||||
        </div>
 | 
			
		||||
      `;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Investigation Approach Section  
 | 
			
		||||
    if (recommendation.investigation_approach) {
 | 
			
		||||
      html += `
 | 
			
		||||
        <div class="card" style="margin-bottom: 2rem; border-left: 4px solid var(--color-accent);">
 | 
			
		||||
          <h4 style="margin: 0 0 1rem 0; color: var(--color-accent); display: flex; align-items: center; gap: 0.5rem;">
 | 
			
		||||
            <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
 | 
			
		||||
              <polyline points="9,11 12,14 22,4"/>
 | 
			
		||||
              <path d="M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11"/>
 | 
			
		||||
            </svg>
 | 
			
		||||
            ${mode === 'workflow' ? 'Untersuchungsansatz' : 'Lösungsansatz'}
 | 
			
		||||
          </h4>
 | 
			
		||||
          ${formatWorkflowSuggestion(recommendation.investigation_approach)}
 | 
			
		||||
        </div>
 | 
			
		||||
      `;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Critical Considerations Section
 | 
			
		||||
    if (recommendation.critical_considerations) {
 | 
			
		||||
      html += `
 | 
			
		||||
        <div class="card" style="margin-bottom: 2rem; border-left: 4px solid var(--color-warning);">
 | 
			
		||||
          <h4 style="margin: 0 0 1rem 0; color: var(--color-warning); display: flex; align-items: center; gap: 0.5rem;">
 | 
			
		||||
            <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
 | 
			
		||||
              <circle cx="12" cy="12" r="10"/>
 | 
			
		||||
              <line x1="12" y1="8" x2="12" y2="12"/>
 | 
			
		||||
              <line x1="12" y1="16" x2="12.01" y2="16"/>
 | 
			
		||||
            </svg>
 | 
			
		||||
            ${mode === 'workflow' ? 'Kritische Überlegungen' : 'Wichtige Voraussetzungen'}
 | 
			
		||||
          </h4>
 | 
			
		||||
          ${formatWorkflowSuggestion(recommendation.critical_considerations)}
 | 
			
		||||
        </div>
 | 
			
		||||
      `;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return html;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  function displayWorkflowResults(recommendation, originalQuery) {
 | 
			
		||||
    const toolsByPhase = {};
 | 
			
		||||
    
 | 
			
		||||
    const phaseOrder = phases.map(phase => phase.id);
 | 
			
		||||
    const phaseNames = phases.reduce((acc, phase) => {
 | 
			
		||||
      acc[phase.id] = phase.name;
 | 
			
		||||
      return acc;
 | 
			
		||||
    }, {});
 | 
			
		||||
 | 
			
		||||
    phaseOrder.forEach(phase => {
 | 
			
		||||
      toolsByPhase[phase] = [];
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    recommendation.recommended_tools?.forEach(recTool => {
 | 
			
		||||
      if (toolsByPhase[recTool.phase]) {
 | 
			
		||||
        const fullTool = tools.find(t => t.name === recTool.name);
 | 
			
		||||
        if (fullTool) {
 | 
			
		||||
          toolsByPhase[recTool.phase].push({
 | 
			
		||||
            ...fullTool,
 | 
			
		||||
            recommendation: recTool
 | 
			
		||||
          });
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    const resultsHTML = `
 | 
			
		||||
      <div class="workflow-container">
 | 
			
		||||
        <div style="text-align: center; margin-bottom: 2rem; padding: 1.5rem; background: linear-gradient(135deg, var(--color-accent) 0%, var(--color-primary) 100%); color: white; border-radius: 0.75rem;">
 | 
			
		||||
          <h3 style="margin: 0 0 0.75rem 0; font-size: 1.5rem;">Empfohlener DFIR-Workflow</h3>
 | 
			
		||||
          <p style="margin: 0; opacity: 0.9; line-height: 1.5;">
 | 
			
		||||
            Basierend auf Ihrer Anfrage: "<em>${originalQuery.slice(0, 100)}${originalQuery.length > 100 ? '...' : ''}</em>"
 | 
			
		||||
          </p>
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
        ${renderContextualAnalysis(recommendation, 'workflow')}
 | 
			
		||||
 | 
			
		||||
        ${renderBackgroundKnowledge(recommendation.background_knowledge)}
 | 
			
		||||
 | 
			
		||||
        ${phaseOrder.map((phase, index) => {
 | 
			
		||||
          const phaseTools = toolsByPhase[phase];
 | 
			
		||||
          if (phaseTools.length === 0) return '';
 | 
			
		||||
 | 
			
		||||
          return `
 | 
			
		||||
            <div class="workflow-phase">
 | 
			
		||||
              <div class="phase-header">
 | 
			
		||||
                <div class="phase-number">${index + 1}</div>
 | 
			
		||||
                <div class="phase-info">
 | 
			
		||||
                  <h3 class="phase-title">${phaseNames[phase]}</h3>
 | 
			
		||||
                  <div class="phase-tools">
 | 
			
		||||
                    ${phaseTools.map(tool => {
 | 
			
		||||
                      const hasValidProjectUrl = tool.projectUrl !== undefined && 
 | 
			
		||||
                                                tool.projectUrl !== null && 
 | 
			
		||||
                                                tool.projectUrl !== "" && 
 | 
			
		||||
                                                tool.projectUrl.trim() !== "";
 | 
			
		||||
                      
 | 
			
		||||
                      const priorityColors = {
 | 
			
		||||
                        high: 'var(--color-error)',
 | 
			
		||||
                        medium: 'var(--color-warning)', 
 | 
			
		||||
                        low: 'var(--color-accent)'
 | 
			
		||||
                      };
 | 
			
		||||
 | 
			
		||||
                      return `
 | 
			
		||||
                        <div class="tool-recommendation ${tool.type === 'method' ? 'method' : hasValidProjectUrl ? 'hosted' : (tool.license !== 'Proprietary' ? 'oss' : '')}"
 | 
			
		||||
                             onclick="window.showToolDetails('${tool.name}')">
 | 
			
		||||
                          <div class="tool-rec-header">
 | 
			
		||||
                            <h4 class="tool-rec-name">
 | 
			
		||||
                              ${tool.icon ? `<span style="margin-right: 0.5rem;">${tool.icon}</span>` : ''}
 | 
			
		||||
                              ${tool.name}
 | 
			
		||||
                            </h4>
 | 
			
		||||
                            <span class="tool-rec-priority ${tool.recommendation.priority}" 
 | 
			
		||||
                                  style="background-color: ${priorityColors[tool.recommendation.priority]};">
 | 
			
		||||
                              ${tool.recommendation.priority}
 | 
			
		||||
                            </span>
 | 
			
		||||
                          </div>
 | 
			
		||||
                          
 | 
			
		||||
                          <div class="tool-rec-justification">
 | 
			
		||||
                            "${tool.recommendation.justification}"
 | 
			
		||||
                          </div>
 | 
			
		||||
                          
 | 
			
		||||
                          <div class="tool-rec-metadata">
 | 
			
		||||
                            <div style="display: flex; flex-wrap: wrap; gap: 0.25rem; margin-bottom: 0.5rem;">
 | 
			
		||||
                              ${tool.type !== 'method' && hasValidProjectUrl ? '<span class="badge badge-primary">CC24-Server</span>' : ''}
 | 
			
		||||
                              ${tool.knowledgebase === true ? '<span class="badge badge-error">📖</span>' : ''}
 | 
			
		||||
                              <span class="badge" style="background-color: var(--color-bg-tertiary); color: var(--color-text);">${tool.skillLevel}</span>
 | 
			
		||||
                            </div>
 | 
			
		||||
                            <div style="font-size: 0.8125rem; color: var(--color-text-secondary);">
 | 
			
		||||
                              ${tool.type === 'method' ? 'Methode' : tool.platforms.join(', ') + ' • ' + tool.license}
 | 
			
		||||
                            </div>
 | 
			
		||||
                          </div>
 | 
			
		||||
                        </div>
 | 
			
		||||
                      `;
 | 
			
		||||
                    }).join('')}
 | 
			
		||||
                  </div>
 | 
			
		||||
                </div>
 | 
			
		||||
              </div>
 | 
			
		||||
              ${index < phaseOrder.length - 1 ? `
 | 
			
		||||
                <div class="workflow-arrow">
 | 
			
		||||
                  <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="var(--color-primary)" stroke-width="2">
 | 
			
		||||
                    <line x1="12" y1="5" x2="12" y2="19"/>
 | 
			
		||||
                    <polyline points="19,12 12,19 5,12"/>
 | 
			
		||||
                  </svg>
 | 
			
		||||
                </div>
 | 
			
		||||
              ` : ''}
 | 
			
		||||
            </div>
 | 
			
		||||
          `;
 | 
			
		||||
        }).join('')}
 | 
			
		||||
 | 
			
		||||
        ${recommendation.workflow_suggestion ? `
 | 
			
		||||
          <div class="card" style="margin-top: 2rem; border-left: 4px solid var(--color-accent);">
 | 
			
		||||
            <h4 style="margin: 0 0 1rem 0; color: var(--color-accent); display: flex; align-items: center; gap: 0.5rem;">
 | 
			
		||||
              <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
 | 
			
		||||
                <polyline points="9,11 12,14 22,4"/>
 | 
			
		||||
                <path d="M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11"/>
 | 
			
		||||
              </svg>
 | 
			
		||||
              Workflow-Empfehlung
 | 
			
		||||
            </h4>
 | 
			
		||||
            ${formatWorkflowSuggestion(recommendation.workflow_suggestion)}
 | 
			
		||||
          </div>
 | 
			
		||||
        ` : ''}
 | 
			
		||||
 | 
			
		||||
        ${recommendation.additional_notes ? `
 | 
			
		||||
          <div class="card" style="margin-top: 1rem; background-color: var(--color-warning); color: white;">
 | 
			
		||||
            <h4 style="margin: 0 0 1rem 0; display: flex; align-items: center; gap: 0.5rem;">
 | 
			
		||||
              <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
 | 
			
		||||
                <circle cx="12" cy="12" r="10"/>
 | 
			
		||||
                <line x1="12" y1="8" x2="12" y2="12"/>
 | 
			
		||||
                <line x1="12" y1="16" x2="12.01" y2="16"/>
 | 
			
		||||
              </svg>
 | 
			
		||||
              Wichtige Hinweise
 | 
			
		||||
            </h4>
 | 
			
		||||
            <div style="color: white;">
 | 
			
		||||
              ${formatWorkflowSuggestion(recommendation.additional_notes).replace(/color: var\(--color-text\)/g, 'color: white')}
 | 
			
		||||
            </div>
 | 
			
		||||
          </div>
 | 
			
		||||
        ` : ''}
 | 
			
		||||
      </div>
 | 
			
		||||
    `;
 | 
			
		||||
    
 | 
			
		||||
    aiResults.innerHTML = '';
 | 
			
		||||
    const tempDiv = document.createElement('div');
 | 
			
		||||
    tempDiv.innerHTML = resultsHTML;
 | 
			
		||||
    aiResults.appendChild(tempDiv);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  function displayToolResults(recommendation, originalQuery) {
 | 
			
		||||
    function getSuitabilityText(score) {
 | 
			
		||||
      const suitabilityTexts = {
 | 
			
		||||
        high: 'GUT GEEIGNET',
 | 
			
		||||
        medium: 'GEEIGNET', 
 | 
			
		||||
        low: 'VIELLEICHT GEEIGNET'
 | 
			
		||||
      };
 | 
			
		||||
      return suitabilityTexts[score] || 'GEEIGNET';
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function getToolPhases(tool) {
 | 
			
		||||
      if (!tool.phases || tool.phases.length === 0) return '';
 | 
			
		||||
      
 | 
			
		||||
      const phaseNames = phases.reduce((acc, phase) => {
 | 
			
		||||
        acc[phase.id] = phase.name;
 | 
			
		||||
        return acc;
 | 
			
		||||
      }, {});
 | 
			
		||||
      
 | 
			
		||||
      const domainAgnosticNames = domainAgnosticSoftware.reduce((acc, section) => {
 | 
			
		||||
        acc[section.id] = section.name;
 | 
			
		||||
        return acc;
 | 
			
		||||
      }, {});
 | 
			
		||||
      
 | 
			
		||||
      const allPhaseNames = { ...phaseNames, ...domainAgnosticNames };
 | 
			
		||||
      
 | 
			
		||||
      return tool.phases.map(phaseId => allPhaseNames[phaseId]).filter(Boolean).join(', ');
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    const resultsHTML = `
 | 
			
		||||
      <div class="tool-results-container">
 | 
			
		||||
        <div style="text-align: center; margin-bottom: 2rem; padding: 1.5rem; background: linear-gradient(135deg, var(--color-accent) 0%, var(--color-primary) 100%); color: white; border-radius: 0.75rem;">
 | 
			
		||||
          <h3 style="margin: 0 0 0.75rem 0; font-size: 1.5rem;">
 | 
			
		||||
            <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="margin-right: 0.5rem; vertical-align: middle;">
 | 
			
		||||
              <path d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z"/>
 | 
			
		||||
            </svg>
 | 
			
		||||
            Passende Empfehlungen
 | 
			
		||||
          </h3>
 | 
			
		||||
          <p style="margin: 0; opacity: 0.9; line-height: 1.5;">
 | 
			
		||||
            Basierend auf Ihrer Anfrage: "<em>${originalQuery.slice(0, 100)}${originalQuery.length > 100 ? '...' : ''}</em>"
 | 
			
		||||
          </p>
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
        ${renderContextualAnalysis(recommendation, 'tool')}
 | 
			
		||||
 | 
			
		||||
        ${renderBackgroundKnowledge(recommendation.background_knowledge)}
 | 
			
		||||
 | 
			
		||||
        <div class="tool-recommendations-grid" style="display: grid; gap: 1.5rem;">
 | 
			
		||||
          ${recommendation.recommended_tools?.map((toolRec, index) => {
 | 
			
		||||
            const fullTool = tools.find(t => t.name === toolRec.name);
 | 
			
		||||
            if (!fullTool) return '';
 | 
			
		||||
            
 | 
			
		||||
            const hasValidProjectUrl = fullTool.projectUrl !== undefined && 
 | 
			
		||||
                                      fullTool.projectUrl !== null && 
 | 
			
		||||
                                      fullTool.projectUrl !== "" && 
 | 
			
		||||
                                      fullTool.projectUrl.trim() !== "";
 | 
			
		||||
            const isMethod = fullTool.type === 'method';
 | 
			
		||||
            const suitabilityColors = {
 | 
			
		||||
              high: 'var(--color-accent)',
 | 
			
		||||
              medium: 'var(--color-warning)',
 | 
			
		||||
              low: 'var(--color-text-secondary)'
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            const rankColors = {
 | 
			
		||||
              1: 'var(--color-accent)',
 | 
			
		||||
              2: 'var(--color-primary)',
 | 
			
		||||
              3: 'var(--color-warning)'
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            return `
 | 
			
		||||
              <div class="tool-detailed-recommendation card ${isMethod ? 'card-method' : hasValidProjectUrl ? 'card-hosted' : (fullTool.license !== 'Proprietary' ? 'card-oss' : '')}" 
 | 
			
		||||
                   style="cursor: pointer; position: relative;" 
 | 
			
		||||
                   onclick="window.showToolDetails('${fullTool.name}')">
 | 
			
		||||
                
 | 
			
		||||
                <div class="tool-rank-badge" style="position: absolute; top: -8px; right: -8px; width: 32px; height: 32px; background-color: ${rankColors[toolRec.rank]}; color: white; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-weight: bold; font-size: 1.125rem; box-shadow: var(--shadow-md);">
 | 
			
		||||
                  ${toolRec.rank}
 | 
			
		||||
                </div>
 | 
			
		||||
 | 
			
		||||
                <div class="tool-rec-header" style="margin-bottom: 1rem;">
 | 
			
		||||
                  <h3 style="margin: 0 0 0.5rem 0; color: var(--color-text); font-size: 1.25rem;">${fullTool.name}</h3>
 | 
			
		||||
                  <div style="display: flex; flex-wrap: wrap; gap: 0.5rem; align-items: center;">
 | 
			
		||||
                    <span class="badge" style="background-color: ${suitabilityColors[toolRec.suitability_score]}; color: white; font-size: 0.8125rem;">
 | 
			
		||||
                      ${getSuitabilityText(toolRec.suitability_score)}
 | 
			
		||||
                    </span>
 | 
			
		||||
                    ${isMethod ? '<span class="badge" style="background-color: var(--color-method); color: white;">Methode</span>' : ''}
 | 
			
		||||
                    ${!isMethod && hasValidProjectUrl ? '<span class="badge badge-primary">CC24-Server</span>' : ''}
 | 
			
		||||
                    ${!isMethod && fullTool.license !== 'Proprietary' ? '<span class="badge badge-success">Open Source</span>' : ''}
 | 
			
		||||
                    ${fullTool.knowledgebase === true ? '<span class="badge badge-error">📖</span>' : ''}
 | 
			
		||||
                  </div>
 | 
			
		||||
                </div>
 | 
			
		||||
                <div>
 | 
			
		||||
                  ${(() => {
 | 
			
		||||
                    const toolPhases = getToolPhases(fullTool);
 | 
			
		||||
                    return toolPhases ? `
 | 
			
		||||
                      <div style="margin-top: 0.75rem;">
 | 
			
		||||
                        <div style="display: flex; align-items: center; gap: 0.5rem; font-size: 0.8125rem; color: var(--color-text-secondary);">
 | 
			
		||||
                          <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="flex-shrink: 0;">
 | 
			
		||||
                            <polyline points="9,11 12,14 22,4"/>
 | 
			
		||||
                            <path d="M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11"/>
 | 
			
		||||
                          </svg>
 | 
			
		||||
                          <span><strong>Anwendbare Phasen:</strong> ${toolPhases}</span>
 | 
			
		||||
                        </div>
 | 
			
		||||
                      </div>
 | 
			
		||||
                    ` : '';
 | 
			
		||||
                  })()}
 | 
			
		||||
                </div>
 | 
			
		||||
                <div class="tool-detailed-explanation" style="margin-bottom: 1.5rem;">
 | 
			
		||||
                  <h4 style="margin: 0.8rem 0 0.75rem 0; color: var(--color-accent); font-size: 1rem;">
 | 
			
		||||
                    <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="margin-right: 0.5rem; vertical-align: middle;">
 | 
			
		||||
                      <path d="M9 11H5a2 2 0 0 0-2 2v7a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7a2 2 0 0 0-2-2h-4"/>
 | 
			
		||||
                      <path d="M9 11V7a3 3 0 0 1 6 0v4"/>
 | 
			
		||||
                    </svg>
 | 
			
		||||
                    Warum diese Methode für Ihr Szenario?
 | 
			
		||||
                  </h4>
 | 
			
		||||
                  ${formatWorkflowSuggestion(toolRec.detailed_explanation)}                  
 | 
			
		||||
                  ${toolRec.implementation_approach ? `
 | 
			
		||||
                    <h4 style="margin: 0.8rem 0 0.75rem 0; color: var(--color-primary); font-size: 1rem;">
 | 
			
		||||
                      <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="margin-right: 0.5rem; vertical-align: middle;">
 | 
			
		||||
                        <polyline points="9,11 12,14 22,4"/>
 | 
			
		||||
                        <path d="M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11"/>
 | 
			
		||||
                      </svg>
 | 
			
		||||
                      Anwendungsansatz
 | 
			
		||||
                    </h4>
 | 
			
		||||
                    ${formatWorkflowSuggestion(toolRec.implementation_approach)}
 | 
			
		||||
                  ` : ''}
 | 
			
		||||
                </div>
 | 
			
		||||
 | 
			
		||||
                ${(toolRec.pros && toolRec.pros.length > 0) || (toolRec.cons && toolRec.cons.length > 0) ? `
 | 
			
		||||
                  <div class="pros-cons-section" style="display: grid; grid-template-columns: 1fr 1fr; gap: 1rem; margin-bottom: 1.5rem;">
 | 
			
		||||
                    ${toolRec.pros && toolRec.pros.length > 0 ? `
 | 
			
		||||
                      <div class="pros" style="background-color: var(--color-oss-bg); padding: 1rem; border-radius: 0.5rem; border-left: 3px solid var(--color-accent);">
 | 
			
		||||
                        <h5 style="margin: 0 0 0.5rem 0; color: var(--color-accent); font-size: 0.875rem; text-transform: uppercase;">
 | 
			
		||||
                          <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="margin-right: 0.25rem; vertical-align: middle;">
 | 
			
		||||
                            <polyline points="20,6 9,17 4,12"/>
 | 
			
		||||
                          </svg>
 | 
			
		||||
                          Vorteile
 | 
			
		||||
                        </h5>
 | 
			
		||||
                        <ul style="margin: 0; padding-left: 1rem; font-size: 0.875rem; line-height: 1.5;">
 | 
			
		||||
                          ${toolRec.pros.map(pro => `<li style="margin-bottom: 0.25rem;">${pro}</li>`).join('')}
 | 
			
		||||
                        </ul>
 | 
			
		||||
                      </div>
 | 
			
		||||
                    ` : ''}
 | 
			
		||||
                    ${toolRec.cons && toolRec.cons.length > 0 ? `
 | 
			
		||||
                      <div class="cons" style="background-color: var(--color-hosted-bg); padding: 1rem; border-radius: 0.5rem; border-left: 3px solid var(--color-warning);">
 | 
			
		||||
                        <h5 style="margin: 0 0 0.5rem 0; color: var(--color-warning); font-size: 0.875rem; text-transform: uppercase;">
 | 
			
		||||
                          <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="margin-right: 0.25rem; vertical-align: middle;">
 | 
			
		||||
                            <line x1="18" y1="6" x2="6" y2="18"/>
 | 
			
		||||
                            <line x1="6" y1="6" x2="18" y2="18"/>
 | 
			
		||||
                          </svg>
 | 
			
		||||
                          Nachteile
 | 
			
		||||
                        </h5>
 | 
			
		||||
                        <ul style="margin: 0; padding-left: 1rem; font-size: 0.875rem; line-height: 1.5;">
 | 
			
		||||
                          ${toolRec.cons.map(con => `<li style="margin-bottom: 0.25rem;">${con}</li>`).join('')}
 | 
			
		||||
                        </ul>
 | 
			
		||||
                      </div>
 | 
			
		||||
                    ` : ''}
 | 
			
		||||
                  </div>
 | 
			
		||||
                ` : ''}
 | 
			
		||||
 | 
			
		||||
                <div class="tool-metadata" style="display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 0.75rem; font-size: 0.8125rem; color: var(--color-text-secondary); margin-bottom: 1rem;">
 | 
			
		||||
                  ${!isMethod ? '<div><strong>Plattformen:</strong> ' + fullTool.platforms.join(', ') + '</div>' : ''}
 | 
			
		||||
                  <div><strong>Skill Level:</strong> ${fullTool.skillLevel}</div>
 | 
			
		||||
                  ${!isMethod ? '<div><strong>Lizenz:</strong> ' + fullTool.license + '</div>' : ''}
 | 
			
		||||
                  <div><strong>Typ:</strong> ${isMethod ? 'Methode' : fullTool.accessType}</div>
 | 
			
		||||
                </div>
 | 
			
		||||
 | 
			
		||||
                ${toolRec.alternatives ? `
 | 
			
		||||
                  <div class="alternatives" style="background-color: var(--color-bg-secondary); padding: 1rem; border-radius: 0.5rem; margin-bottom: 1rem;">
 | 
			
		||||
                    <h5 style="margin: 0 0 0.5rem 0; color: var(--color-text-secondary); font-size: 0.875rem;">
 | 
			
		||||
                      <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="margin-right: 0.25rem; vertical-align: middle;">
 | 
			
		||||
                        <path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"/>
 | 
			
		||||
                        <polyline points="3.27,6.96 12,12.01 20.73,6.96"/>
 | 
			
		||||
                        <line x1="12" y1="22.08" x2="12" y2="12"/>
 | 
			
		||||
                      </svg>
 | 
			
		||||
                      Alternative Ansätze
 | 
			
		||||
                    </h5>
 | 
			
		||||
                  ${formatWorkflowSuggestion(toolRec.alternatives)}
 | 
			
		||||
                  </div>
 | 
			
		||||
                ` : ''}
 | 
			
		||||
              </div>
 | 
			
		||||
            `;
 | 
			
		||||
          }).join('')}
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
        ${recommendation.additional_considerations ? `
 | 
			
		||||
          <div class="card" style="margin-top: 2rem; background-color: var(--color-bg-secondary); border-left: 4px solid var(--color-text-secondary);">
 | 
			
		||||
            <h4 style="margin: 0 0 1rem 0; color: var(--color-text-secondary); display: flex; align-items: center; gap: 0.5rem;">
 | 
			
		||||
              <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
 | 
			
		||||
                <circle cx="12" cy="12" r="10"/>
 | 
			
		||||
                <line x1="12" y1="8" x2="12" y2="12"/>
 | 
			
		||||
                <line x1="12" y1="16" x2="12.01" y2="16"/>
 | 
			
		||||
              </svg>
 | 
			
		||||
              Zusätzliche Überlegungen
 | 
			
		||||
            </h4>
 | 
			
		||||
            ${formatWorkflowSuggestion(recommendation.additional_considerations)}
 | 
			
		||||
          </div>
 | 
			
		||||
        ` : ''}
 | 
			
		||||
      </div>
 | 
			
		||||
    `;
 | 
			
		||||
    
 | 
			
		||||
    aiResults.innerHTML = '';
 | 
			
		||||
    const tempDiv = document.createElement('div');
 | 
			
		||||
    tempDiv.innerHTML = resultsHTML;
 | 
			
		||||
    aiResults.appendChild(tempDiv);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
    const handleSubmit = async () => {
 | 
			
		||||
      const query = aiInput.value.trim();
 | 
			
		||||
      
 | 
			
		||||
@ -402,456 +909,6 @@ document.addEventListener('DOMContentLoaded', () => {
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  function formatWorkflowSuggestion(text) {
 | 
			
		||||
    const numberedListPattern = /(\d+\.\s)/g;
 | 
			
		||||
    
 | 
			
		||||
    if (numberedListPattern.test(text)) {
 | 
			
		||||
      const items = text.split(/\d+\.\s/).filter(item => item.trim().length > 0);
 | 
			
		||||
      
 | 
			
		||||
      if (items.length > 1) {
 | 
			
		||||
        const listItems = items.map(item => 
 | 
			
		||||
          `<li style="margin-bottom: 0.5rem; line-height: 1.6;">${item.trim()}</li>`
 | 
			
		||||
        ).join('');
 | 
			
		||||
        
 | 
			
		||||
        return `<ol style="margin: 0; padding-left: 1.5rem; color: var(--color-text);">${listItems}</ol>`;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    const bulletPattern = /^[\s]*[-\*•]\s/gm;
 | 
			
		||||
    if (bulletPattern.test(text)) {
 | 
			
		||||
      const items = text.split(/^[\s]*[-\*•]\s/gm).filter(item => item.trim().length > 0);
 | 
			
		||||
      
 | 
			
		||||
      if (items.length > 1) {
 | 
			
		||||
        const listItems = items.map(item => 
 | 
			
		||||
          `<li style="margin-bottom: 0.5rem; line-height: 1.6;">${item.trim()}</li>`
 | 
			
		||||
        ).join('');
 | 
			
		||||
        
 | 
			
		||||
        return `<ul style="margin: 0; padding-left: 1.5rem; color: var(--color-text);">${listItems}</ul>`;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    if (text.includes('\n')) {
 | 
			
		||||
      const lines = text.split('\n').filter(line => line.trim().length > 0);
 | 
			
		||||
      if (lines.length > 1) {
 | 
			
		||||
        const listItems = lines.map(line => 
 | 
			
		||||
          `<li style="margin-bottom: 0.5rem; line-height: 1.6;">${line.trim()}</li>`
 | 
			
		||||
        ).join('');
 | 
			
		||||
        
 | 
			
		||||
        return `<ul style="margin: 0; padding-left: 1.5rem; color: var(--color-text);">${listItems}</ul>`;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    return `<p style="margin: 0; line-height: 1.6; color: var(--color-text);">${text}</p>`;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  function renderBackgroundKnowledge(backgroundKnowledge) {
 | 
			
		||||
    if (!backgroundKnowledge || backgroundKnowledge.length === 0) {
 | 
			
		||||
      return '';
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    const conceptLinks = backgroundKnowledge.map(concept => `
 | 
			
		||||
      <div class="concept-recommendation" style="background-color: var(--color-concept-bg); border: 1px solid var(--color-concept); border-radius: 0.5rem; padding: 1rem; margin-bottom: 0.75rem;">
 | 
			
		||||
        <div style="display: flex; align-items: center; gap: 0.75rem; margin-bottom: 0.5rem;">
 | 
			
		||||
          <button class="concept-link" 
 | 
			
		||||
                  onclick="event.stopPropagation(); window.showToolDetails('${concept.concept_name}', 'secondary')" 
 | 
			
		||||
                  style="background: none; border: none; color: var(--color-concept); font-weight: 600; cursor: pointer; text-decoration: underline; font-size: 0.875rem; padding: 0;"
 | 
			
		||||
                  onmouseover="this.style.color='var(--color-concept-hover)';"
 | 
			
		||||
                  onmouseout="this.style.color='var(--color-concept)';">
 | 
			
		||||
            📚 ${concept.concept_name}
 | 
			
		||||
          </button>
 | 
			
		||||
          <span class="badge" style="background-color: var(--color-concept); color: white; font-size: 0.625rem;">Hintergrundwissen</span>
 | 
			
		||||
        </div>
 | 
			
		||||
        <p style="margin: 0; font-size: 0.8125rem; line-height: 1.5; color: var(--color-text-secondary);">
 | 
			
		||||
          ${concept.relevance}
 | 
			
		||||
        </p>
 | 
			
		||||
      </div>
 | 
			
		||||
    `).join('');
 | 
			
		||||
    
 | 
			
		||||
    return `
 | 
			
		||||
      <div class="card" style="margin-bottom: 2rem; border-left: 4px solid var(--color-concept);">
 | 
			
		||||
        <h4 style="margin: 0 0 1rem 0; color: var(--color-concept); display: flex; align-items: center; gap: 0.5rem;">
 | 
			
		||||
          <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
 | 
			
		||||
            <path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"/>
 | 
			
		||||
            <path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z"/>
 | 
			
		||||
          </svg>
 | 
			
		||||
          Empfohlenes Hintergrundwissen
 | 
			
		||||
        </h4>
 | 
			
		||||
        ${conceptLinks}
 | 
			
		||||
      </div>
 | 
			
		||||
    `;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  function displayWorkflowResults(recommendation, originalQuery) {
 | 
			
		||||
    const toolsByPhase = {};
 | 
			
		||||
    
 | 
			
		||||
    const phaseOrder = phases.map(phase => phase.id);
 | 
			
		||||
    const phaseNames = phases.reduce((acc, phase) => {
 | 
			
		||||
      acc[phase.id] = phase.name;
 | 
			
		||||
      return acc;
 | 
			
		||||
    }, {});
 | 
			
		||||
 | 
			
		||||
    phaseOrder.forEach(phase => {
 | 
			
		||||
      toolsByPhase[phase] = [];
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    recommendation.recommended_tools?.forEach(recTool => {
 | 
			
		||||
      if (toolsByPhase[recTool.phase]) {
 | 
			
		||||
        const fullTool = tools.find(t => t.name === recTool.name);
 | 
			
		||||
        if (fullTool) {
 | 
			
		||||
          toolsByPhase[recTool.phase].push({
 | 
			
		||||
            ...fullTool,
 | 
			
		||||
            recommendation: recTool
 | 
			
		||||
          });
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    const resultsHTML = `
 | 
			
		||||
      <div class="workflow-container">
 | 
			
		||||
        <div style="text-align: center; margin-bottom: 2rem; padding: 1.5rem; background: linear-gradient(135deg, var(--color-accent) 0%, var(--color-primary) 100%); color: white; border-radius: 0.75rem;">
 | 
			
		||||
          <h3 style="margin: 0 0 0.75rem 0; font-size: 1.5rem;">Empfohlener DFIR-Workflow</h3>
 | 
			
		||||
          <p style="margin: 0; opacity: 0.9; line-height: 1.5;">
 | 
			
		||||
            Basierend auf Ihrer Anfrage: "<em>${originalQuery.slice(0, 100)}${originalQuery.length > 100 ? '...' : ''}</em>"
 | 
			
		||||
          </p>
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
        ${recommendation.scenario_analysis ? `
 | 
			
		||||
          <div class="card" style="margin-bottom: 2rem; border-left: 4px solid var(--color-primary);">
 | 
			
		||||
            <h4 style="margin: 0 0 1rem 0; color: var(--color-primary); display: flex; align-items: center; gap: 0.5rem;">
 | 
			
		||||
              <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
 | 
			
		||||
                <path d="M12 20h9"/>
 | 
			
		||||
                <path d="M16.5 3.5a2.12 2.12 0 0 1 3 3L7 19l-4 1 1-4L16.5 3.5z"/>
 | 
			
		||||
              </svg>
 | 
			
		||||
              Szenario-Analyse
 | 
			
		||||
            </h4>
 | 
			
		||||
            ${formatWorkflowSuggestion(recommendation.scenario_analysis)}
 | 
			
		||||
          </div>
 | 
			
		||||
        ` : ''}
 | 
			
		||||
 | 
			
		||||
        ${renderBackgroundKnowledge(recommendation.background_knowledge)}
 | 
			
		||||
 | 
			
		||||
        ${phaseOrder.map((phase, index) => {
 | 
			
		||||
          const phaseTools = toolsByPhase[phase];
 | 
			
		||||
          if (phaseTools.length === 0) return '';
 | 
			
		||||
 | 
			
		||||
          return `
 | 
			
		||||
            <div class="workflow-phase">
 | 
			
		||||
              <div class="phase-header">
 | 
			
		||||
                <div class="phase-number">${index + 1}</div>
 | 
			
		||||
                <div class="phase-info">
 | 
			
		||||
                  <h3 class="phase-title">${phaseNames[phase]}</h3>
 | 
			
		||||
                  <div class="phase-tools">
 | 
			
		||||
                    ${phaseTools.map(tool => {
 | 
			
		||||
                      const hasValidProjectUrl = tool.projectUrl !== undefined && 
 | 
			
		||||
                                                tool.projectUrl !== null && 
 | 
			
		||||
                                                tool.projectUrl !== "" && 
 | 
			
		||||
                                                tool.projectUrl.trim() !== "";
 | 
			
		||||
                      
 | 
			
		||||
                      const priorityColors = {
 | 
			
		||||
                        high: 'var(--color-error)',
 | 
			
		||||
                        medium: 'var(--color-warning)', 
 | 
			
		||||
                        low: 'var(--color-accent)'
 | 
			
		||||
                      };
 | 
			
		||||
 | 
			
		||||
                      return `
 | 
			
		||||
                        <div class="tool-recommendation ${tool.type === 'method' ? 'method' : hasValidProjectUrl ? 'hosted' : (tool.license !== 'Proprietary' ? 'oss' : '')}"
 | 
			
		||||
                             onclick="window.showToolDetails('${tool.name}')">
 | 
			
		||||
                          <div class="tool-rec-header">
 | 
			
		||||
                            <h4 class="tool-rec-name">
 | 
			
		||||
                              ${tool.icon ? `<span style="margin-right: 0.5rem;">${tool.icon}</span>` : ''}
 | 
			
		||||
                              ${tool.name}
 | 
			
		||||
                            </h4>
 | 
			
		||||
                            <span class="tool-rec-priority ${tool.recommendation.priority}" 
 | 
			
		||||
                                  style="background-color: ${priorityColors[tool.recommendation.priority]};">
 | 
			
		||||
                              ${tool.recommendation.priority}
 | 
			
		||||
                            </span>
 | 
			
		||||
                          </div>
 | 
			
		||||
                          
 | 
			
		||||
                          <div class="tool-rec-justification">
 | 
			
		||||
                            "${tool.recommendation.justification}"
 | 
			
		||||
                          </div>
 | 
			
		||||
                          
 | 
			
		||||
                          <div class="tool-rec-metadata">
 | 
			
		||||
                            <div style="display: flex; flex-wrap: wrap; gap: 0.25rem; margin-bottom: 0.5rem;">
 | 
			
		||||
                              ${tool.type !== 'method' && hasValidProjectUrl ? '<span class="badge badge-primary">CC24-Server</span>' : ''}
 | 
			
		||||
                              ${tool.knowledgebase === true ? '<span class="badge badge-error">📖</span>' : ''}
 | 
			
		||||
                              <span class="badge" style="background-color: var(--color-bg-tertiary); color: var(--color-text);">${tool.skillLevel}</span>
 | 
			
		||||
                            </div>
 | 
			
		||||
                            <div style="font-size: 0.8125rem; color: var(--color-text-secondary);">
 | 
			
		||||
                              ${tool.type === 'method' ? 'Methode' : tool.platforms.join(', ') + ' • ' + tool.license}
 | 
			
		||||
                            </div>
 | 
			
		||||
                          </div>
 | 
			
		||||
                        </div>
 | 
			
		||||
                      `;
 | 
			
		||||
                    }).join('')}
 | 
			
		||||
                  </div>
 | 
			
		||||
                </div>
 | 
			
		||||
              </div>
 | 
			
		||||
              ${index < phaseOrder.length - 1 ? `
 | 
			
		||||
                <div class="workflow-arrow">
 | 
			
		||||
                  <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="var(--color-primary)" stroke-width="2">
 | 
			
		||||
                    <line x1="12" y1="5" x2="12" y2="19"/>
 | 
			
		||||
                    <polyline points="19,12 12,19 5,12"/>
 | 
			
		||||
                  </svg>
 | 
			
		||||
                </div>
 | 
			
		||||
              ` : ''}
 | 
			
		||||
            </div>
 | 
			
		||||
          `;
 | 
			
		||||
        }).join('')}
 | 
			
		||||
 | 
			
		||||
        ${recommendation.workflow_suggestion ? `
 | 
			
		||||
          <div class="card" style="margin-top: 2rem; border-left: 4px solid var(--color-accent);">
 | 
			
		||||
            <h4 style="margin: 0 0 1rem 0; color: var(--color-accent); display: flex; align-items: center; gap: 0.5rem;">
 | 
			
		||||
              <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
 | 
			
		||||
                <polyline points="9,11 12,14 22,4"/>
 | 
			
		||||
                <path d="M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11"/>
 | 
			
		||||
              </svg>
 | 
			
		||||
              Workflow-Empfehlung
 | 
			
		||||
            </h4>
 | 
			
		||||
            ${formatWorkflowSuggestion(recommendation.workflow_suggestion)}
 | 
			
		||||
          </div>
 | 
			
		||||
        ` : ''}
 | 
			
		||||
 | 
			
		||||
        ${recommendation.additional_notes ? `
 | 
			
		||||
          <div class="card" style="margin-top: 1rem; background-color: var(--color-warning); color: white;">
 | 
			
		||||
            <h4 style="margin: 0 0 1rem 0; display: flex; align-items: center; gap: 0.5rem;">
 | 
			
		||||
              <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
 | 
			
		||||
                <circle cx="12" cy="12" r="10"/>
 | 
			
		||||
                <line x1="12" y1="8" x2="12" y2="12"/>
 | 
			
		||||
                <line x1="12" y1="16" x2="12.01" y2="16"/>
 | 
			
		||||
              </svg>
 | 
			
		||||
              Wichtige Hinweise
 | 
			
		||||
            </h4>
 | 
			
		||||
            <div style="color: white;">
 | 
			
		||||
              ${formatWorkflowSuggestion(recommendation.additional_notes).replace(/color: var\(--color-text\)/g, 'color: white')}
 | 
			
		||||
            </div>
 | 
			
		||||
          </div>
 | 
			
		||||
        ` : ''}
 | 
			
		||||
      </div>
 | 
			
		||||
    `;
 | 
			
		||||
    
 | 
			
		||||
    aiResults.innerHTML = '';
 | 
			
		||||
    const tempDiv = document.createElement('div');
 | 
			
		||||
    tempDiv.innerHTML = resultsHTML;
 | 
			
		||||
    aiResults.appendChild(tempDiv);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  function displayToolResults(recommendation, originalQuery) {
 | 
			
		||||
    function getSuitabilityText(score) {
 | 
			
		||||
      const suitabilityTexts = {
 | 
			
		||||
        high: 'GUT GEEIGNET',
 | 
			
		||||
        medium: 'GEEIGNET', 
 | 
			
		||||
        low: 'VIELLEICHT GEEIGNET'
 | 
			
		||||
      };
 | 
			
		||||
      return suitabilityTexts[score] || 'GEEIGNET';
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function getToolPhases(tool) {
 | 
			
		||||
      if (!tool.phases || tool.phases.length === 0) return '';
 | 
			
		||||
      
 | 
			
		||||
      const phaseNames = phases.reduce((acc, phase) => {
 | 
			
		||||
        acc[phase.id] = phase.name;
 | 
			
		||||
        return acc;
 | 
			
		||||
      }, {});
 | 
			
		||||
      
 | 
			
		||||
      const domainAgnosticNames = domainAgnosticSoftware.reduce((acc, section) => {
 | 
			
		||||
        acc[section.id] = section.name;
 | 
			
		||||
        return acc;
 | 
			
		||||
      }, {});
 | 
			
		||||
      
 | 
			
		||||
      const allPhaseNames = { ...phaseNames, ...domainAgnosticNames };
 | 
			
		||||
      
 | 
			
		||||
      return tool.phases.map(phaseId => allPhaseNames[phaseId]).filter(Boolean).join(', ');
 | 
			
		||||
    }
 | 
			
		||||
    const resultsHTML = `
 | 
			
		||||
      <div class="tool-results-container">
 | 
			
		||||
        <div style="text-align: center; margin-bottom: 2rem; padding: 1.5rem; background: linear-gradient(135deg, var(--color-accent) 0%, var(--color-primary) 100%); color: white; border-radius: 0.75rem;">
 | 
			
		||||
          <h3 style="margin: 0 0 0.75rem 0; font-size: 1.5rem;">
 | 
			
		||||
            <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="margin-right: 0.5rem; vertical-align: middle;">
 | 
			
		||||
              <path d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z"/>
 | 
			
		||||
            </svg>
 | 
			
		||||
            Passende Empfehlungen
 | 
			
		||||
          </h3>
 | 
			
		||||
          <p style="margin: 0; opacity: 0.9; line-height: 1.5;">
 | 
			
		||||
            Basierend auf Ihrer Anfrage: "<em>${originalQuery.slice(0, 100)}${originalQuery.length > 100 ? '...' : ''}</em>"
 | 
			
		||||
          </p>
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
        ${recommendation.problem_analysis ? `
 | 
			
		||||
          <div class="card" style="margin-bottom: 2rem; border-left: 4px solid var(--color-primary);">
 | 
			
		||||
            <h4 style="margin: 0 0 1rem 0; color: var(--color-primary); display: flex; align-items: center; gap: 0.5rem;">
 | 
			
		||||
              <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
 | 
			
		||||
                <circle cx="12" cy="12" r="10"/>
 | 
			
		||||
                <path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"/>
 | 
			
		||||
                <line x1="12" y1="17" x2="12.01" y2="17"/>
 | 
			
		||||
              </svg>
 | 
			
		||||
              Problem-Analyse
 | 
			
		||||
            </h4>
 | 
			
		||||
            ${formatWorkflowSuggestion(recommendation.problem_analysis)}
 | 
			
		||||
          </div>
 | 
			
		||||
        ` : ''}
 | 
			
		||||
 | 
			
		||||
        ${renderBackgroundKnowledge(recommendation.background_knowledge)}
 | 
			
		||||
 | 
			
		||||
        <div class="tool-recommendations-grid" style="display: grid; gap: 1.5rem;">
 | 
			
		||||
          ${recommendation.recommended_tools?.map((toolRec, index) => {
 | 
			
		||||
            const fullTool = tools.find(t => t.name === toolRec.name);
 | 
			
		||||
            if (!fullTool) return '';
 | 
			
		||||
            
 | 
			
		||||
            const hasValidProjectUrl = fullTool.projectUrl !== undefined && 
 | 
			
		||||
                                      fullTool.projectUrl !== null && 
 | 
			
		||||
                                      fullTool.projectUrl !== "" && 
 | 
			
		||||
                                      fullTool.projectUrl.trim() !== "";
 | 
			
		||||
            const isMethod = fullTool.type === 'method';
 | 
			
		||||
            const suitabilityColors = {
 | 
			
		||||
              high: 'var(--color-accent)',
 | 
			
		||||
              medium: 'var(--color-warning)',
 | 
			
		||||
              low: 'var(--color-text-secondary)'
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            const rankColors = {
 | 
			
		||||
              1: 'var(--color-accent)',
 | 
			
		||||
              2: 'var(--color-primary)',
 | 
			
		||||
              3: 'var(--color-warning)'
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            return `
 | 
			
		||||
              <div class="tool-detailed-recommendation card ${isMethod ? 'card-method' : hasValidProjectUrl ? 'card-hosted' : (fullTool.license !== 'Proprietary' ? 'card-oss' : '')}" 
 | 
			
		||||
                   style="cursor: pointer; position: relative;" 
 | 
			
		||||
                   onclick="window.showToolDetails('${fullTool.name}')">
 | 
			
		||||
                
 | 
			
		||||
                <div class="tool-rank-badge" style="position: absolute; top: -8px; right: -8px; width: 32px; height: 32px; background-color: ${rankColors[toolRec.rank]}; color: white; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-weight: bold; font-size: 1.125rem; box-shadow: var(--shadow-md);">
 | 
			
		||||
                  ${toolRec.rank}
 | 
			
		||||
                </div>
 | 
			
		||||
 | 
			
		||||
                <div class="tool-rec-header" style="margin-bottom: 1rem;">
 | 
			
		||||
                  <h3 style="margin: 0 0 0.5rem 0; color: var(--color-text); font-size: 1.25rem;">${fullTool.name}</h3>
 | 
			
		||||
                  <div style="display: flex; flex-wrap: wrap; gap: 0.5rem; align-items: center;">
 | 
			
		||||
                    <span class="badge" style="background-color: ${suitabilityColors[toolRec.suitability_score]}; color: white; font-size: 0.8125rem;">
 | 
			
		||||
                      ${getSuitabilityText(toolRec.suitability_score)}
 | 
			
		||||
                    </span>
 | 
			
		||||
                    ${isMethod ? '<span class="badge" style="background-color: var(--color-method); color: white;">Methode</span>' : ''}
 | 
			
		||||
                    ${!isMethod && hasValidProjectUrl ? '<span class="badge badge-primary">CC24-Server</span>' : ''}
 | 
			
		||||
                    ${!isMethod && fullTool.license !== 'Proprietary' ? '<span class="badge badge-success">Open Source</span>' : ''}
 | 
			
		||||
                    ${fullTool.knowledgebase === true ? '<span class="badge badge-error">📖</span>' : ''}
 | 
			
		||||
                  </div>
 | 
			
		||||
                </div>
 | 
			
		||||
                <div>
 | 
			
		||||
                  ${(() => {
 | 
			
		||||
                    const toolPhases = getToolPhases(fullTool);
 | 
			
		||||
                    return toolPhases ? `
 | 
			
		||||
                      <div style="margin-top: 0.75rem;">
 | 
			
		||||
                        <div style="display: flex; align-items: center; gap: 0.5rem; font-size: 0.8125rem; color: var(--color-text-secondary);">
 | 
			
		||||
                          <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="flex-shrink: 0;">
 | 
			
		||||
                            <polyline points="9,11 12,14 22,4"/>
 | 
			
		||||
                            <path d="M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11"/>
 | 
			
		||||
                          </svg>
 | 
			
		||||
                          <span><strong>Anwendbare Phasen:</strong> ${toolPhases}</span>
 | 
			
		||||
                        </div>
 | 
			
		||||
                      </div>
 | 
			
		||||
                    ` : '';
 | 
			
		||||
                  })()}
 | 
			
		||||
                </div>
 | 
			
		||||
                <div class="tool-detailed-explanation" style="margin-bottom: 1.5rem;">
 | 
			
		||||
                  <h4 style="margin: 0.8rem 0 0.75rem 0; color: var(--color-accent); font-size: 1rem;">
 | 
			
		||||
                    <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="margin-right: 0.5rem; vertical-align: middle;">
 | 
			
		||||
                      <path d="M9 11H5a2 2 0 0 0-2 2v7a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7a2 2 0 0 0-2-2h-4"/>
 | 
			
		||||
                      <path d="M9 11V7a3 3 0 0 1 6 0v4"/>
 | 
			
		||||
                    </svg>
 | 
			
		||||
                    Warum diese Methode?
 | 
			
		||||
                  </h4>
 | 
			
		||||
                  ${formatWorkflowSuggestion(toolRec.detailed_explanation)}                  
 | 
			
		||||
                  ${toolRec.implementation_approach ? `
 | 
			
		||||
                    <h4 style="margin: 0.8rem 0 0.75rem 0; color: var(--color-primary); font-size: 1rem;">
 | 
			
		||||
                      <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="margin-right: 0.5rem; vertical-align: middle;">
 | 
			
		||||
                        <polyline points="9,11 12,14 22,4"/>
 | 
			
		||||
                        <path d="M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11"/>
 | 
			
		||||
                      </svg>
 | 
			
		||||
                      Anwendungsansatz
 | 
			
		||||
                    </h4>
 | 
			
		||||
                    ${formatWorkflowSuggestion(toolRec.implementation_approach)}
 | 
			
		||||
                  ` : ''}
 | 
			
		||||
                </div>
 | 
			
		||||
 | 
			
		||||
                ${(toolRec.pros && toolRec.pros.length > 0) || (toolRec.cons && toolRec.cons.length > 0) ? `
 | 
			
		||||
                  <div class="pros-cons-section" style="display: grid; grid-template-columns: 1fr 1fr; gap: 1rem; margin-bottom: 1.5rem;">
 | 
			
		||||
                    ${toolRec.pros && toolRec.pros.length > 0 ? `
 | 
			
		||||
                      <div class="pros" style="background-color: var(--color-oss-bg); padding: 1rem; border-radius: 0.5rem; border-left: 3px solid var(--color-accent);">
 | 
			
		||||
                        <h5 style="margin: 0 0 0.5rem 0; color: var(--color-accent); font-size: 0.875rem; text-transform: uppercase;">
 | 
			
		||||
                          <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="margin-right: 0.25rem; vertical-align: middle;">
 | 
			
		||||
                            <polyline points="20,6 9,17 4,12"/>
 | 
			
		||||
                          </svg>
 | 
			
		||||
                          Vorteile
 | 
			
		||||
                        </h5>
 | 
			
		||||
                        <ul style="margin: 0; padding-left: 1rem; font-size: 0.875rem; line-height: 1.5;">
 | 
			
		||||
                          ${toolRec.pros.map(pro => `<li style="margin-bottom: 0.25rem;">${pro}</li>`).join('')}
 | 
			
		||||
                        </ul>
 | 
			
		||||
                      </div>
 | 
			
		||||
                    ` : ''}
 | 
			
		||||
                    ${toolRec.cons && toolRec.cons.length > 0 ? `
 | 
			
		||||
                      <div class="cons" style="background-color: var(--color-hosted-bg); padding: 1rem; border-radius: 0.5rem; border-left: 3px solid var(--color-warning);">
 | 
			
		||||
                        <h5 style="margin: 0 0 0.5rem 0; color: var(--color-warning); font-size: 0.875rem; text-transform: uppercase;">
 | 
			
		||||
                          <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="margin-right: 0.25rem; vertical-align: middle;">
 | 
			
		||||
                            <line x1="18" y1="6" x2="6" y2="18"/>
 | 
			
		||||
                            <line x1="6" y1="6" x2="18" y2="18"/>
 | 
			
		||||
                          </svg>
 | 
			
		||||
                          Nachteile
 | 
			
		||||
                        </h5>
 | 
			
		||||
                        <ul style="margin: 0; padding-left: 1rem; font-size: 0.875rem; line-height: 1.5;">
 | 
			
		||||
                          ${toolRec.cons.map(con => `<li style="margin-bottom: 0.25rem;">${con}</li>`).join('')}
 | 
			
		||||
                        </ul>
 | 
			
		||||
                      </div>
 | 
			
		||||
                    ` : ''}
 | 
			
		||||
                  </div>
 | 
			
		||||
                ` : ''}
 | 
			
		||||
 | 
			
		||||
                <div class="tool-metadata" style="display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 0.75rem; font-size: 0.8125rem; color: var(--color-text-secondary); margin-bottom: 1rem;">
 | 
			
		||||
                  ${!isMethod ? '<div><strong>Plattformen:</strong> ' + fullTool.platforms.join(', ') + '</div>' : ''}
 | 
			
		||||
                  <div><strong>Skill Level:</strong> ${fullTool.skillLevel}</div>
 | 
			
		||||
                  ${!isMethod ? '<div><strong>Lizenz:</strong> ' + fullTool.license + '</div>' : ''}
 | 
			
		||||
                  <div><strong>Typ:</strong> ${isMethod ? 'Methode' : fullTool.accessType}</div>
 | 
			
		||||
                </div>
 | 
			
		||||
 | 
			
		||||
                ${toolRec.alternatives ? `
 | 
			
		||||
                  <div class="alternatives" style="background-color: var(--color-bg-secondary); padding: 1rem; border-radius: 0.5rem; margin-bottom: 1rem;">
 | 
			
		||||
                    <h5 style="margin: 0 0 0.5rem 0; color: var(--color-text-secondary); font-size: 0.875rem;">
 | 
			
		||||
                      <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="margin-right: 0.25rem; vertical-align: middle;">
 | 
			
		||||
                        <path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"/>
 | 
			
		||||
                        <polyline points="3.27,6.96 12,12.01 20.73,6.96"/>
 | 
			
		||||
                        <line x1="12" y1="22.08" x2="12" y2="12"/>
 | 
			
		||||
                      </svg>
 | 
			
		||||
                      Alternative Ansätze
 | 
			
		||||
                    </h5>
 | 
			
		||||
                  ${formatWorkflowSuggestion(toolRec.alternatives)}
 | 
			
		||||
                  </div>
 | 
			
		||||
                ` : ''}
 | 
			
		||||
              </div>
 | 
			
		||||
            `;
 | 
			
		||||
          }).join('')}
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
        ${recommendation.additional_considerations ? `
 | 
			
		||||
          <div class="card" style="margin-top: 2rem; background-color: var(--color-bg-secondary); border-left: 4px solid var(--color-text-secondary);">
 | 
			
		||||
            <h4 style="margin: 0 0 1rem 0; color: var(--color-text-secondary); display: flex; align-items: center; gap: 0.5rem;">
 | 
			
		||||
              <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
 | 
			
		||||
                <circle cx="12" cy="12" r="10"/>
 | 
			
		||||
                <line x1="12" y1="8" x2="12" y2="12"/>
 | 
			
		||||
                <line x1="12" y1="16" x2="12.01" y2="16"/>
 | 
			
		||||
              </svg>
 | 
			
		||||
              Zusätzliche Überlegungen
 | 
			
		||||
            </h4>
 | 
			
		||||
            ${formatWorkflowSuggestion(recommendation.additional_considerations)}
 | 
			
		||||
          </div>
 | 
			
		||||
        ` : ''}
 | 
			
		||||
      </div>
 | 
			
		||||
    `;
 | 
			
		||||
    
 | 
			
		||||
    aiResults.innerHTML = '';
 | 
			
		||||
    const tempDiv = document.createElement('div');
 | 
			
		||||
    tempDiv.innerHTML = resultsHTML;
 | 
			
		||||
    aiResults.appendChild(tempDiv);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  updateModeUI();
 | 
			
		||||
});
 | 
			
		||||
</script>
 | 
			
		||||
							
								
								
									
										182
									
								
								src/pages/api/ai/enhance-input.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										182
									
								
								src/pages/api/ai/enhance-input.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,182 @@
 | 
			
		||||
// src/pages/api/ai/enhance-input.ts
 | 
			
		||||
import type { APIRoute } from 'astro';
 | 
			
		||||
import { withAPIAuth } from '../../../utils/auth.js';
 | 
			
		||||
import { apiError, apiServerError, createAuthErrorResponse } from '../../../utils/api.js';
 | 
			
		||||
import { enqueueApiCall } from '../../../utils/rateLimitedQueue.js';
 | 
			
		||||
 | 
			
		||||
export const prerender = false;
 | 
			
		||||
 | 
			
		||||
function getEnv(key: string): string {
 | 
			
		||||
  const value = process.env[key];
 | 
			
		||||
  if (!value) {
 | 
			
		||||
    throw new Error(`Missing environment variable: ${key}`);
 | 
			
		||||
  }
 | 
			
		||||
  return value;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const AI_MODEL = getEnv('AI_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
 | 
			
		||||
 | 
			
		||||
function sanitizeInput(input: string): string {
 | 
			
		||||
  return input
 | 
			
		||||
    .replace(/```[\s\S]*?```/g, '[CODE_BLOCK_REMOVED]')
 | 
			
		||||
    .replace(/\<\/?[^>]+(>|$)/g, '')
 | 
			
		||||
    .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
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function checkRateLimit(userId: string): boolean {
 | 
			
		||||
  const now = Date.now();
 | 
			
		||||
  const userLimit = rateLimitStore.get(userId);
 | 
			
		||||
  
 | 
			
		||||
  if (!userLimit || now > userLimit.resetTime) {
 | 
			
		||||
    rateLimitStore.set(userId, { count: 1, resetTime: now + RATE_LIMIT_WINDOW });
 | 
			
		||||
    return true;
 | 
			
		||||
  }
 | 
			
		||||
  
 | 
			
		||||
  if (userLimit.count >= RATE_LIMIT_MAX) {
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
  
 | 
			
		||||
  userLimit.count++;
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function cleanupExpiredRateLimits() {
 | 
			
		||||
  const now = Date.now();
 | 
			
		||||
  for (const [userId, limit] of rateLimitStore.entries()) {
 | 
			
		||||
    if (now > limit.resetTime) {
 | 
			
		||||
      rateLimitStore.delete(userId);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Clean up expired limits every 5 minutes
 | 
			
		||||
setInterval(cleanupExpiredRateLimits, 5 * 60 * 1000);
 | 
			
		||||
 | 
			
		||||
function createEnhancementPrompt(input: string): string {
 | 
			
		||||
  return `Analysiere diese forensische Szenario-Beschreibung und schlage 2-3 kurze, präzise Fragen vor, die dem Nutzer helfen würden, vollständigere Informationen zu liefern.
 | 
			
		||||
 | 
			
		||||
Nutzer-Eingabe: "${input}"
 | 
			
		||||
 | 
			
		||||
Konzentriere dich auf wichtige Details die für eine forensische Untersuchung relevant sind:
 | 
			
		||||
- Betroffene Systeme/Plattformen
 | 
			
		||||
- Zeitrahmen/Timeline
 | 
			
		||||
- Verfügbare Evidenz
 | 
			
		||||
- Verdächtige Aktivitäten
 | 
			
		||||
- Technische Details
 | 
			
		||||
 | 
			
		||||
Antworte NUR mit einem JSON-Array von 2-3 kurzen Fragen (max. 60 Zeichen pro Frage):
 | 
			
		||||
["Frage 1?", "Frage 2?", "Frage 3?"]
 | 
			
		||||
 | 
			
		||||
Keine zusätzlichen Erklärungen.`;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const POST: APIRoute = async ({ request }) => {
 | 
			
		||||
  try {
 | 
			
		||||
    const authResult = await withAPIAuth(request, 'ai');
 | 
			
		||||
    if (!authResult.authenticated) {
 | 
			
		||||
      return createAuthErrorResponse();
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    const userId = authResult.userId;
 | 
			
		||||
 | 
			
		||||
    if (!checkRateLimit(userId)) {
 | 
			
		||||
      return apiError.rateLimit('Enhancement rate limit exceeded');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const body = await request.json();
 | 
			
		||||
    const { input } = body;
 | 
			
		||||
 | 
			
		||||
    if (!input || typeof input !== 'string' || input.length < 20) {
 | 
			
		||||
      return apiError.badRequest('Input too short for enhancement');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const sanitizedInput = sanitizeInput(input);
 | 
			
		||||
    if (sanitizedInput.length < 20) {
 | 
			
		||||
      return apiError.badRequest('Input too short after sanitization');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const systemPrompt = createEnhancementPrompt(sanitizedInput);
 | 
			
		||||
    const taskId = `enhance_${userId}_${Date.now()}_${Math.random().toString(36).substr(2, 4)}`;
 | 
			
		||||
    
 | 
			
		||||
    const aiResponse = await enqueueApiCall(() =>
 | 
			
		||||
      fetch(process.env.AI_API_ENDPOINT + '/v1/chat/completions', {
 | 
			
		||||
        method: 'POST',
 | 
			
		||||
        headers: {
 | 
			
		||||
          'Content-Type': 'application/json',
 | 
			
		||||
          'Authorization': `Bearer ${process.env.AI_API_KEY}`
 | 
			
		||||
        },
 | 
			
		||||
        body: JSON.stringify({
 | 
			
		||||
          model: AI_MODEL,
 | 
			
		||||
          messages: [
 | 
			
		||||
            {
 | 
			
		||||
              role: 'user',
 | 
			
		||||
              content: systemPrompt
 | 
			
		||||
            }
 | 
			
		||||
          ],
 | 
			
		||||
          max_tokens: 200,
 | 
			
		||||
          temperature: 0.7
 | 
			
		||||
        })
 | 
			
		||||
      }), taskId);
 | 
			
		||||
 | 
			
		||||
    if (!aiResponse.ok) {
 | 
			
		||||
      console.error('AI enhancement error:', await aiResponse.text());
 | 
			
		||||
      return apiServerError.unavailable('Enhancement service unavailable');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const aiData = await aiResponse.json();
 | 
			
		||||
    const aiContent = aiData.choices?.[0]?.message?.content;
 | 
			
		||||
 | 
			
		||||
    if (!aiContent) {
 | 
			
		||||
      return apiServerError.unavailable('No enhancement response');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    let questions;
 | 
			
		||||
    try {
 | 
			
		||||
      // Clean up the response and parse JSON
 | 
			
		||||
      const cleanedContent = aiContent
 | 
			
		||||
        .replace(/^```json\s*/i, '')
 | 
			
		||||
        .replace(/\s*```\s*$/, '')
 | 
			
		||||
        .trim();
 | 
			
		||||
      
 | 
			
		||||
      questions = JSON.parse(cleanedContent);
 | 
			
		||||
      
 | 
			
		||||
      if (!Array.isArray(questions) || questions.length === 0) {
 | 
			
		||||
        throw new Error('Invalid questions format');
 | 
			
		||||
      }
 | 
			
		||||
      
 | 
			
		||||
      // Validate and clean questions
 | 
			
		||||
      questions = questions
 | 
			
		||||
        .filter(q => typeof q === 'string' && q.length > 5 && q.length < 100)
 | 
			
		||||
        .slice(0, 3);
 | 
			
		||||
        
 | 
			
		||||
      if (questions.length === 0) {
 | 
			
		||||
        throw new Error('No valid questions found');
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
    } catch (error) {
 | 
			
		||||
      console.error('Failed to parse enhancement response:', aiContent);
 | 
			
		||||
      return apiServerError.unavailable('Invalid enhancement response format');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    console.log(`[AI Enhancement] User: ${userId}, Questions: ${questions.length}, Input length: ${sanitizedInput.length}`);
 | 
			
		||||
 | 
			
		||||
    return new Response(JSON.stringify({
 | 
			
		||||
      success: true,
 | 
			
		||||
      questions,
 | 
			
		||||
      taskId
 | 
			
		||||
    }), {
 | 
			
		||||
      status: 200,
 | 
			
		||||
      headers: { 'Content-Type': 'application/json' }
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
  } catch (error) {
 | 
			
		||||
    console.error('Enhancement error:', error);
 | 
			
		||||
    return apiServerError.internal('Enhancement processing failed');
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
@ -155,6 +155,11 @@ WICHTIGE REGELN:
 | 
			
		||||
8. WICHTIG: Erwähne relevante Hintergrundwissen-Konzepte wenn Tools verwendet werden, die related_concepts haben
 | 
			
		||||
9. Konzepte sind NICHT Tools - empfehle sie nicht als actionable Schritte, sondern als Wissensbasis
 | 
			
		||||
 | 
			
		||||
ENHANCED CONTEXTUAL ANALYSIS:
 | 
			
		||||
10. Analysiere das Szenario detailliert und identifiziere Schlüsselelemente, Bedrohungen und forensische Herausforderungen
 | 
			
		||||
11. Entwickle einen strategischen Untersuchungsansatz basierend auf dem spezifischen Szenario
 | 
			
		||||
12. Identifiziere zeitkritische oder besonders wichtige Faktoren für diesen Fall
 | 
			
		||||
 | 
			
		||||
SOFTWARE/METHODEN-AUSWAHL NACH PHASE:
 | 
			
		||||
${phaseDescriptions}
 | 
			
		||||
 | 
			
		||||
@ -163,16 +168,18 @@ ${domainAgnosticDescriptions}
 | 
			
		||||
 | 
			
		||||
ANTWORT-FORMAT (strict JSON):
 | 
			
		||||
{
 | 
			
		||||
  "scenario_analysis": "Detaillierte Analyse des Szenarios auf Deutsch/English",
 | 
			
		||||
  "scenario_analysis": "Detaillierte Analyse des Szenarios: Erkannte Schlüsselelemente, Art des Vorfalls, betroffene Systeme, potentielle Bedrohungen und forensische Herausforderungen",
 | 
			
		||||
  "investigation_approach": "Strategischer Untersuchungsansatz für dieses spezifische Szenario: Prioritäten, Reihenfolge der Phasen, besondere Überlegungen",
 | 
			
		||||
  "critical_considerations": "Zeitkritische Faktoren, wichtige Sicherheitsaspekte oder besondere Vorsichtsmaßnahmen für diesen Fall",
 | 
			
		||||
  "recommended_tools": [
 | 
			
		||||
    {
 | 
			
		||||
      "name": "EXAKTER Name aus der Tools-Database",
 | 
			
		||||
      "priority": "high|medium|low", 
 | 
			
		||||
      "phase": "${validPhases}",
 | 
			
		||||
      "justification": "Warum diese Methode für diese Phase und Szenario geeignet ist"
 | 
			
		||||
      "justification": "Warum diese Methode für diese Phase und dieses spezifische Szenario geeignet ist - mit Bezug zu den erkannten Schlüsselelementen"
 | 
			
		||||
    }
 | 
			
		||||
  ],
 | 
			
		||||
  "workflow_suggestion": "Vorgeschlagener Untersuchungsablauf",
 | 
			
		||||
  "workflow_suggestion": "Vorgeschlagener Untersuchungsablauf mit konkreten Schritten für dieses Szenario",
 | 
			
		||||
  "background_knowledge": [
 | 
			
		||||
    {
 | 
			
		||||
      "concept_name": "EXAKTER Name aus der Konzepte-Database",
 | 
			
		||||
@ -230,15 +237,22 @@ WICHTIGE REGELN:
 | 
			
		||||
10. WICHTIG: Erwähne relevante Hintergrundwissen-Konzepte wenn Tools verwendet werden, die related_concepts haben
 | 
			
		||||
11. Konzepte sind NICHT Tools - empfehle sie nicht als actionable Schritte, sondern als Wissensbasis
 | 
			
		||||
 | 
			
		||||
ENHANCED CONTEXTUAL ANALYSIS:
 | 
			
		||||
12. Analysiere das Problem detailliert und identifiziere technische Anforderungen, Herausforderungen und Erfolgsfaktoren
 | 
			
		||||
13. Entwickle einen strategischen Lösungsansatz basierend auf dem spezifischen Problem
 | 
			
		||||
14. Identifiziere wichtige Voraussetzungen oder Warnungen für die Anwendung
 | 
			
		||||
 | 
			
		||||
ANTWORT-FORMAT (strict JSON):
 | 
			
		||||
{
 | 
			
		||||
  "problem_analysis": "Detaillierte Analyse des Problems/der Anforderung",
 | 
			
		||||
  "problem_analysis": "Detaillierte Analyse des Problems: Erkannte technische Anforderungen, Herausforderungen, benötigte Fähigkeiten und Erfolgsfaktoren",
 | 
			
		||||
  "investigation_approach": "Strategischer Lösungsansatz für dieses spezifische Problem: Herangehensweise, Prioritäten, optimale Anwendungsreihenfolge",
 | 
			
		||||
  "critical_considerations": "Wichtige Voraussetzungen, potentielle Fallstricke oder Warnungen für die Anwendung der empfohlenen Lösungen",
 | 
			
		||||
  "recommended_tools": [
 | 
			
		||||
    {
 | 
			
		||||
      "name": "EXAKTER Name aus der Tools-Database",
 | 
			
		||||
      "rank": 1,
 | 
			
		||||
      "suitability_score": "high|medium|low",
 | 
			
		||||
      "detailed_explanation": "Detaillierte Erklärung, warum dieses Tool/diese Methode das Problem löst",
 | 
			
		||||
      "detailed_explanation": "Detaillierte Erklärung, warum dieses Tool/diese Methode das spezifische Problem löst - mit Bezug zu den erkannten Anforderungen",
 | 
			
		||||
      "implementation_approach": "Konkrete Schritte/Ansatz zur Anwendung für dieses spezifische Problem",
 | 
			
		||||
      "pros": ["Spezifische Vorteile für diesen Anwendungsfall", "Weitere Vorteile"],
 | 
			
		||||
      "cons": ["Potentielle Nachteile oder Limitationen", "Weitere Einschränkungen"],
 | 
			
		||||
@ -348,6 +362,10 @@ export const POST: APIRoute = async ({ request }) => {
 | 
			
		||||
    if (mode === 'workflow') {
 | 
			
		||||
      validatedRecommendation = {
 | 
			
		||||
        ...recommendation,
 | 
			
		||||
        // Ensure all new fields are included with fallbacks
 | 
			
		||||
        scenario_analysis: recommendation.scenario_analysis || recommendation.problem_analysis || '',
 | 
			
		||||
        investigation_approach: recommendation.investigation_approach || '',
 | 
			
		||||
        critical_considerations: recommendation.critical_considerations || '',
 | 
			
		||||
        recommended_tools: recommendation.recommended_tools?.filter((tool: any) => {
 | 
			
		||||
          if (!validToolNames.has(tool.name)) {
 | 
			
		||||
            console.warn(`AI recommended unknown tool: ${tool.name}`);
 | 
			
		||||
@ -366,6 +384,10 @@ export const POST: APIRoute = async ({ request }) => {
 | 
			
		||||
    } else {
 | 
			
		||||
      validatedRecommendation = {
 | 
			
		||||
        ...recommendation,
 | 
			
		||||
        // Ensure all new fields are included with fallbacks
 | 
			
		||||
        problem_analysis: recommendation.problem_analysis || recommendation.scenario_analysis || '',
 | 
			
		||||
        investigation_approach: recommendation.investigation_approach || '',
 | 
			
		||||
        critical_considerations: recommendation.critical_considerations || '',
 | 
			
		||||
        recommended_tools: recommendation.recommended_tools?.filter((tool: any) => {
 | 
			
		||||
          if (!validToolNames.has(tool.name)) {
 | 
			
		||||
            console.warn(`AI recommended unknown tool: ${tool.name}`);
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user