Skip to content

Commit

Permalink
better scroll to bottom handling in chat prompt widget
Browse files Browse the repository at this point in the history
added smooth scrolling animation
  • Loading branch information
w4ffl35 committed Oct 23, 2024
1 parent 122d6e8 commit 3785fc2
Showing 1 changed file with 39 additions and 18 deletions.
57 changes: 39 additions & 18 deletions src/airunner/widgets/llm/chat_prompt_widget.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from PySide6.QtCore import Slot, QTimer
from PySide6.QtCore import Slot, QTimer, QPropertyAnimation
from PySide6.QtWidgets import QSpacerItem, QSizePolicy
from PySide6.QtCore import Qt

Expand All @@ -14,6 +14,8 @@ class ChatPromptWidget(BaseWidget):

def __init__(self, *args, **kwargs):
super().__init__()
self.scroll_bar = None
self._loading_widget = None
self.conversation = None
self.is_modal = True
self.generating = False
Expand All @@ -35,13 +37,16 @@ def __init__(self, *args, **kwargs):
self.ui.action.addItem("Auto")
self.ui.action.addItem("Chat")
self.ui.action.addItem("Image")
self.ui.action.addItem("RAG")
action = LLMActionType[self.action]
if action is LLMActionType.APPLICATION_COMMAND:
self.ui.action.setCurrentIndex(0)
elif action is LLMActionType.CHAT:
self.ui.action.setCurrentIndex(1)
elif action is LLMActionType.GENERATE_IMAGE:
self.ui.action.setCurrentIndex(2)
elif action is LLMActionType.PERFORM_RAG_SEARCH:
self.ui.action.setCurrentIndex(3)
self.ui.action.blockSignals(False)
self.originalKeyPressEvent = None
self.originalKeyPressEvent = self.ui.prompt.keyPressEvent
Expand All @@ -52,6 +57,7 @@ def __init__(self, *args, **kwargs):
self.register(SignalCode.CONVERSATION_DELETED, self.on_conversation_deleted)
self.held_message = None
self._disabled = False
self.scroll_animation = None

@Slot(str)
def handle_token_signal(self, val: str):
Expand Down Expand Up @@ -86,7 +92,6 @@ def on_set_conversation(self, message):
if len(message["messages"]) > 0:
self.conversation_id = message["messages"][0]["conversation_id"]
QTimer.singleShot(0, lambda: self._set_conversation_widgets(message["messages"]))
self.scroll_to_bottom()

def _set_conversation_widgets(self, messages):
for message in messages:
Expand All @@ -97,6 +102,7 @@ def _set_conversation_widgets(self, messages):
first_message=True,
use_loading_widget=False
)
self.scroll_to_bottom()

def on_hear_signal(self, data: dict):
transcription = data["transcription"]
Expand Down Expand Up @@ -146,6 +152,7 @@ def _clear_conversation(self):
self.conversation_history = []
self._clear_conversation_widgets()
self._create_conversation()
self.remove_loading_widget()

def _create_conversation(self):
conversation_id = self.create_conversation()
Expand All @@ -166,6 +173,7 @@ def interrupt_button_clicked(self):
self.emit_signal(SignalCode.INTERRUPT_PROCESS_SIGNAL)
self.stop_progress_bar()
self.generating = False
self.remove_loading_widget()
self.enable_send_button()

@property
Expand Down Expand Up @@ -206,6 +214,7 @@ def do_generate(self, image_override=None, prompt_override=None, callback=None,
}
}
)
self.scroll_to_bottom()

def on_token_signal(self, val):
self.handle_token_signal(val)
Expand Down Expand Up @@ -307,20 +316,21 @@ def describe_image(self, image, callback):
)

def add_loading_widget(self):
self._has_loading_widget = True
self.ui.scrollAreaWidgetContents.layout().addWidget(
LoadingWidget()
)
if not self._has_loading_widget:
self._has_loading_widget = True
if self._loading_widget is None:
self._loading_widget = LoadingWidget()
self.ui.scrollAreaWidgetContents.layout().addWidget(
self._loading_widget
)

def remove_loading_widget(self):
# remove the last LoadingWidget from scrollAreaWidgetContents.layout()
for i in range(self.ui.scrollAreaWidgetContents.layout().count()):
current_widget = self.ui.scrollAreaWidgetContents.layout().itemAt(i).widget()
if isinstance(current_widget, LoadingWidget):
self.ui.scrollAreaWidgetContents.layout().removeWidget(current_widget)
current_widget.deleteLater()
self._has_loading_widget = False
break
if self._has_loading_widget:
try:
self.ui.scrollAreaWidgetContents.layout().removeWidget(self._loading_widget)
except RuntimeError:
pass
self._has_loading_widget = False

def add_message_to_conversation(
self,
Expand All @@ -347,7 +357,7 @@ def add_message_to_conversation(

self.remove_spacer()

if is_bot and use_loading_widget and self._has_loading_widget and action == LLMActionType.CHAT:
if is_bot and use_loading_widget:
self.remove_loading_widget()

if message != "":
Expand Down Expand Up @@ -378,6 +388,17 @@ def action_button_clicked_generate_characters(self):
pass

def scroll_to_bottom(self):
self.ui.chat_container.verticalScrollBar().setValue(
self.ui.chat_container.verticalScrollBar().maximum()
)
if self.scroll_bar is None:
self.scroll_bar = self.ui.chat_container.verticalScrollBar()

if self.scroll_animation is None:
self.scroll_animation = QPropertyAnimation(self.scroll_bar, b"value")
self.scroll_animation.setDuration(500)

# Stop any ongoing animation
if self.scroll_animation and self.scroll_animation.state() == QPropertyAnimation.State.Running:
self.scroll_animation.stop()

self.scroll_animation.setStartValue(self.scroll_bar.value())
self.scroll_animation.setEndValue(self.scroll_bar.maximum())
self.scroll_animation.start()

0 comments on commit 3785fc2

Please sign in to comment.