from PySide6.QtCore import QEasingCurve, QPoint, QPropertyAnimation, Qt, Signal from PySide6.QtWidgets import ( QApplication, QLabel, QLineEdit, QMainWindow, QVBoxLayout, QWidget, ) class RisaAssistantPopup(QMainWindow): text_submitted = Signal(str) def __init__(self): super().__init__() self.setWindowFlags( Qt.WindowType.FramelessWindowHint | Qt.WindowType.WindowStaysOnTopHint | Qt.WindowType.Tool ) self.setAttribute(Qt.WidgetAttribute.WA_TranslucentBackground) self.popup_width = 540 self.font_style = "font-family: 'Segoe UI', -apple-system, BlinkMacSystemFont, Arial, sans-serif;" self.main_container = QWidget(self) self.main_layout = QVBoxLayout(self.main_container) self.main_layout.setContentsMargins(10, 10, 10, 10) self.main_layout.setSpacing(15) self.content_card = QWidget() self.content_card.setObjectName("ContentCard") self.content_layout = QVBoxLayout(self.content_card) self.content_layout.setContentsMargins(22, 16, 22, 16) self.content_label = QLabel() self.content_label.setTextFormat(Qt.TextFormat.RichText) self.content_label.setWordWrap(True) self.content_layout.addWidget(self.content_label) self.main_layout.addWidget(self.content_card) self.input_card = QWidget() self.input_card.setObjectName("InputCard") self.input_layout = QVBoxLayout(self.input_card) self.input_layout.setContentsMargins(20, 20, 20, 20) self.input_layout.setAlignment(Qt.AlignmentFlag.AlignCenter) self.star_icon = QLabel("✹") self.star_icon.setStyleSheet("font-size: 42px; color: #000000; margin-bottom: 2px;") self.star_icon.setAlignment(Qt.AlignmentFlag.AlignCenter) self.input_layout.addWidget(self.star_icon) self.status_label = QLabel("Sleeping...") self.status_label.setAlignment(Qt.AlignmentFlag.AlignCenter) self.status_label.setStyleSheet("font-size: 13px; color: #777777; font-weight: 600; margin-bottom: 5px;") self.input_layout.addWidget(self.status_label) self.input_field = QLineEdit() self.input_field.setPlaceholderText("Type or Speak to RISA...") self.input_field.setAlignment(Qt.AlignmentFlag.AlignCenter) self.input_field.setFrame(False) self.input_layout.addWidget(self.input_field) self.input_field.returnPressed.connect(self._on_text_submitted) self.main_layout.addWidget(self.input_card) self.setCentralWidget(self.main_container) self.setStyleSheet(""" QWidget#ContentCard, QWidget#InputCard { background-color: rgba(245, 245, 240, 0.78); border: 1px solid rgba(255, 255, 255, 0.45); border-radius: 28px; } QLineEdit { background: transparent; color: #000000; font-family: 'Segoe UI', Arial, sans-serif; font-size: 18px; } QLineEdit::placeholder { color: #555555; } """) self.content_card.hide() self.anim = None def _on_text_submitted(self): text = self.input_field.text().strip() if text: self.input_field.clear() self.text_submitted.emit(text) def set_listening_state(self, state: str): if state == "idle": self.star_icon.setStyleSheet("font-size: 42px; color: #888888; margin-bottom: 2px;") self.status_label.setText("Sleeping (Say 'Hey Risa')...") self.status_label.setStyleSheet("font-size: 13px; color: #888888; font-weight: 600; margin-bottom: 5px;") elif state == "active": self.star_icon.setStyleSheet("font-size: 42px; color: #D4FF00; margin-bottom: 2px;") self.status_label.setText("Listening...") self.status_label.setStyleSheet("font-size: 13px; color: #ff8c00; font-weight: bold; margin-bottom: 5px;") elif state == "thinking": self.star_icon.setStyleSheet("font-size: 42px; color: #007acc; margin-bottom: 2px;") self.status_label.setText("Thinking...") self.status_label.setStyleSheet("font-size: 13px; color: #007acc; font-weight: bold; margin-bottom: 5px;") def update_layout_and_animate(self): self.setFixedWidth(self.popup_width) self.adjustSize() self.resize(self.popup_width, self.sizeHint().height()) self.animate_popup(self.height()) def show_initial_input(self): self.content_card.hide() self.input_layout.setContentsMargins(20, 25, 20, 25) self.star_icon.show() self.status_label.show() self.input_field.setFocus() self.update_layout_and_animate() def show_thinking(self, prompt_text: str): html_content = f"""
| {data.get('temp', '72')}° | {data.get('humidity', '55')}% Humidity {data.get('high', '85')}° High {data.get('low', '63')}° Low |
| {data.get('condition', 'Sunny')} | {data.get('location', 'Bridgewater NJ')} |
| {data.get('time', '3:30 PM')} | {data.get('day', '')} |
| Sunny, rain soon. | {data.get('date', '')} |