"""Scrollback for the speech bubble. The bubble is transient by design, so anything Bolt said more than a few seconds ago is gone. This is the "wait, what was that path again?" window: the last HISTORY_LIMIT turns, copyable. Opened from the tray. Reads a bolt_pet.history.ConversationHistory (pure logic, tested separately); this file is only presentation. """ from __future__ import annotations import time from typing import Callable, Optional from PySide6.QtCore import Qt from PySide6.QtWidgets import ( QApplication, QDialog, QHBoxLayout, QPlainTextEdit, QPushButton, QVBoxLayout, ) from ..history import ConversationHistory def _clock(timestamp: float) -> str: return time.strftime("%H:%M:%S", time.localtime(timestamp)) class HistoryWindow(QDialog): def __init__(self, history: ConversationHistory, on_clear: Optional[Callable[[], None]] = None): super().__init__() self._history = history self._on_clear = on_clear self.setWindowTitle("Bolt — conversation history") self.resize(620, 420) self._view = QPlainTextEdit() self._view.setReadOnly(True) self._view.setLineWrapMode(QPlainTextEdit.WidgetWidth) copy_button = QPushButton("Copy all") copy_button.clicked.connect(self._copy_all) clear_button = QPushButton("Clear") clear_button.clicked.connect(self._clear) close_button = QPushButton("Close") close_button.clicked.connect(self.close) close_button.setDefault(True) buttons = QHBoxLayout() buttons.addWidget(copy_button) buttons.addWidget(clear_button) buttons.addStretch(1) buttons.addWidget(close_button) layout = QVBoxLayout(self) layout.addWidget(self._view) layout.addLayout(buttons) def refresh(self) -> None: self._view.setPlainText(self._history.as_text(clock=_clock)) # Jump to the newest line — that's what you opened this for. scrollbar = self._view.verticalScrollBar() scrollbar.setValue(scrollbar.maximum()) def show_refreshed(self) -> None: self.refresh() self.show() self.raise_() self.activateWindow() def _copy_all(self) -> None: QApplication.clipboard().setText(self._history.as_text(clock=_clock)) def _clear(self) -> None: self._history.clear() if self._on_clear is not None: self._on_clear() self.refresh() def keyPressEvent(self, event) -> None: if event.key() == Qt.Key_Escape: self.close() return super().keyPressEvent(event)