phase 2 + css
This commit is contained in:
parent
72d5f267f0
commit
867a651a55
@ -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';
|
||||||
|
@ -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); }
|
Loading…
x
Reference in New Issue
Block a user