phase 2 + css

This commit is contained in:
overcuriousity 2025-07-26 21:07:24 +02:00
parent 72d5f267f0
commit 867a651a55
2 changed files with 349 additions and 9 deletions

View File

@ -58,13 +58,28 @@ const domainAgnosticSoftware = data['domain-agnostic-software'] || [];
<div class="card" style="border-left: 4px solid var(--color-accent); padding: 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;"> <div style="display: flex; align-items: center; gap: 0.5rem; margin-bottom: 0.75rem;">
<span id="prompting-status">💡 KI analysiert Ihre Eingabe...</span> <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 id="prompting-spinner" style="display: none;">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="var(--color-accent)" stroke-width="2" style="animation: spin 1s linear infinite;">
<path d="M21 12a9 9 0 11-6.219-8.56"/>
</svg>
</div>
</div> </div>
<div id="suggested-questions" style="display: none;"> <div id="suggested-questions" style="display: none;">
<p style="font-size: 0.875rem; color: var(--color-text-secondary); margin-bottom: 0.5rem;"> <p style="margin: 0 0 0.75rem 0; font-size: 0.875rem; color: var(--color-text-secondary); line-height: 1.5;">
Klicken Sie auf eine Frage, um sie zu Ihrer Beschreibung hinzuzufügen: Zur besseren Analyse Ihres Szenarios könnten diese Informationen hilfreich sein:
</p> </p>
<div id="questions-container" style="display: flex; flex-direction: column; gap: 0.375rem;"></div> <div id="questions-list" style="display: flex; flex-direction: column; gap: 0.5rem;">
<!-- Questions will be populated here -->
</div>
<div style="margin-top: 1rem; text-align: right;">
<button id="dismiss-suggestions" class="btn-icon" style="font-size: 0.75rem; padding: 0.25rem 0.5rem; border-radius: 0.25rem; background-color: var(--color-bg-secondary);">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="margin-right: 0.25rem;">
<line x1="18" y1="6" x2="6" y2="18"/>
<line x1="6" y1="6" x2="18" y2="18"/>
</svg>
Ausblenden
</button>
</div>
</div> </div>
</div> </div>
</div> </div>
@ -165,6 +180,14 @@ document.addEventListener('DOMContentLoaded', () => {
const aiResults = document.getElementById('ai-results'); const aiResults = document.getElementById('ai-results');
const aiDescription = document.getElementById('ai-description'); const aiDescription = document.getElementById('ai-description');
// Smart prompting elements
const smartPromptingContainer = document.getElementById('smart-prompting-container');
const promptingStatus = document.getElementById('prompting-status');
const promptingSpinner = document.getElementById('prompting-spinner');
const suggestedQuestions = document.getElementById('suggested-questions');
const questionsList = document.getElementById('questions-list');
const dismissSuggestions = document.getElementById('dismiss-suggestions');
const toggleSwitch = document.querySelector('.toggle-switch'); const toggleSwitch = document.querySelector('.toggle-switch');
const toggleSlider = document.querySelector('.toggle-slider'); const toggleSlider = document.querySelector('.toggle-slider');
const workflowLabel = document.getElementById('workflow-label'); const workflowLabel = document.getElementById('workflow-label');
@ -173,6 +196,11 @@ document.addEventListener('DOMContentLoaded', () => {
let currentRecommendation = null; let currentRecommendation = null;
let currentMode = 'workflow'; let currentMode = 'workflow';
// Smart prompting state
let enhancementTimeout;
let enhancementAbortController;
let suggestionsVisible = false;
if (!aiInput || !aiSubmitBtn || !aiLoading || !aiError || !aiResults) { if (!aiInput || !aiSubmitBtn || !aiLoading || !aiError || !aiResults) {
console.error('AI interface elements not found'); console.error('AI interface elements not found');
return; return;
@ -197,11 +225,8 @@ document.addEventListener('DOMContentLoaded', () => {
const config = modeConfig[currentMode]; const config = modeConfig[currentMode];
aiInput.placeholder = config.placeholder; aiInput.placeholder = config.placeholder;
aiDescription.textContent = config.description; aiDescription.textContent = config.description;
submitBtnText.textContent = config.submitText; submitBtnText.textContent = config.submitText;
loadingText.textContent = config.loadingText; loadingText.textContent = config.loadingText;
if (currentMode === 'workflow') { if (currentMode === 'workflow') {
@ -228,10 +253,178 @@ document.addEventListener('DOMContentLoaded', () => {
function switchToMode(mode) { function switchToMode(mode) {
if (currentMode !== mode) { if (currentMode !== mode) {
currentMode = mode; currentMode = mode;
resetSmartPrompting();
updateModeUI(); updateModeUI();
} }
} }
// Smart Prompting Functions
function showPromptingStatus(state) {
if (!smartPromptingContainer || !promptingStatus || !promptingSpinner) return;
switch (state) {
case 'analyzing':
smartPromptingContainer.style.display = 'block';
promptingStatus.textContent = '💡 KI analysiert Ihre Eingabe...';
promptingSpinner.style.display = 'inline-block';
suggestedQuestions.style.display = 'none';
suggestionsVisible = false;
break;
case 'suggestions':
promptingStatus.textContent = '✅ Verbesserungsvorschläge verfügbar';
promptingSpinner.style.display = 'none';
suggestedQuestions.style.display = 'block';
suggestionsVisible = true;
break;
case 'rate-limited':
promptingStatus.textContent = '⏸️ Verbesserungen verfügbar nach Hauptabfrage';
promptingSpinner.style.display = 'none';
suggestedQuestions.style.display = 'none';
suggestionsVisible = false;
break;
case 'error':
promptingStatus.textContent = '⚠️ Verbesserungen temporär nicht verfügbar';
promptingSpinner.style.display = 'none';
suggestedQuestions.style.display = 'none';
suggestionsVisible = false;
break;
case 'hidden':
smartPromptingContainer.style.display = 'none';
suggestionsVisible = false;
break;
}
}
function displaySuggestions(suggestions) {
if (!questionsList || !suggestions || suggestions.length === 0) return;
questionsList.innerHTML = '';
suggestions.forEach((question, index) => {
const questionElement = document.createElement('div');
questionElement.style.cssText = `
background-color: var(--color-bg-secondary);
border: 1px solid var(--color-border);
border-radius: 0.375rem;
padding: 0.75rem;
cursor: pointer;
transition: var(--transition-fast);
font-size: 0.875rem;
line-height: 1.4;
`;
questionElement.innerHTML = `
<div style="display: flex; align-items: start; gap: 0.5rem;">
<span style="color: var(--color-accent); font-weight: bold; flex-shrink: 0;">${index + 1}.</span>
<span style="flex: 1;">${question}</span>
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="var(--color-text-secondary)" stroke-width="2" style="flex-shrink: 0; margin-top: 0.125rem;">
<line x1="12" y1="5" x2="12" y2="19"/>
<line x1="5" y1="12" x2="19" y2="12"/>
</svg>
</div>
`;
questionElement.addEventListener('mouseenter', () => {
questionElement.style.backgroundColor = 'var(--color-bg-tertiary)';
questionElement.style.borderColor = 'var(--color-accent)';
});
questionElement.addEventListener('mouseleave', () => {
questionElement.style.backgroundColor = 'var(--color-bg-secondary)';
questionElement.style.borderColor = 'var(--color-border)';
});
questionElement.addEventListener('click', () => {
const currentText = aiInput.value.trim();
const newText = currentText ? `${currentText}\n\n${question}` : question;
aiInput.value = newText;
aiInput.focus();
// Trigger input event to update character counter
aiInput.dispatchEvent(new Event('input', { bubbles: true }));
// Hide suggestions after adding
showPromptingStatus('hidden');
});
questionsList.appendChild(questionElement);
});
showPromptingStatus('suggestions');
}
async function triggerSmartPrompting() {
const inputText = aiInput.value.trim();
if (inputText.length < 50) {
showPromptingStatus('hidden');
return;
}
// Cancel any previous enhancement call
if (enhancementAbortController) {
enhancementAbortController.abort();
}
enhancementAbortController = new AbortController();
try {
showPromptingStatus('analyzing');
const response = await fetch('/api/ai/enhance-input', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
input: inputText,
taskId: `enhance_${Date.now()}`
}),
signal: enhancementAbortController.signal
});
const data = await response.json();
if (!response.ok) {
if (response.status === 429) {
showPromptingStatus('rate-limited');
} else {
showPromptingStatus('error');
}
return;
}
if (data.success && data.suggestions && data.suggestions.length > 0) {
displaySuggestions(data.suggestions);
} else {
showPromptingStatus('hidden');
}
} catch (error) {
if (error.name === 'AbortError') {
return;
}
console.warn('Smart prompting failed:', error);
showPromptingStatus('error');
// Auto-hide error after 3 seconds
setTimeout(() => {
if (promptingStatus && promptingStatus.textContent.includes('nicht verfügbar')) {
showPromptingStatus('hidden');
}
}, 3000);
}
}
function resetSmartPrompting() {
clearTimeout(enhancementTimeout);
if (enhancementAbortController) {
enhancementAbortController.abort();
}
showPromptingStatus('hidden');
}
toggleSwitch.addEventListener('click', () => { toggleSwitch.addEventListener('click', () => {
switchToMode(currentMode === 'workflow' ? 'tool' : 'workflow'); switchToMode(currentMode === 'workflow' ? 'tool' : 'workflow');
}); });
@ -267,6 +460,53 @@ document.addEventListener('DOMContentLoaded', () => {
aiInput.addEventListener('input', updateCharacterCount); aiInput.addEventListener('input', updateCharacterCount);
updateCharacterCount(); updateCharacterCount();
// Smart Prompting Input Handling
aiInput.addEventListener('input', () => {
const inputLength = aiInput.value.trim().length;
// Clear existing timeout
clearTimeout(enhancementTimeout);
// Cancel any pending enhancement call
if (enhancementAbortController) {
enhancementAbortController.abort();
}
// Hide suggestions if input is too short
if (inputLength < 50) {
showPromptingStatus('hidden');
return;
}
// Hide suggestions if they're currently visible (user is still typing)
if (suggestionsVisible) {
showPromptingStatus('hidden');
}
// Show analyzing state after 1 second of typing (but only if long enough)
setTimeout(() => {
if (aiInput.value.trim().length >= 50) {
showPromptingStatus('analyzing');
}
}, 1000);
// Trigger AI enhancement after 4 seconds of no input
enhancementTimeout = setTimeout(() => {
if (aiInput.value.trim().length >= 50) {
triggerSmartPrompting();
}
}, 4000);
});
// Dismiss suggestions handler
if (dismissSuggestions) {
dismissSuggestions.addEventListener('click', () => {
showPromptingStatus('hidden');
});
}
// ... [Keep all existing display functions - formatWorkflowSuggestion, renderBackgroundKnowledge, renderContextualAnalysis, displayWorkflowResults, displayToolResults] ...
function formatWorkflowSuggestion(text) { function formatWorkflowSuggestion(text) {
const numberedListPattern = /(\d+\.\s)/g; const numberedListPattern = /(\d+\.\s)/g;
@ -403,6 +643,7 @@ document.addEventListener('DOMContentLoaded', () => {
return html; return html;
} }
// Keep existing display functions...
function displayWorkflowResults(recommendation, originalQuery) { function displayWorkflowResults(recommendation, originalQuery) {
const toolsByPhase = {}; const toolsByPhase = {};
@ -765,6 +1006,9 @@ document.addEventListener('DOMContentLoaded', () => {
return; return;
} }
// Reset smart prompting when submitting
resetSmartPrompting();
const taskId = `ai_${Date.now()}_${Math.random().toString(36).substr(2, 6)}`; const taskId = `ai_${Date.now()}_${Math.random().toString(36).substr(2, 6)}`;
aiResults.style.display = 'none'; aiResults.style.display = 'none';

View File

@ -1923,3 +1923,99 @@ footer {
gap: 0.5rem; gap: 0.5rem;
} }
} }
/* Add to global.css - Smart Prompting Styles */
.smart-prompting-container {
margin-top: 1rem;
animation: slideDown 0.3s ease-out;
}
.prompting-status {
display: flex;
align-items: center;
gap: 0.5rem;
margin-bottom: 0.75rem;
font-weight: 500;
color: var(--color-text);
}
.prompting-spinner {
display: inline-block;
animation: spin 1s linear infinite;
}
.suggested-questions {
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.suggestion-item {
background-color: var(--color-bg-secondary);
border: 1px solid var(--color-border);
border-radius: 0.375rem;
padding: 0.75rem;
cursor: pointer;
transition: var(--transition-fast);
font-size: 0.875rem;
line-height: 1.4;
}
.suggestion-item:hover {
background-color: var(--color-bg-tertiary);
border-color: var(--color-accent);
}
.suggestion-item .suggestion-number {
color: var(--color-accent);
font-weight: bold;
flex-shrink: 0;
margin-right: 0.5rem;
}
.suggestion-item .suggestion-text {
flex: 1;
}
.suggestion-item .suggestion-icon {
flex-shrink: 0;
margin-left: 0.5rem;
color: var(--color-text-secondary);
}
/* Enhanced contextual analysis cards */
.contextual-analysis-card {
margin-bottom: 2rem;
border-left: 4px solid;
transition: var(--transition-fast);
}
.contextual-analysis-card:hover {
transform: translateY(-1px);
box-shadow: var(--shadow-md);
}
.contextual-analysis-card.scenario {
border-left-color: var(--color-primary);
}
.contextual-analysis-card.approach {
border-left-color: var(--color-accent);
}
.contextual-analysis-card.critical {
border-left-color: var(--color-warning);
}
.analysis-header {
display: flex;
align-items: center;
gap: 0.5rem;
margin-bottom: 1rem;
font-size: 1rem;
font-weight: 600;
}
.analysis-header.scenario { color: var(--color-primary); }
.analysis-header.approach { color: var(--color-accent); }
.analysis-header.critical { color: var(--color-warning); }