lattmamb/aether-rides
0
1"""2Frontend UI components for Vision OS3"""4 5import os6import sys7import logging8from typing import Dict, List, Any, Optional, Callable9from PyQt5.QtWidgets import (10 QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, 11 QPushButton, QLabel, QLineEdit, QTextEdit, QScrollArea, 12 QFrame, QSplitter, QTabWidget, QComboBox, QCheckBox, QFileDialog,13 QListWidget, QListWidgetItem, QProgressBar, QMenu, QAction, QSystemTrayIcon14)15from PyQt5.QtGui import (16 QIcon, QPixmap, QFont, QColor, QPalette, QLinearGradient, 17 QGradient, QBrush, QPainter, QPen, QRadialGradient, QFontMetrics18)19from PyQt5.QtCore import (20 Qt, QSize, QPoint, QRect, QPropertyAnimation, QEasingCurve, 21 QTimer, QThread, pyqtSignal, pyqtSlot, QUrl, QObject22)23 24logger = logging.getLogger("vision_os.frontend")25 26# Define color scheme based on the reference images27class ColorScheme:28 """Color scheme for Vision OS UI based on reference images"""29 30 # Background colors31 BACKGROUND_DARK = "#0B0B0F" # Deep black background32 BACKGROUND_PANEL = "#141821" # Slightly lighter panel background33 34 # Accent colors35 ACCENT_BLUE = "#00E0FF" # Neon blue36 ACCENT_CYAN = "#35F2DB" # Cyan accent37 ACCENT_PURPLE = "#9D4EDD" # Purple accent38 39 # Gradient colors40 GRADIENT_START = "#1E2432" # Dark blue for gradients41 GRADIENT_END = "#0F131C" # Darker blue for gradients42 43 # Text colors44 TEXT_PRIMARY = "#FFFFFF" # White text45 TEXT_SECONDARY = "#B0B7C3" # Light gray text46 47 # Status colors48 STATUS_SUCCESS = "#4CAF50" # Green for success49 STATUS_WARNING = "#FFC107" # Yellow for warnings50 STATUS_ERROR = "#F44336" # Red for errors51 52 @staticmethod53 def get_background_gradient():54 """Create a background gradient"""55 gradient = QLinearGradient(0, 0, 0, 1000)56 gradient.setColorAt(0, QColor(ColorScheme.GRADIENT_START))57 gradient.setColorAt(1, QColor(ColorScheme.GRADIENT_END))58 return gradient59 60 @staticmethod61 def get_accent_gradient():62 """Create an accent gradient"""63 gradient = QLinearGradient(0, 0, 400, 0)64 gradient.setColorAt(0, QColor(ColorScheme.ACCENT_BLUE))65 gradient.setColorAt(1, QColor(ColorScheme.ACCENT_PURPLE))66 return gradient67 68 69class StyleSheet:70 """StyleSheet for Vision OS UI"""71 72 @staticmethod73 def get_base_stylesheet():74 """Get the base stylesheet for the application"""75 return f"""76 QWidget {{77 background-color: {ColorScheme.BACKGROUND_DARK};78 color: {ColorScheme.TEXT_PRIMARY};79 font-family: 'SF Pro Display', 'Helvetica Neue', Arial, sans-serif;80 font-size: 14px;81 }}82 83 QMainWindow {{84 background-color: {ColorScheme.BACKGROUND_DARK};85 }}86 87 QLabel {{88 color: {ColorScheme.TEXT_PRIMARY};89 }}90 91 QLabel#title {{92 font-size: 24px;93 font-weight: bold;94 color: {ColorScheme.ACCENT_BLUE};95 }}96 97 QLabel#subtitle {{98 font-size: 16px;99 color: {ColorScheme.TEXT_SECONDARY};100 }}101 102 QPushButton {{103 background-color: {ColorScheme.BACKGROUND_PANEL};104 color: {ColorScheme.ACCENT_BLUE};105 border: 1px solid {ColorScheme.ACCENT_BLUE};106 border-radius: 8px;107 padding: 8px 16px;108 font-weight: bold;109 }}110 111 QPushButton:hover {{112 background-color: rgba(0, 224, 255, 0.2);113 }}114 115 QPushButton:pressed {{116 background-color: rgba(0, 224, 255, 0.3);117 }}118 119 QLineEdit, QTextEdit {{120 background-color: {ColorScheme.BACKGROUND_PANEL};121 color: {ColorScheme.TEXT_PRIMARY};122 border: 1px solid #2A2F3C;123 border-radius: 8px;124 padding: 8px;125 }}126 127 QLineEdit:focus, QTextEdit:focus {{128 border: 1px solid {ColorScheme.ACCENT_BLUE};129 }}130 131 QScrollArea, QListWidget {{132 background-color: {ColorScheme.BACKGROUND_PANEL};133 border: 1px solid #2A2F3C;134 border-radius: 8px;135 }}136 137 QTabWidget::pane {{138 background-color: {ColorScheme.BACKGROUND_PANEL};139 border: 1px solid #2A2F3C;140 border-radius: 8px;141 }}142 143 QTabBar::tab {{144 background-color: {ColorScheme.BACKGROUND_DARK};145 color: {ColorScheme.TEXT_SECONDARY};146 border: 1px solid #2A2F3C;147 border-bottom: none;148 border-top-left-radius: 8px;149 border-top-right-radius: 8px;150 padding: 8px 16px;151 margin-right: 2px;152 }}153 154 QTabBar::tab:selected {{155 background-color: {ColorScheme.BACKGROUND_PANEL};156 color: {ColorScheme.ACCENT_BLUE};157 border-bottom: none;158 }}159 160 QComboBox {{161 background-color: {ColorScheme.BACKGROUND_PANEL};162 color: {ColorScheme.TEXT_PRIMARY};163 border: 1px solid #2A2F3C;164 border-radius: 8px;165 padding: 8px;166 }}167 168 QComboBox::drop-down {{169 border: none;170 }}171 172 QComboBox QAbstractItemView {{173 background-color: {ColorScheme.BACKGROUND_PANEL};174 color: {ColorScheme.TEXT_PRIMARY};175 selection-background-color: rgba(0, 224, 255, 0.2);176 }}177 178 QProgressBar {{179 background-color: {ColorScheme.BACKGROUND_PANEL};180 color: {ColorScheme.TEXT_PRIMARY};181 border: 1px solid #2A2F3C;182 border-radius: 8px;183 text-align: center;184 }}185 186 QProgressBar::chunk {{187 background-color: {ColorScheme.ACCENT_BLUE};188 border-radius: 8px;189 }}190 191 QCheckBox {{192 color: {ColorScheme.TEXT_PRIMARY};193 }}194 195 QCheckBox::indicator {{196 width: 18px;197 height: 18px;198 border: 1px solid #2A2F3C;199 border-radius: 4px;200 }}201 202 QCheckBox::indicator:checked {{203 background-color: {ColorScheme.ACCENT_BLUE};204 }}205 """206 207 208class NeomorphicButton(QPushButton):209 """Custom button with neomorphic design"""210 211 def __init__(self, text="", parent=None):212 super().__init__(text, parent)213 self.setFixedHeight(50)214 self.setMinimumWidth(120)215 self.setCursor(Qt.PointingHandCursor)216 217 # Set up shadow effect218 self.setStyleSheet("""219 NeomorphicButton {220 background-color: #141821;221 color: #00E0FF;222 border: none;223 border-radius: 10px;224 font-weight: bold;225 padding: 10px 20px;226 text-align: center;227 }228 """)229 230 def paintEvent(self, event):231 painter = QPainter(self)232 painter.setRenderHint(QPainter.Antialiasing)233 234 # Draw button background235 rect = self.rect()236 painter.setPen(Qt.NoPen)237 238 # Create gradient background239 gradient = QLinearGradient(0, 0, 0, rect.height())240 gradient.setColorAt(0, QColor("#1E2432"))241 gradient.setColorAt(1, QColor("#0F131C"))242 painter.setBrush(QBrush(gradient))243 244 # Draw rounded rectangle245 painter.drawRoundedRect(rect, 10, 10)246 247 # Draw top-left shadow (lighter)248 painter.setPen(QPen(QColor(255, 255, 255, 20), 1))249 painter.drawLine(rect.left() + 5, rect.top() + 5, rect.right() - 5, rect.top() + 5)250 painter.drawLine(rect.left() + 5, rect.top() + 5, rect.left() + 5, rect.bottom() - 5)251 252 # Draw bottom-right shadow (darker)253 painter.setPen(QPen(QColor(0, 0, 0, 60), 1))254 painter.drawLine(rect.left() + 5, rect.bottom() - 5, rect.right() - 5, rect.bottom() - 5)255 painter.drawLine(rect.right() - 5, rect.top() + 5, rect.right() - 5, rect.bottom() - 5)256 257 # Draw text258 painter.setPen(QColor(ColorScheme.ACCENT_BLUE))259 painter.setFont(self.font())260 painter.drawText(rect, Qt.AlignCenter, self.text())261 262 263class GlassmorphicPanel(QFrame):264 """Custom panel with glassmorphic design"""265 266 def __init__(self, parent=None):267 super().__init__(parent)268 self.setObjectName("glassmorphicPanel")269 self.setStyleSheet("""270 #glassmorphicPanel {271 background-color: rgba(30, 36, 50, 0.7);272 border: 1px solid rgba(255, 255, 255, 0.1);273 border-radius: 16px;274 }275 """)276 277 # Set up layout278 self.layout = QVBoxLayout(self)279 self.layout.setContentsMargins(20, 20, 20, 20)280 self.layout.setSpacing(15)281 self.setLayout(self.layout)282 283 284class ParticleBackground(QWidget):285 """Background widget with particle effect"""286 287 def __init__(self, parent=None):288 super().__init__(parent)289 self.setAttribute(Qt.WA_StyledBackground, True)290 self.setStyleSheet(f"background-color: {ColorScheme.BACKGROUND_DARK};")291 292 # Particle properties293 self.particles = []294 self.num_particles = 100295 self.init_particles()296 297 # Animation timer298 self.timer = QTimer(self)299 self.timer.timeout.connect(self.update_particles)300 self.timer.start(50) # Update every 50ms301 302 def init_particles(self):303 """Initialize particles"""304 import random305 306 for _ in range(self.num_particles):307 particle = {308 'x': random.randint(0, self.width() or 800),309 'y': random.randint(0, self.height() or 600),310 'size': random.uniform(1, 3),311 'speed': random.uniform(0.2, 1.0),312 'opacity': random.uniform(0.1, 0.5),313 'color': random.choice([314 QColor(ColorScheme.ACCENT_BLUE),315 QColor(ColorScheme.ACCENT_CYAN),316 QColor(ColorScheme.ACCENT_PURPLE)317 ])318 }319 self.particles.append(particle)320 321 def update_particles(self):322 """Update particle positions"""323 import random324 325 for particle in self.particles:326 # Move particles upward with slight horizontal drift327 particle['y'] -= particle['speed']328 particle['x'] += random.uniform(-0.5, 0.5)329 330 # Reset particles that go off-screen331 if particle['y'] < -10:332 particle['y'] = self.height() + 10333 particle['x'] = random.randint(0, self.width())334 335 self.update() # Trigger repaint336 337 def paintEvent(self, event):338 """Paint the particles"""339 super().paintEvent(event)340 341 painter = QPainter(self)342 painter.setRenderHint(QPainter.Antialiasing)343 344 # Draw particles345 for particle in self.particles:346 color = particle['color']347 color.setAlphaF(particle['opacity'])348 painter.setPen(Qt.NoPen)349 painter.setBrush(color)350 351 painter.drawEllipse(352 QPoint(int(particle['x']), int(particle['y'])),353 particle['size'],354 particle['size']355 )356 357 def resizeEvent(self, event):358 """Handle resize events"""359 super().resizeEvent(event)360 361 # Adjust particle positions for new size362 import random363 for particle in self.particles:364 particle['x'] = random.randint(0, self.width())365 particle['y'] = random.randint(0, self.height())366 367 368class ChatPanel(QWidget):369 """Chat interface panel for interacting with Vision OS"""370 371 def __init__(self, parent=None, message_callback=None):372 super().__init__(parent)373 self.message_callback = message_callback374 self.setup_ui()375 376 def setup_ui(self):377 """Set up the chat panel UI"""378 layout = QVBoxLayout(self)379 layout.setContentsMargins(0, 0, 0, 0)380 381 # Chat history area382 self.chat_history = QTextEdit()383 self.chat_history.setReadOnly(True)384 self.chat_history.setStyleSheet("""385 QTextEdit {386 background-color: rgba(20, 24, 33, 0.7);387 border: 1px solid rgba(255, 255, 255, 0.1);388 border-radius: 12px;389 padding: 15px;390 font-size: 14px;391 }392 """)393 394 # Input area395 input_container = QWidget()396 input_layout = QHBoxLayout(input_container)397 input_layout.setContentsMargins(0, 10, 0, 0)398 399 self.message_input = QTextEdit()400 self.message_input.setPlaceholderText("Type your message here...")401 self.message_input.setMaximumHeight(80)402 self.message_input.setStyleSheet("""403 QTextEdit {404 background-color: rgba(20, 24, 33, 0.8);405 border: 1px solid rgba(255, 255, 255, 0.1);406 border-radius: 12px;407 padding: 10px 15px;408 font-size: 14px;409 }410 """)411 412 self.send_button = NeomorphicButton("Send")413 self.send_button.setFixedSize(100, 40)414 self.send_button.clicked.connect(self.send_message)415 416 input_layout.addWidget(self.message_input, 1)417 input_layout.addWidget(self.send_button, 0)418 419 # Add widgets to main layout420 layout.addWidget(self.chat_history, 1)421 layout.addWidget(input_container, 0)422 423 # Set up key event for message input424 self.message_input.installEventFilter(self)425 426 def eventFilter(self, obj, event):427 """Handle key events for message input"""428 if obj == self.message_input and event.type() == event.KeyPress:429 if event.key() == Qt.Key_Return and not event.modifiers() & Qt.ShiftModifier:430 self.send_message()431 return True432 return super().eventFilter(obj, event)433 434 def send_message(self):435 """Send the current message"""436 message = self.message_input.toPlainText().strip()437 if not message:438 return439 440 # Add user message to chat history441 self.add_message(message, is_user=True)442 443 # Clear input field444 self.message_input.clear()445 446 # Call the message callback if provided447 if self.message_callback:448 self.message_callback(message)449 450 def add_message(self, message, is_user=False):451 """Add a message to the chat history"""452 # Format message with appropriate styling453 sender = "You" if is_user else "Vision OS"454 color = "#FFFFFF" if is_user else ColorScheme.ACCENT_BLUE455 456 html = f"""457 <div style="margin-bottom: 10px;">458 <span style="color: {color}; font-weight: bold;">{sender}:</span>459 <div style="margin-left: 10px; margin-top: 5px;">{message}</div>460 </div>461 """462 463 # Add to chat history464 cursor = self.chat_history.textCursor()465 cursor.movePosition(cursor.End)466 cursor.insertHtml(html)467 468 # Scroll to bottom469 self.chat_history.setTextCursor(cursor)470 self.chat_history.ensureCursorVisible()471 472 473class AgentPanel(QWidget):474 """Panel for displaying and interacting with available agents"""475 476 def __init__(self, parent=None, agent_callback=None):477 super().__init__(parent)478 self.agent_callback = agent_callback479 self.setup_ui()480 481 def setup_ui(self):482 """Set up the agent panel UI"""483 layout = QVBoxLayout(self)484 layout.setContentsMargins(0, 0, 0, 0)485 486 # Title487 title = QLabel("Available Agents")488 title.setObjectName("subtitle")489 title.setAlignment(Qt.AlignCenter)490 491 # Agent list492 self.agent_list = QListWidget()493 self.agent_list.setStyleSheet("""494 QListWidget {495 background-color: rgba(20, 24, 33, 0.7);496 border: 1px solid rgba(255, 255, 255, 0.1);497 border-radius: 12px;498 padding: 10px;499 }500 501 QListWidget::item {502 background-color: rgba(30, 36, 50, 0.7);503 border: 1px solid rgba(255, 255, 255, 0.1);504 border-radius: 8px;505 padding: 10px;506 margin-bottom: 5px;507 }508 509 QListWidget::item:selected {510 background-color: rgba(0, 224, 255, 0.2);511 border: 1px solid rgba(0, 224, 255, 0.5);512 }513 514 QListWidget::item:hover {515 background-color: rgba(0, 224, 255, 0.1);516 }517 """)518 519 # Add agents to the list520 self.add_agent("System Agent", "Control your computer and OS", "system")521 self.add_agent("Automation Agent", "Automate web and applications", "automation")522 self.add_agent("File Agent", "Manage and process files", "file")523 self.add_agent("Task Aid Agent", "Manage tasks and projects", "task")524 self.add_agent("Creative Agent", "Generate creative content", "creative")525 self.add_agent("Grant Writer Agent", "Create grant proposals", "grant")526 527 # Connect signals528 self.agent_list.itemClicked.connect(self.on_agent_selected)529 530 # Add widgets to layout531 layout.addWidget(title)532 layout.addWidget(self.agent_list)533 534 def add_agent(self, name, description, agent_id):535 """Add an agent to the list"""536 item = QListWidgetItem()537 item.setData(Qt.UserRole, agent_id)538 539 # Create widget for the item540 widget = QWidget()541 layout = QVBoxLayout(widget)542 layout.setContentsMargins(5, 5, 5, 5)543 544 name_label = QLabel(name)545 name_label.setStyleSheet("font-weight: bold; color: #FFFFFF;")546 547 desc_label = QLabel(description)548 desc_label.setStyleSheet("color: #B0B7C3; font-size: 12px;")549 550 layout.addWidget(name_label)551 layout.addWidget(desc_label)552 553 # Set size hint554 item.setSizeHint(widget.sizeHint())555 556 # Add to list557 self.agent_list.addItem(item)558 self.agent_list.setItemWidget(item, widget)559 560 def on_agent_selected(self, item):561 """Handle agent selection"""562 agent_id = item.data(Qt.UserRole)563 564 if self.agent_callback:565 self.agent_callback(agent_id)566 567 568class MainWindow(QMainWindow):569 """Main window for Vision OS application"""570 571 def __init__(self):572 super().__init__()573 self.setWindowTitle("Vision OS")574 self.setMinimumSize(1200, 800)575 576 # Apply stylesheet577 self.setStyleSheet(StyleSheet.get_base_stylesheet())578 579 # Set up UI580 self.setup_ui()581 582 def setup_ui(self):583 """Set up the main window UI"""584 # Create central widget with particle background585 self.central_widget = ParticleBackground()586 self.setCentralWidget(self.central_widget)587 588 # Main layout589 main_layout = QVBoxLayout(self.central_widget)590 main_layout.setContentsMargins(20, 20, 20, 20)591 main_layout.setSpacing(20)592 593 # Header594 header = QWidget()595 header_layout = QHBoxLayout(header)596 header_layout.setContentsMargins(0, 0, 0, 0)597 598 logo_label = QLabel("VISION OS")599 logo_label.setObjectName("title")600 logo_label.setStyleSheet("""601 QLabel#title {602 font-size: 28px;603 font-weight: bold;604 background: -webkit-linear-gradient(left, #00E0FF, #9D4EDD);605 -webkit-background-clip: text;606 -webkit-text-fill-color: transparent;607 }608 """)609 610 header_layout.addWidget(logo_label)611 header_layout.addStretch(1)612 613 # Create menu buttons614 for text in ["Dashboard", "Settings", "Help"]:615 button = QPushButton(text)616 button.setFlat(True)617 button.setStyleSheet("""618 QPushButton {619 color: #B0B7C3;620 border: none;621 font-size: 16px;622 padding: 8px 16px;623 }624 QPushButton:hover {625 color: #FFFFFF;626 }627 """)628 header_layout.addWidget(button)629 630 # Main content area631 content = QWidget()632 content_layout = QHBoxLayout(content)633 content_layout.setContentsMargins(0, 0, 0, 0)634 content_layout.setSpacing(20)635 636 # Left panel (agents)637 left_panel = GlassmorphicPanel()638 left_panel.layout.setContentsMargins(15, 15, 15, 15)639 640 agent_panel = AgentPanel(agent_callback=self.on_agent_selected)641 left_panel.layout.addWidget(agent_panel)642 643 # Center panel (chat)644 center_panel = GlassmorphicPanel()645 center_panel.layout.setContentsMargins(15, 15, 15, 15)646 647 chat_title = QLabel("Vision OS Assistant")648 chat_title.setObjectName("subtitle")649 chat_title.setAlignment(Qt.AlignCenter)650 651 self.chat_panel = ChatPanel(message_callback=self.on_message_sent)652 653 center_panel.layout.addWidget(chat_title)654 center_panel.layout.addWidget(self.chat_panel)655 656 # Right panel (context)657 right_panel = GlassmorphicPanel()658 right_panel.layout.setContentsMargins(15, 15, 15, 15)659 660 context_title = QLabel("Context & Memory")661 context_title.setObjectName("subtitle")662 context_title.setAlignment(Qt.AlignCenter)663 664 self.context_text = QTextEdit()665 self.context_text.setReadOnly(True)666 self.context_text.setPlaceholderText("Context information will appear here...")667 self.context_text.setStyleSheet("""668 QTextEdit {669 background-color: rgba(20, 24, 33, 0.7);670 border: 1px solid rgba(255, 255, 255, 0.1);671 border-radius: 12px;672 padding: 15px;673 font-size: 14px;674 }675 """)676 677 right_panel.layout.addWidget(context_title)678 right_panel.layout.addWidget(self.context_text)679 680 # Add panels to content layout681 content_layout.addWidget(left_panel, 1)682 content_layout.addWidget(center_panel, 2)683 content_layout.addWidget(right_panel, 1)684 685 # Add widgets to main layout686 main_layout.addWidget(header)687 main_layout.addWidget(content, 1)688 689 # Add welcome message690 self.add_welcome_message()691 692 def add_welcome_message(self):693 """Add welcome message to chat"""694 welcome_message = """695 Welcome to Vision OS! I'm your AI assistant, ready to help you with:696 697 • System control and automation698 • Web browsing and data extraction699 • File management and organization700 • Task planning and tracking701 • Creative content generation702 • Grant writing and proposals703 704 How can I assist you today?705 """706 707 self.chat_panel.add_message(welcome_message)708 709 def on_message_sent(self, message):710 """Handle message sent from chat panel"""711 # This would connect to the orchestrator to process the message712 # For now, just echo a response713 import time714 time.sleep(0.5) # Simulate processing time715 716 response = f"I received your message: '{message}'\n\nI'm processing your request..."717 self.chat_panel.add_message(response)718 719 def on_agent_selected(self, agent_id):720 """Handle agent selection"""721 # Update context panel with agent information722 agent_info = {723 "system": "System Agent can control your computer, execute commands, and manage system resources.",724 "automation": "Automation Agent can automate web browsing, form filling, and data extraction.",725 "file": "File Agent can manage files, organize folders, and process documents.",726 "task": "Task Aid Agent can help you manage tasks, track progress, and break down complex goals.",727 "creative": "Creative Agent can generate various types of creative content like stories, poems, and marketing copy.",728 "grant": "Grant Writer Agent can help you create professional grant proposals and research statements."729 }730 731 if agent_id in agent_info:732 self.context_text.setText(f"Selected Agent: {agent_id.capitalize()} Agent\n\n{agent_info[agent_id]}")733 734 # Also add a message to the chat735 self.chat_panel.add_message(f"You've selected the {agent_id.capitalize()} Agent. How can I help you with this?")736 737 738def launch_app():739 """Launch the Vision OS application"""740 app = QApplication(sys.argv)741 742 # Set application style743 app.setStyle("Fusion")744 745 # Create and show main window746 window = MainWindow()747 window.show()748 749 sys.exit(app.exec_())750 751 752if __name__ == "__main__":753 launch_app()754 