Spaces:
Sleeping
Sleeping
| <template> | |
| <div v-if="isVisible" class="modal-overlay" @click="closeModal"> | |
| <div class="modal-container" @click.stop> | |
| <!-- Modal Header --> | |
| <div class="modal-header"> | |
| <h2 class="modal-title"> | |
| 🤖 Multi-Agent Communication | |
| </h2> | |
| <p class="modal-subtitle"> | |
| Jane Alesi Master Coordinator mit automatischer Spezialist-Delegation | |
| </p> | |
| <button @click="closeModal" class="close-button"> | |
| <svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24"> | |
| <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path> | |
| </svg> | |
| </button> | |
| </div> | |
| <div class="modal-body"> | |
| <!-- 🔧 KOMPAKTER: Capabilities Section --> | |
| <div class="capabilities-section"> | |
| <h3 class="section-title">Verfügbare Spezialisten</h3> | |
| <div class="specialists-grid"> | |
| <div | |
| v-for="specialist in availableSpecialists" | |
| :key="specialist.id" | |
| :class="['specialist-card', { | |
| 'active': specialist.id === selectedSpecialist, | |
| 'processing': specialist.id === currentlyProcessingAgent | |
| }]" | |
| > | |
| <div class="specialist-icon"> | |
| {{ getSpecialistIcon(specialist.specialization) }} | |
| <div v-if="specialist.id === currentlyProcessingAgent" class="processing-indicator"> | |
| <div class="spinner-small"></div> | |
| </div> | |
| </div> | |
| <div class="specialist-info"> | |
| <div class="specialist-name">{{ specialist.name }}</div> | |
| <div class="specialist-role">{{ specialist.specialization }}</div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- 🚀 ENHANCED: Chat Interface with guaranteed scroll --> | |
| <div class="chat-section"> | |
| <!-- Live Delegation Status --> | |
| <div v-if="delegationStatus" class="delegation-status"> | |
| <div class="delegation-icon">⚡</div> | |
| <div class="delegation-text">{{ delegationStatus }}</div> | |
| <div class="delegation-progress"> | |
| <div class="progress-bar" :style="{ width: delegationProgress + '%' }"></div> | |
| </div> | |
| </div> | |
| <div class="chat-messages-wrapper"> | |
| <div class="chat-messages" ref="messagesContainer"> | |
| <div | |
| v-for="message in chatMessages" | |
| :key="message.id" | |
| :class="['message', message.type, { 'highlighted': message.isHighlighted }]" | |
| > | |
| <div class="message-header"> | |
| <div class="message-agent"> | |
| <span class="agent-icon">{{ getAgentIcon(message.agent) }}</span> | |
| <span class="agent-name">{{ message.agentName }}</span> | |
| <span v-if="message.role" class="agent-role">({{ message.role }})</span> | |
| <span v-if="message.provider" class="provider-badge" :class="message.provider"> | |
| {{ message.provider === 'colossus' ? '🏠 Local' : '⚡ Cloud' }} | |
| </span> | |
| </div> | |
| <div class="message-time">{{ formatTime(message.timestamp) }}</div> | |
| </div> | |
| <div class="message-content"> | |
| {{ message.content }} | |
| </div> | |
| <div v-if="message.processingTime || message.cost" class="message-meta"> | |
| <span v-if="message.processingTime" class="processing-time"> | |
| ⏱️ {{ message.processingTime }}s | |
| </span> | |
| <span v-if="message.cost" class="cost-info"> | |
| 💰 ${{ message.cost.toFixed(6) }} | |
| </span> | |
| <span v-if="message.provider" class="provider-info"> | |
| via {{ message.provider }} | |
| </span> | |
| </div> | |
| </div> | |
| <!-- Enhanced Coordination Chain Visualization --> | |
| <div v-if="currentCoordinationChain.length > 0" class="coordination-chain enhanced"> | |
| <h4 class="chain-title">🔗 Active Coordination Chain</h4> | |
| <div class="chain-flow"> | |
| <div | |
| v-for="(agent, index) in currentCoordinationChain" | |
| :key="index" | |
| :class="['chain-agent', { | |
| 'active': index === currentChainStep, | |
| 'completed': index < currentChainStep, | |
| 'pending': index > currentChainStep | |
| }]" | |
| > | |
| <span class="chain-icon">{{ getAgentIcon(agent) }}</span> | |
| <span class="chain-name">{{ getAgentName(agent) }}</span> | |
| <div v-if="index === currentChainStep" class="chain-progress"> | |
| <div class="progress-dots"> | |
| <span></span><span></span><span></span> | |
| </div> | |
| </div> | |
| <svg v-if="index < currentCoordinationChain.length - 1" class="chain-arrow" fill="currentColor" viewBox="0 0 20 20"> | |
| <path fill-rule="evenodd" d="M10.293 15.707a1 1 0 010-1.414L14.586 10l-4.293-4.293a1 1 0 111.414-1.414l5 5a1 1 0 010 1.414l-5 5a1 1 0 01-1.414 0z" clip-rule="evenodd"></path> | |
| </svg> | |
| </div> | |
| </div> | |
| <div class="chain-status"> | |
| {{ getChainStatusText() }} | |
| </div> | |
| </div> | |
| <!-- Enhanced Processing State --> | |
| <div v-if="isProcessing" class="message processing enhanced"> | |
| <div class="message-header"> | |
| <div class="message-agent"> | |
| <span class="agent-icon loading">🤖</span> | |
| <span class="agent-name">{{ currentProcessor }}</span> | |
| <span class="agent-role">({{ processingStatus }})</span> | |
| </div> | |
| <div class="processing-timer">{{ processingTime }}s</div> | |
| </div> | |
| <div class="message-content"> | |
| <div class="enhanced-typing-indicator"> | |
| <div class="typing-text">{{ getProcessingMessage() }}</div> | |
| <div class="typing-dots"> | |
| <span></span><span></span><span></span> | |
| </div> | |
| </div> | |
| <!-- Provider switch notification --> | |
| <div v-if="providerSwitchMessage" class="provider-switch-notification"> | |
| <div class="switch-icon">🔄</div> | |
| <div class="switch-text">{{ providerSwitchMessage }}</div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Auto-scroll trigger --> | |
| <div ref="scrollTrigger"></div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- 🚀 ENHANCED INPUT SECTION: Guaranteed visibility --> | |
| <div class="chat-input-section enhanced"> | |
| <div class="input-group"> | |
| <textarea | |
| v-model="currentMessage" | |
| @keydown.enter.exact.prevent="sendMessage" | |
| @keydown.enter.shift.exact="() => {}" | |
| placeholder="Stelle eine Frage oder gib eine Aufgabe ein..." | |
| class="message-input" | |
| rows="3" | |
| :disabled="isProcessing" | |
| ></textarea> | |
| <button | |
| @click="sendMessage" | |
| :disabled="!canSendMessage" | |
| class="send-button" | |
| > | |
| <svg v-if="!isProcessing" class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"> | |
| <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 19l9 2-9-18-9 18 9-2zm0 0v-8"></path> | |
| </svg> | |
| <div v-else class="spinner"></div> | |
| </button> | |
| </div> | |
| <div class="input-options"> | |
| <div class="task-priority"> | |
| <label for="priority">Priorität:</label> | |
| <select v-model="taskPriority" id="priority" class="priority-select"> | |
| <option value="normal">Normal</option> | |
| <option value="high">Hoch</option> | |
| <option value="urgent">Dringend</option> | |
| <option value="low">Niedrig</option> | |
| </select> | |
| </div> | |
| <div class="preferred-agent"> | |
| <label for="preferredAgent">Bevorzugter Agent:</label> | |
| <select v-model="preferredAgent" id="preferredAgent" class="agent-select"> | |
| <option value="">Automatische Auswahl</option> | |
| <option value="john_alesi">John Alesi (Development)</option> | |
| <option value="lara_alesi">Lara Alesi (Medical)</option> | |
| <option value="justus_alesi">Justus Alesi (Legal)</option> | |
| <option value="theo_alesi">Theo Alesi (Finance)</option> | |
| <option value="leon_alesi">Leon Alesi (System)</option> | |
| <option value="luna_alesi">Luna Alesi (Coaching)</option> | |
| </select> | |
| </div> | |
| <!-- Provider selection --> | |
| <div class="provider-selection"> | |
| <label>Provider:</label> | |
| <div class="provider-buttons"> | |
| <button | |
| @click="preferredProvider = 'auto'" | |
| :class="['provider-btn', { active: preferredProvider === 'auto' }]" | |
| > | |
| 🤖 Auto | |
| </button> | |
| <button | |
| @click="preferredProvider = 'colossus'" | |
| :class="['provider-btn', { active: preferredProvider === 'colossus' }]" | |
| > | |
| 🏠 Local (Free) | |
| </button> | |
| <button | |
| @click="preferredProvider = 'openrouter'" | |
| :class="['provider-btn', { active: preferredProvider === 'openrouter' }]" | |
| > | |
| ⚡ Cloud (Fast) | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- 🔧 ENHANCED: Modal Footer with better stats --> | |
| <div class="modal-footer enhanced"> | |
| <div class="footer-stats"> | |
| <span class="stat-item"> | |
| <span class="stat-icon">💬</span> | |
| <span class="stat-value">{{ chatMessages.length }}</span> | |
| <span class="stat-label">Messages</span> | |
| </span> | |
| <span class="stat-item"> | |
| <span class="stat-icon">🤖</span> | |
| <span class="stat-value">{{ uniqueAgentsCount }}</span> | |
| <span class="stat-label">Agents</span> | |
| </span> | |
| <span class="stat-item"> | |
| <span class="stat-icon">⏱️</span> | |
| <span class="stat-value">{{ averageResponseTime }}s</span> | |
| <span class="stat-label">Avg Response</span> | |
| </span> | |
| <span class="stat-item"> | |
| <span class="stat-icon">💰</span> | |
| <span class="stat-value">${{ totalCost.toFixed(4) }}</span> | |
| <span class="stat-label">Total Cost</span> | |
| </span> | |
| </div> | |
| <div class="footer-actions"> | |
| <button @click="clearChat" class="clear-button"> | |
| 🗑️ Clear Chat | |
| </button> | |
| <button @click="closeModal" class="close-modal-button"> | |
| Schließen | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </template> | |
| <script> | |
| import { ref, computed, nextTick, onMounted, onUnmounted, watch } from 'vue' | |
| import { saapApi } from '../../services/saapApi' | |
| export default { | |
| name: 'MultiAgentChatModalEnhanced', | |
| props: { | |
| isVisible: { | |
| type: Boolean, | |
| default: false | |
| } | |
| }, | |
| emits: ['close'], | |
| setup(props, { emit }) { | |
| // Reactive state | |
| const chatMessages = ref([]) | |
| const currentMessage = ref('') | |
| const isProcessing = ref(false) | |
| const currentProcessor = ref('Jane Alesi') | |
| const processingStatus = ref('Ready') | |
| const processingTime = ref(0) | |
| const selectedSpecialist = ref(null) | |
| const currentlyProcessingAgent = ref(null) | |
| const currentCoordinationChain = ref([]) | |
| const currentChainStep = ref(0) | |
| const taskPriority = ref('normal') | |
| const preferredAgent = ref('') | |
| const preferredProvider = ref('auto') | |
| const messagesContainer = ref(null) | |
| const scrollTrigger = ref(null) | |
| // Enhanced state for delegation tracking | |
| const delegationStatus = ref('') | |
| const delegationProgress = ref(0) | |
| const providerSwitchMessage = ref('') | |
| // Processing timer | |
| let processingTimer = null | |
| // Available specialists data | |
| const availableSpecialists = ref([ | |
| { | |
| id: 'john_alesi', | |
| name: 'John Alesi', | |
| specialization: 'Development', | |
| description: 'Software-Entwicklung und AGI-Architekturen' | |
| }, | |
| { | |
| id: 'lara_alesi', | |
| name: 'Lara Alesi', | |
| specialization: 'Medical', | |
| description: 'Medizinische Expertise und Gesundheitswesen' | |
| }, | |
| { | |
| id: 'justus_alesi', | |
| name: 'Justus Alesi', | |
| specialization: 'Legal', | |
| description: 'Rechtsberatung für DE/CH/EU' | |
| }, | |
| { | |
| id: 'theo_alesi', | |
| name: 'Theo Alesi', | |
| specialization: 'Finance', | |
| description: 'Finanz- und Investitionsanalyse' | |
| }, | |
| { | |
| id: 'leon_alesi', | |
| name: 'Leon Alesi', | |
| specialization: 'System', | |
| description: 'IT-Systemintegration und Infrastructure' | |
| }, | |
| { | |
| id: 'luna_alesi', | |
| name: 'Luna Alesi', | |
| specialization: 'Coaching', | |
| description: 'Coaching und Organisationsentwicklung' | |
| } | |
| ]) | |
| // Computed properties | |
| const canSendMessage = computed(() => { | |
| return currentMessage.value.trim().length > 0 && !isProcessing.value | |
| }) | |
| const uniqueAgentsCount = computed(() => { | |
| const agents = new Set(chatMessages.value.map(msg => msg.agent)) | |
| return agents.size | |
| }) | |
| const averageResponseTime = computed(() => { | |
| const responseTimes = chatMessages.value | |
| .filter(msg => msg.processingTime) | |
| .map(msg => msg.processingTime) | |
| if (responseTimes.length === 0) return 0 | |
| return (responseTimes.reduce((sum, time) => sum + time, 0) / responseTimes.length).toFixed(1) | |
| }) | |
| const totalCost = computed(() => { | |
| return chatMessages.value | |
| .filter(msg => msg.cost) | |
| .reduce((sum, msg) => sum + msg.cost, 0) | |
| }) | |
| // Helper functions | |
| const getSpecialistIcon = (specialization) => { | |
| const icons = { | |
| 'Development': '💻', | |
| 'Medical': '⚕️', | |
| 'Legal': '⚖️', | |
| 'Finance': '💰', | |
| 'System': '🔧', | |
| 'Coaching': '🎯' | |
| } | |
| return icons[specialization] || '🤖' | |
| } | |
| const getAgentIcon = (agentId) => { | |
| const icons = { | |
| 'jane_alesi': '👩💼', | |
| 'john_alesi': '💻', | |
| 'lara_alesi': '⚕️', | |
| 'justus_alesi': '⚖️', | |
| 'theo_alesi': '💰', | |
| 'leon_alesi': '🔧', | |
| 'luna_alesi': '🎯', | |
| 'user': '👤' | |
| } | |
| return icons[agentId] || '🤖' | |
| } | |
| const getAgentName = (agentId) => { | |
| const names = { | |
| 'jane_alesi': 'Jane Alesi', | |
| 'john_alesi': 'John Alesi', | |
| 'lara_alesi': 'Lara Alesi', | |
| 'justus_alesi': 'Justus Alesi', | |
| 'theo_alesi': 'Theo Alesi', | |
| 'leon_alesi': 'Leon Alesi', | |
| 'luna_alesi': 'Luna Alesi' | |
| } | |
| return names[agentId] || agentId | |
| } | |
| const formatTime = (timestamp) => { | |
| return new Date(timestamp).toLocaleTimeString('de-DE', { | |
| hour: '2-digit', | |
| minute: '2-digit', | |
| second: '2-digit' | |
| }) | |
| } | |
| const getProcessingMessage = () => { | |
| const messages = [ | |
| 'Jane analysiert deine Anfrage...', | |
| 'Wählt den besten Spezialisten aus...', | |
| 'Delegiert an Fachexperten...', | |
| 'Verarbeitet Antworten...', | |
| 'Koordiniert finale Response...' | |
| ] | |
| return messages[Math.min(Math.floor(processingTime.value / 5), messages.length - 1)] | |
| } | |
| const getChainStatusText = () => { | |
| if (currentChainStep.value === 0) return 'Jane analysiert Intent...' | |
| if (currentChainStep.value === 1) return 'Delegiert an Specialist...' | |
| if (currentChainStep.value === 2) return 'Koordiniert finale Antwort...' | |
| return 'Workflow abgeschlossen' | |
| } | |
| // Enhanced scrolling with intersection observer | |
| const scrollToBottom = (force = false) => { | |
| nextTick(() => { | |
| if (messagesContainer.value) { | |
| const container = messagesContainer.value | |
| const isNearBottom = container.scrollTop + container.clientHeight >= container.scrollHeight - 50 | |
| if (force || isNearBottom) { | |
| if (scrollTrigger.value) { | |
| scrollTrigger.value.scrollIntoView({ behavior: 'smooth' }) | |
| } | |
| } | |
| } | |
| }) | |
| } | |
| // Message handling | |
| const addMessage = (content, agent, agentName, type = 'agent', meta = {}) => { | |
| const message = { | |
| id: Date.now() + Math.random(), | |
| content, | |
| agent, | |
| agentName, | |
| type, | |
| timestamp: new Date(), | |
| role: meta.role, | |
| processingTime: meta.processingTime, | |
| cost: meta.cost, | |
| provider: meta.provider, | |
| isHighlighted: meta.highlight || false | |
| } | |
| chatMessages.value.push(message) | |
| scrollToBottom(true) | |
| return message | |
| } | |
| // Enhanced WebSocket delegation tracking | |
| const updateDelegationStatus = (status, progress = 0) => { | |
| delegationStatus.value = status | |
| delegationProgress.value = progress | |
| if (progress >= 100) { | |
| setTimeout(() => { | |
| delegationStatus.value = '' | |
| delegationProgress.value = 0 | |
| }, 2000) | |
| } | |
| } | |
| const startProcessingTimer = () => { | |
| processingTime.value = 0 | |
| processingTimer = setInterval(() => { | |
| processingTime.value += 1 | |
| // Provider switch logic | |
| if (processingTime.value === 15 && preferredProvider.value === 'auto') { | |
| providerSwitchMessage.value = 'Colossus timeout → switching to OpenRouter...' | |
| setTimeout(() => providerSwitchMessage.value = '', 3000) | |
| } | |
| }, 1000) | |
| } | |
| const stopProcessingTimer = () => { | |
| if (processingTimer) { | |
| clearInterval(processingTimer) | |
| processingTimer = null | |
| } | |
| processingTime.value = 0 | |
| providerSwitchMessage.value = '' | |
| } | |
| const sendMessage = async () => { | |
| if (!canSendMessage.value) return | |
| const message = currentMessage.value.trim() | |
| currentMessage.value = '' | |
| // Add user message | |
| addMessage(message, 'user', 'Du', 'user') | |
| // Start enhanced processing | |
| isProcessing.value = true | |
| currentProcessor.value = 'Jane Alesi' | |
| processingStatus.value = 'Analysiert Intent...' | |
| currentCoordinationChain.value = ['jane_alesi'] | |
| currentChainStep.value = 0 | |
| updateDelegationStatus('Jane Alesi analysiert Intent...', 20) | |
| startProcessingTimer() | |
| try { | |
| // Simulated delegation steps | |
| setTimeout(() => { | |
| updateDelegationStatus('Wählt besten Spezialisten...', 40) | |
| currentChainStep.value = 1 | |
| }, 2000) | |
| setTimeout(() => { | |
| updateDelegationStatus('Delegiert Aufgabe...', 60) | |
| currentChainStep.value = 2 | |
| }, 4000) | |
| // Call multi-agent API | |
| const response = await saapApi.multiAgentChat({ | |
| user_message: message, | |
| user_context: { | |
| timestamp: new Date().toISOString(), | |
| priority: taskPriority.value, | |
| preferred_provider: preferredProvider.value | |
| }, | |
| preferred_agent: preferredAgent.value || null, | |
| task_priority: taskPriority.value | |
| }) | |
| updateDelegationStatus('Koordiniert finale Antwort...', 80) | |
| // Update coordination chain | |
| currentCoordinationChain.value = response.coordination_chain || ['jane_alesi'] | |
| // Add coordinator response | |
| addMessage( | |
| response.coordinator_response, | |
| 'jane_alesi', | |
| 'Jane Alesi', | |
| 'coordinator', | |
| { | |
| role: 'Master Coordinator', | |
| processingTime: response.processing_time, | |
| cost: response.cost_info?.total_cost, | |
| provider: response.provider || 'auto', | |
| highlight: true | |
| } | |
| ) | |
| // Add specialist response if available | |
| if (response.delegated_agent && response.specialist_response) { | |
| const specialistName = getAgentName(response.delegated_agent) | |
| const specialist = availableSpecialists.value.find(s => s.id === response.delegated_agent) | |
| selectedSpecialist.value = response.delegated_agent | |
| currentlyProcessingAgent.value = response.delegated_agent | |
| setTimeout(() => { | |
| addMessage( | |
| response.specialist_response, | |
| response.delegated_agent, | |
| specialistName, | |
| 'specialist', | |
| { | |
| role: specialist?.specialization || 'Specialist', | |
| processingTime: response.processing_time, | |
| provider: response.provider || 'auto', | |
| highlight: true | |
| } | |
| ) | |
| selectedSpecialist.value = null | |
| currentlyProcessingAgent.value = null | |
| updateDelegationStatus('Workflow abgeschlossen!', 100) | |
| }, 1000) | |
| } else { | |
| updateDelegationStatus('Workflow abgeschlossen!', 100) | |
| } | |
| } catch (error) { | |
| console.error('Multi-Agent Chat Error:', error) | |
| addMessage( | |
| 'Entschuldigung, es ist ein Fehler bei der Multi-Agent-Kommunikation aufgetreten. Bitte versuche es erneut.', | |
| 'jane_alesi', | |
| 'Jane Alesi', | |
| 'error', | |
| { role: 'Error Handler', highlight: true } | |
| ) | |
| updateDelegationStatus('Fehler aufgetreten', 0) | |
| } finally { | |
| isProcessing.value = false | |
| currentProcessor.value = 'Jane Alesi' | |
| processingStatus.value = 'Ready' | |
| currentChainStep.value = 0 | |
| stopProcessingTimer() | |
| } | |
| } | |
| const clearChat = () => { | |
| chatMessages.value = [] | |
| currentCoordinationChain.value = [] | |
| selectedSpecialist.value = null | |
| currentlyProcessingAgent.value = null | |
| delegationStatus.value = '' | |
| delegationProgress.value = 0 | |
| // Add welcome message | |
| addMessage( | |
| 'Hallo! Ich bin Jane Alesi, deine Master Coordinatorin. Ich kann Aufgaben an spezialisierte Agenten delegieren oder sie selbst bearbeiten. Wie kann ich dir helfen?', | |
| 'jane_alesi', | |
| 'Jane Alesi', | |
| 'coordinator', | |
| { role: 'Master Coordinator' } | |
| ) | |
| } | |
| const closeModal = () => { | |
| emit('close') | |
| } | |
| // Watch for modal visibility changes | |
| watch(() => props.isVisible, (newVal) => { | |
| if (newVal && chatMessages.value.length === 0) { | |
| clearChat() | |
| } | |
| }) | |
| // Lifecycle | |
| onMounted(() => { | |
| if (chatMessages.value.length === 0) { | |
| clearChat() | |
| } | |
| }) | |
| onUnmounted(() => { | |
| stopProcessingTimer() | |
| }) | |
| return { | |
| // State | |
| chatMessages, | |
| currentMessage, | |
| isProcessing, | |
| currentProcessor, | |
| processingStatus, | |
| processingTime, | |
| selectedSpecialist, | |
| currentlyProcessingAgent, | |
| currentCoordinationChain, | |
| currentChainStep, | |
| taskPriority, | |
| preferredAgent, | |
| preferredProvider, | |
| messagesContainer, | |
| scrollTrigger, | |
| availableSpecialists, | |
| delegationStatus, | |
| delegationProgress, | |
| providerSwitchMessage, | |
| // Computed | |
| canSendMessage, | |
| uniqueAgentsCount, | |
| averageResponseTime, | |
| totalCost, | |
| // Methods | |
| getSpecialistIcon, | |
| getAgentIcon, | |
| getAgentName, | |
| formatTime, | |
| getProcessingMessage, | |
| getChainStatusText, | |
| sendMessage, | |
| clearChat, | |
| closeModal | |
| } | |
| } | |
| } | |
| </script> | |
| <style scoped> | |
| /* Modal Overlay */ | |
| .modal-overlay { | |
| position: fixed; | |
| top: 0; | |
| left: 0; | |
| right: 0; | |
| bottom: 0; | |
| background-color: rgba(0, 0, 0, 0.75); | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| z-index: 1000; | |
| backdrop-filter: blur(4px); | |
| } | |
| /* 🚀 ENHANCED: Modal Container - MAXIMUM VISIBILITY */ | |
| .modal-container { | |
| background: white; | |
| border-radius: 16px; | |
| width: 96vw; | |
| max-width: 1400px; | |
| height: 96vh; | |
| max-height: 98vh; | |
| display: flex; | |
| flex-direction: column; | |
| box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25); | |
| overflow: hidden; | |
| } | |
| /* Modal Header */ | |
| .modal-header { | |
| background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); | |
| color: white; | |
| padding: 16px 20px; | |
| position: relative; | |
| flex-shrink: 0; | |
| } | |
| .modal-title { | |
| margin: 0; | |
| font-size: 22px; | |
| font-weight: 700; | |
| } | |
| .modal-subtitle { | |
| margin: 6px 0 0 0; | |
| font-size: 13px; | |
| opacity: 0.9; | |
| } | |
| .close-button { | |
| position: absolute; | |
| top: 16px; | |
| right: 20px; | |
| background: rgba(255, 255, 255, 0.2); | |
| border: none; | |
| border-radius: 8px; | |
| padding: 8px; | |
| color: white; | |
| cursor: pointer; | |
| transition: background-color 0.2s; | |
| } | |
| .close-button:hover { | |
| background: rgba(255, 255, 255, 0.3); | |
| } | |
| /* Modal Body */ | |
| .modal-body { | |
| flex: 1; | |
| display: flex; | |
| flex-direction: column; | |
| overflow: hidden; | |
| min-height: 0; | |
| } | |
| /* 🔧 OPTIMIZED: Capabilities Section */ | |
| .capabilities-section { | |
| padding: 6px 16px; | |
| border-bottom: 1px solid #e5e7eb; | |
| background: #f8fafc; | |
| flex-shrink: 0; | |
| } | |
| .section-title { | |
| margin: 0 0 6px 0; | |
| font-size: 13px; | |
| font-weight: 600; | |
| color: #374151; | |
| } | |
| .specialists-grid { | |
| display: grid; | |
| grid-template-columns: repeat(auto-fit, minmax(110px, 1fr)); | |
| gap: 3px; | |
| } | |
| .specialist-card { | |
| display: flex; | |
| align-items: center; | |
| padding: 4px 6px; | |
| border: 2px solid #e5e7eb; | |
| border-radius: 6px; | |
| background: white; | |
| transition: all 0.2s; | |
| cursor: pointer; | |
| position: relative; | |
| } | |
| .specialist-card:hover { | |
| border-color: #9ca3af; | |
| transform: translateY(-1px); | |
| } | |
| .specialist-card.active { | |
| border-color: #3b82f6; | |
| background: #eff6ff; | |
| box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1); | |
| } | |
| .specialist-card.processing { | |
| border-color: #f59e0b; | |
| background: #fffbeb; | |
| animation: processing-pulse 2s infinite; | |
| } | |
| @keyframes processing-pulse { | |
| 0%, 100% { transform: scale(1); } | |
| 50% { transform: scale(1.02); } | |
| } | |
| .specialist-icon { | |
| font-size: 16px; | |
| margin-right: 5px; | |
| position: relative; | |
| } | |
| .processing-indicator { | |
| position: absolute; | |
| top: -2px; | |
| right: -2px; | |
| width: 8px; | |
| height: 8px; | |
| } | |
| .spinner-small { | |
| width: 8px; | |
| height: 8px; | |
| border: 1px solid #f59e0b; | |
| border-radius: 50%; | |
| border-top-color: transparent; | |
| animation: spin 0.8s linear infinite; | |
| } | |
| .specialist-info { | |
| flex: 1; | |
| } | |
| .specialist-name { | |
| font-size: 10px; | |
| font-weight: 600; | |
| color: #374151; | |
| } | |
| .specialist-role { | |
| font-size: 9px; | |
| color: #6b7280; | |
| } | |
| /* 🚀 ENHANCED: Chat Section with GUARANTEED SCROLL */ | |
| .chat-section { | |
| flex: 1; | |
| display: flex; | |
| flex-direction: column; | |
| overflow: hidden; | |
| min-height: 0; | |
| } | |
| /* Live Delegation Status */ | |
| .delegation-status { | |
| background: linear-gradient(90deg, #3b82f6, #1d4ed8); | |
| color: white; | |
| padding: 8px 16px; | |
| display: flex; | |
| align-items: center; | |
| gap: 10px; | |
| font-size: 13px; | |
| flex-shrink: 0; | |
| } | |
| .delegation-icon { | |
| font-size: 16px; | |
| animation: bounce 1s infinite; | |
| } | |
| @keyframes bounce { | |
| 0%, 100% { transform: translateY(0); } | |
| 50% { transform: translateY(-2px); } | |
| } | |
| .delegation-text { | |
| flex: 1; | |
| font-weight: 500; | |
| } | |
| .delegation-progress { | |
| width: 100px; | |
| height: 4px; | |
| background: rgba(255, 255, 255, 0.3); | |
| border-radius: 2px; | |
| overflow: hidden; | |
| } | |
| .progress-bar { | |
| height: 100%; | |
| background: rgba(255, 255, 255, 0.9); | |
| transition: width 0.5s ease; | |
| border-radius: 2px; | |
| } | |
| /* 🚀 CRITICAL: Chat Messages Wrapper - GUARANTEED SCROLLABLE */ | |
| .chat-messages-wrapper { | |
| flex: 1; | |
| overflow: hidden; | |
| display: flex; | |
| flex-direction: column; | |
| min-height: 400px; /* Minimum guaranteed height */ | |
| } | |
| .chat-messages { | |
| flex: 1; | |
| overflow-y: auto; | |
| padding: 16px 20px; | |
| background: #fafafa; | |
| scroll-behavior: smooth; | |
| /* Ensure scrolling always works */ | |
| height: 100%; | |
| max-height: none ; | |
| } | |
| /* Message Styles */ | |
| .message { | |
| margin-bottom: 16px; | |
| padding: 12px 16px; | |
| border-radius: 12px; | |
| background: white; | |
| border: 1px solid #e5e7eb; | |
| transition: all 0.3s ease; | |
| } | |
| .message.highlighted { | |
| transform: scale(1.01); | |
| box-shadow: 0 8px 16px -4px rgba(0, 0, 0, 0.1); | |
| border-color: #3b82f6; | |
| } | |
| .message.user { | |
| background: #eff6ff; | |
| border-color: #3b82f6; | |
| margin-left: 60px; | |
| } | |
| .message.coordinator { | |
| background: #f3e8ff; | |
| border-color: #8b5cf6; | |
| } | |
| .message.specialist { | |
| background: #ecfdf5; | |
| border-color: #10b981; | |
| } | |
| .message.error { | |
| background: #fef2f2; | |
| border-color: #ef4444; | |
| } | |
| .message.processing.enhanced { | |
| background: linear-gradient(45deg, #fffbeb, #fef3c7); | |
| border-color: #f59e0b; | |
| animation: processing-glow 2s infinite; | |
| } | |
| @keyframes processing-glow { | |
| 0%, 100% { box-shadow: 0 0 8px rgba(245, 158, 11, 0.3); } | |
| 50% { box-shadow: 0 0 16px rgba(245, 158, 11, 0.5); } | |
| } | |
| .message-header { | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| margin-bottom: 8px; | |
| } | |
| .message-agent { | |
| display: flex; | |
| align-items: center; | |
| font-size: 14px; | |
| font-weight: 600; | |
| gap: 4px; | |
| } | |
| .agent-icon { | |
| font-size: 16px; | |
| } | |
| .agent-icon.loading { | |
| animation: pulse 1.5s infinite; | |
| } | |
| .agent-name { | |
| color: #374151; | |
| } | |
| .agent-role { | |
| color: #6b7280; | |
| font-weight: 400; | |
| } | |
| .provider-badge { | |
| padding: 2px 6px; | |
| border-radius: 4px; | |
| font-size: 10px; | |
| font-weight: 500; | |
| } | |
| .provider-badge.colossus { | |
| background: #dcfce7; | |
| color: #166534; | |
| } | |
| .provider-badge.openrouter { | |
| background: #dbeafe; | |
| color: #1e40af; | |
| } | |
| .message-time { | |
| font-size: 12px; | |
| color: #9ca3af; | |
| } | |
| .processing-timer { | |
| font-size: 12px; | |
| color: #f59e0b; | |
| font-weight: 600; | |
| } | |
| .message-content { | |
| color: #374151; | |
| line-height: 1.5; | |
| white-space: pre-wrap; | |
| font-size: 14px; | |
| } | |
| .message-meta { | |
| display: flex; | |
| gap: 12px; | |
| margin-top: 8px; | |
| font-size: 12px; | |
| color: #6b7280; | |
| flex-wrap: wrap; | |
| } | |
| .processing-time, .cost-info, .provider-info { | |
| display: flex; | |
| align-items: center; | |
| gap: 4px; | |
| } | |
| /* Enhanced Coordination Chain */ | |
| .coordination-chain.enhanced { | |
| background: linear-gradient(135deg, #f0f9ff, #e0f2fe); | |
| border: 2px solid #0ea5e9; | |
| border-radius: 12px; | |
| padding: 16px; | |
| margin: 16px 0; | |
| animation: chain-appear 0.5s ease; | |
| } | |
| @keyframes chain-appear { | |
| from { opacity: 0; transform: translateY(10px); } | |
| to { opacity: 1; transform: translateY(0); } | |
| } | |
| .chain-title { | |
| margin: 0 0 12px 0; | |
| font-size: 14px; | |
| font-weight: 700; | |
| color: #0c4a6e; | |
| display: flex; | |
| align-items: center; | |
| gap: 6px; | |
| } | |
| .chain-flow { | |
| display: flex; | |
| align-items: center; | |
| flex-wrap: wrap; | |
| gap: 8px; | |
| margin-bottom: 8px; | |
| } | |
| .chain-agent { | |
| display: flex; | |
| align-items: center; | |
| gap: 6px; | |
| padding: 6px 12px; | |
| background: white; | |
| border-radius: 8px; | |
| font-size: 12px; | |
| font-weight: 500; | |
| border: 2px solid #e5e7eb; | |
| transition: all 0.3s ease; | |
| position: relative; | |
| } | |
| .chain-agent.active { | |
| border-color: #3b82f6; | |
| background: #eff6ff; | |
| color: #1e40af; | |
| transform: scale(1.05); | |
| } | |
| .chain-agent.completed { | |
| border-color: #10b981; | |
| background: #ecfdf5; | |
| color: #065f46; | |
| } | |
| .chain-agent.pending { | |
| opacity: 0.6; | |
| } | |
| .chain-progress { | |
| position: absolute; | |
| bottom: -2px; | |
| left: 0; | |
| right: 0; | |
| height: 2px; | |
| background: #3b82f6; | |
| animation: progress-flow 1.5s infinite; | |
| } | |
| @keyframes progress-flow { | |
| 0% { transform: translateX(-100%); } | |
| 100% { transform: translateX(100%); } | |
| } | |
| .progress-dots { | |
| display: flex; | |
| gap: 2px; | |
| margin-left: 4px; | |
| } | |
| .progress-dots span { | |
| width: 3px; | |
| height: 3px; | |
| background: #3b82f6; | |
| border-radius: 50%; | |
| animation: dot-bounce 1s infinite; | |
| } | |
| .progress-dots span:nth-child(2) { animation-delay: 0.1s; } | |
| .progress-dots span:nth-child(3) { animation-delay: 0.2s; } | |
| @keyframes dot-bounce { | |
| 0%, 80%, 100% { transform: scale(0); } | |
| 40% { transform: scale(1); } | |
| } | |
| .chain-status { | |
| font-size: 12px; | |
| color: #0c4a6e; | |
| font-weight: 500; | |
| text-align: center; | |
| margin-top: 8px; | |
| font-style: italic; | |
| } | |
| .chain-icon { | |
| font-size: 14px; | |
| } | |
| .chain-arrow { | |
| width: 16px; | |
| height: 16px; | |
| color: #0ea5e9; | |
| } | |
| /* Enhanced Typing Indicator */ | |
| .enhanced-typing-indicator { | |
| display: flex; | |
| align-items: center; | |
| gap: 12px; | |
| } | |
| .typing-text { | |
| font-style: italic; | |
| color: #6b7280; | |
| } | |
| .typing-dots { | |
| display: flex; | |
| gap: 4px; | |
| } | |
| .typing-dots span { | |
| width: 8px; | |
| height: 8px; | |
| border-radius: 50%; | |
| background: #f59e0b; | |
| animation: typing 1.4s infinite ease-in-out; | |
| } | |
| .typing-dots span:nth-child(1) { animation-delay: -0.32s; } | |
| .typing-dots span:nth-child(2) { animation-delay: -0.16s; } | |
| @keyframes typing { | |
| 0%, 80%, 100% { | |
| transform: scale(0); | |
| opacity: 0.5; | |
| } | |
| 40% { | |
| transform: scale(1); | |
| opacity: 1; | |
| } | |
| } | |
| .provider-switch-notification { | |
| display: flex; | |
| align-items: center; | |
| gap: 8px; | |
| margin-top: 8px; | |
| padding: 6px 10px; | |
| background: #fef3c7; | |
| border-radius: 6px; | |
| font-size: 12px; | |
| color: #92400e; | |
| border: 1px solid #f59e0b; | |
| } | |
| .switch-icon { | |
| font-size: 14px; | |
| animation: spin 1s linear infinite; | |
| } | |
| @keyframes spin { | |
| to { transform: rotate(360deg); } | |
| } | |
| @keyframes pulse { | |
| 0%, 100% { transform: scale(1); } | |
| 50% { transform: scale(1.1); } | |
| } | |
| /* 🚀 ENHANCED: Input Section - ABSOLUTE VISIBILITY */ | |
| .chat-input-section.enhanced { | |
| background: white; | |
| border-top: 2px solid #e5e7eb; | |
| padding: 16px 20px; | |
| flex-shrink: 0 ; /* CRITICAL: Never allow shrinking */ | |
| position: relative; | |
| z-index: 50; /* Higher z-index to ensure visibility */ | |
| min-height: 120px ; /* GUARANTEED minimum space */ | |
| box-shadow: 0 -4px 12px rgba(0, 0, 0, 0.1); /* Visual separation */ | |
| } | |
| .input-group { | |
| display: flex; | |
| gap: 12px; | |
| align-items: flex-end; | |
| margin-bottom: 12px; | |
| } | |
| .message-input { | |
| flex: 1; | |
| padding: 12px 16px; | |
| border: 2px solid #e5e7eb; | |
| border-radius: 12px; | |
| font-size: 14px; | |
| line-height: 1.4; | |
| resize: vertical; | |
| min-height: 48px; | |
| max-height: 120px; | |
| transition: border-color 0.2s; | |
| background: white; | |
| } | |
| .message-input:focus { | |
| outline: none; | |
| border-color: #3b82f6; | |
| box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1); | |
| } | |
| .message-input:disabled { | |
| background: #f9fafb; | |
| color: #9ca3af; | |
| } | |
| .send-button { | |
| padding: 12px; | |
| background: #3b82f6; | |
| color: white; | |
| border: none; | |
| border-radius: 12px; | |
| cursor: pointer; | |
| transition: all 0.2s; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| width: 48px; | |
| height: 48px; | |
| flex-shrink: 0; | |
| } | |
| .send-button:hover:not(:disabled) { | |
| background: #2563eb; | |
| transform: translateY(-1px); | |
| } | |
| .send-button:disabled { | |
| background: #9ca3af; | |
| cursor: not-allowed; | |
| } | |
| .spinner { | |
| width: 20px; | |
| height: 20px; | |
| border: 2px solid rgba(255,255,255,0.3); | |
| border-radius: 50%; | |
| border-top-color: white; | |
| animation: spin 1s linear infinite; | |
| } | |
| /* Input Options */ | |
| .input-options { | |
| display: flex; | |
| gap: 16px; | |
| align-items: center; | |
| font-size: 12px; | |
| flex-wrap: wrap; | |
| } | |
| .task-priority, .preferred-agent, .provider-selection { | |
| display: flex; | |
| align-items: center; | |
| gap: 6px; | |
| } | |
| .priority-select, .agent-select { | |
| padding: 4px 8px; | |
| border: 1px solid #d1d5db; | |
| border-radius: 6px; | |
| font-size: 12px; | |
| background: white; | |
| min-width: 120px; | |
| } | |
| .provider-buttons { | |
| display: flex; | |
| gap: 4px; | |
| } | |
| .provider-btn { | |
| padding: 4px 8px; | |
| border: 1px solid #d1d5db; | |
| border-radius: 6px; | |
| background: white; | |
| cursor: pointer; | |
| transition: all 0.2s; | |
| font-size: 11px; | |
| } | |
| .provider-btn.active { | |
| background: #3b82f6; | |
| color: white; | |
| border-color: #3b82f6; | |
| } | |
| .provider-btn:hover:not(.active) { | |
| background: #f3f4f6; | |
| } | |
| /* Enhanced Modal Footer */ | |
| .modal-footer.enhanced { | |
| padding: 12px 20px; | |
| border-top: 2px solid #e5e7eb; | |
| background: #f8fafc; | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| flex-shrink: 0; | |
| } | |
| .footer-stats { | |
| display: flex; | |
| gap: 20px; | |
| flex-wrap: wrap; | |
| } | |
| .stat-item { | |
| display: flex; | |
| align-items: center; | |
| gap: 4px; | |
| font-size: 12px; | |
| color: #6b7280; | |
| } | |
| .stat-icon { | |
| font-size: 14px; | |
| } | |
| .stat-value { | |
| font-weight: 600; | |
| color: #374151; | |
| } | |
| .footer-actions { | |
| display: flex; | |
| gap: 12px; | |
| } | |
| .clear-button, .close-modal-button { | |
| padding: 6px 12px; | |
| border: 1px solid #d1d5db; | |
| border-radius: 8px; | |
| background: white; | |
| cursor: pointer; | |
| transition: all 0.2s; | |
| font-size: 13px; | |
| } | |
| .clear-button:hover { | |
| background: #f3f4f6; | |
| } | |
| .close-modal-button { | |
| background: #3b82f6; | |
| color: white; | |
| border-color: #3b82f6; | |
| } | |
| .close-modal-button:hover { | |
| background: #2563eb; | |
| } | |
| /* 🚀 ENHANCED: Responsive Design */ | |
| @media (max-width: 768px) { | |
| .modal-container { | |
| width: 98vw; | |
| height: 98vh; | |
| } | |
| .chat-messages-wrapper { | |
| min-height: 300px; | |
| } | |
| .chat-messages { | |
| padding: 12px 16px; | |
| } | |
| .specialists-grid { | |
| grid-template-columns: repeat(2, 1fr); | |
| } | |
| .input-options { | |
| flex-direction: column; | |
| align-items: flex-start; | |
| gap: 8px; | |
| } | |
| .footer-stats { | |
| flex-direction: column; | |
| gap: 8px; | |
| } | |
| .chat-input-section.enhanced { | |
| padding: 12px 16px; | |
| min-height: 100px ; | |
| } | |
| .chain-flow { | |
| flex-direction: column; | |
| align-items: stretch; | |
| } | |
| .chain-arrow { | |
| transform: rotate(90deg); | |
| margin: 4px 0; | |
| } | |
| } | |
| /* 🚀 ULTRA-WIDE: Maximum Screen Utilization */ | |
| @media (min-width: 1400px) { | |
| .modal-container { | |
| max-width: 1600px; | |
| } | |
| .chat-messages-wrapper { | |
| min-height: 600px; | |
| } | |
| .specialists-grid { | |
| grid-template-columns: repeat(6, 1fr); | |
| } | |
| } | |
| /* 🚀 CRITICAL: Prevent any scroll issues */ | |
| * { | |
| box-sizing: border-box; | |
| } | |
| .chat-messages::-webkit-scrollbar { | |
| width: 8px; | |
| } | |
| .chat-messages::-webkit-scrollbar-track { | |
| background: #f1f5f9; | |
| border-radius: 4px; | |
| } | |
| .chat-messages::-webkit-scrollbar-thumb { | |
| background: #cbd5e1; | |
| border-radius: 4px; | |
| } | |
| .chat-messages::-webkit-scrollbar-thumb:hover { | |
| background: #94a3b8; | |
| } | |
| </style> |