Skip to content

Commit b035d5a

Browse files
committed
Hide continue and stop buttons for now
1 parent bcade82 commit b035d5a

File tree

2 files changed

+69
-17
lines changed

2 files changed

+69
-17
lines changed

tldw_chatbook/UI/Chat_Window.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,8 @@ def compose(self) -> ComposeResult:
4848
yield Button(get_char(EMOJI_SEND, FALLBACK_SEND), id="send-chat", classes="send-button")
4949
yield Button("💡", id="respond-for-me-button", classes="action-button suggest-button") # Suggest button
5050
self.app_instance.loguru_logger.debug("ChatWindow: 'respond-for-me-button' composed.")
51-
yield Button(get_char(EMOJI_STOP, FALLBACK_STOP), id="stop-chat-generation", classes="stop-button",
52-
disabled=True)
51+
#yield Button(get_char(EMOJI_STOP, FALLBACK_STOP), id="stop-chat-generation", classes="stop-button",
52+
# disabled=True)
5353
yield Button(get_char(EMOJI_CHARACTER_ICON, FALLBACK_CHARACTER_ICON), id="toggle-chat-right-sidebar",
5454
classes="sidebar-toggle")
5555

tldw_chatbook/Widgets/chat_message.py

Lines changed: 67 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@
44
# Imports
55
#
66
# 3rd-party Libraries
7+
import logging
78
from typing import Optional
89

910
from textual.app import ComposeResult
1011
from textual.containers import Horizontal, Vertical
12+
from textual.css.query import QueryError
1113
from textual.widget import Widget
1214
from textual.widgets import Static, Button, Label # Added Label
1315
from textual.reactive import reactive
@@ -83,8 +85,9 @@ class ChatMessage(Widget):
8385

8486
# Store the raw text content
8587
message_text = reactive("", repaint=True)
86-
role = reactive("User", repaint=True) # "User" or "AI"
87-
generation_complete = reactive(True) # Used for AI messages to show actions
88+
role = reactive("User", repaint=True)
89+
# Use an internal reactive to manage generation status and trigger UI updates
90+
_generation_complete_internal = reactive(True)
8891

8992
# -- Internal state for message metadata ---
9093
message_id_internal: reactive[Optional[str]] = reactive(None)
@@ -108,7 +111,7 @@ def __init__(self,
108111
super().__init__(**kwargs)
109112
self.message_text = message
110113
self.role = role
111-
self.generation_complete = generation_complete
114+
self._generation_complete_internal = generation_complete
112115

113116
self.message_id_internal = message_id
114117
self.message_version_internal = message_version
@@ -124,6 +127,11 @@ def __init__(self,
124127
else: # Any role other than "user" (e.g., "AI", "Default Assistant", "Character Name") gets the -ai style
125128
self.add_class("-ai")
126129

130+
@property
131+
def generation_complete(self) -> bool:
132+
"""Public property to access the generation status."""
133+
return self._generation_complete_internal
134+
127135
def compose(self) -> ComposeResult:
128136
with Vertical():
129137
yield Label(f"{self.role}", classes="message-header")
@@ -133,7 +141,9 @@ def compose(self) -> ComposeResult:
133141
# This should only apply if it's an AI message AND generation is not complete
134142
if self.has_class("-ai") and not self.generation_complete:
135143
actions_class += " -generating"
136-
with Horizontal(classes=actions_class):
144+
145+
with Horizontal(classes=actions_class) as actions_bar:
146+
actions_bar.id = f"actions-bar-{self.id or self.message_id_internal or 'new'}"
137147
# Common buttons
138148
yield Button("Edit", classes="action-button edit-button")
139149
yield Button("📋", classes="action-button copy-button", id="copy") # Emoji for copy
@@ -144,25 +154,67 @@ def compose(self) -> ComposeResult:
144154
yield Button("👍", classes="action-button thumb-up-button", id="thumb-up")
145155
yield Button("👎", classes="action-button thumb-down-button", id="thumb-down")
146156
yield Button("🔄", classes="action-button regenerate-button", id="regenerate") # Emoji for regenerate
147-
if self.generation_complete: # Only show continue if generation is complete
148-
yield Button("↪️", id="continue-response-button", classes="action-button continue-button")
157+
# FIXME For some reason, the entire UI freezes when clicked...
158+
#yield Button("↪️", id="continue-response-button", classes="action-button continue-button")
149159

150160
# Add delete button for all messages at very end
151161
yield Button("🗑️", classes="action-button delete-button") # Emoji for delete ; Label: Delete, Class: delete-button
152162

153-
def update_message_chunk(self, chunk: str):
163+
def watch__generation_complete_internal(self, complete: bool) -> None:
164+
"""
165+
Watcher for the internal generation status.
166+
Updates the actions bar visibility and the continue button visibility for AI messages.
167+
"""
154168
if self.has_class("-ai"):
155-
self.query_one(".message-text", Static).update(self.message_text + chunk)
156-
self.message_text += chunk
169+
try:
170+
actions_container = self.query_one(".message-actions")
171+
continue_button = self.query_one("#continue-response-button", Button)
172+
173+
if complete:
174+
actions_container.remove_class("-generating") # Makes the bar visible via CSS
175+
actions_container.styles.display = "block" # Ensures bar is visible
176+
continue_button.display = True # Makes continue button visible
177+
else:
178+
# This state typically occurs during initialization if generation_complete=False
179+
actions_container.add_class("-generating") # Hides the bar via CSS
180+
# actions_container.styles.display = "none" # CSS rule should handle this
181+
continue_button.display = False # Hides continue button
182+
except QueryError as qe:
183+
# This might happen if the query runs before the widget is fully composed or if it's being removed.
184+
logging.debug(f"ChatMessage (ID: {self.id}, Role: {self.role}): QueryError in watch__generation_complete_internal: {qe}. Widget might not be fully ready or is not an AI message with these components.")
185+
except Exception as e:
186+
logging.error(f"Error in watch__generation_complete_internal for ChatMessage (ID: {self.id}): {e}", exc_info=True)
187+
else: # Not an AI message
188+
try: # Ensure continue button is hidden for non-AI messages if it somehow got queried
189+
continue_button = self.query_one("#continue-response-button", Button)
190+
continue_button.display = False
191+
except QueryError:
192+
pass # Expected for non-AI messages as the button isn't composed.
193+
157194

158195
def mark_generation_complete(self):
196+
"""
197+
Marks the AI message generation as complete.
198+
This will trigger the watcher for _generation_complete_internal to update UI.
199+
"""
159200
if self.has_class("-ai"):
160-
self.generation_complete = True
161-
actions_container = self.query_one(".message-actions")
162-
actions_container.remove_class("-generating")
163-
# Ensure it's displayed if CSS might still hide it via other means,
164-
# though removing '-generating' should be enough if the CSS is specific.
165-
actions_container.styles.display = "block" # or "flex" if it's a flex container
201+
self._generation_complete_internal = True
202+
203+
def on_mount(self) -> None:
204+
"""Ensure initial state of continue button and actions bar is correct after mounting."""
205+
# Trigger the watcher logic based on the initial state.
206+
self.watch__generation_complete_internal(self._generation_complete_internal)
207+
208+
def update_message_chunk(self, chunk: str):
209+
"""Appends a chunk of text to an AI message during streaming."""
210+
# This method is called by handle_streaming_chunk.
211+
# The _generation_complete_internal should be False during streaming.
212+
if self.has_class("-ai") and not self._generation_complete_internal:
213+
# The static_text_widget.update is handled in handle_streaming_chunk
214+
# This method primarily updates the internal message_text.
215+
self.message_text += chunk
216+
# If called at other times, ensure it doesn't break if static_text_widget not found.
217+
# For streaming, handle_streaming_chunk updates the Static widget directly.
166218

167219
#
168220
#

0 commit comments

Comments
 (0)