feat: 实现流式消息显示和界面优化

This commit is contained in:
JianFeeeee
2026-04-11 09:30:56 +08:00
parent 4075f9a30d
commit b33a87d19e
14 changed files with 538 additions and 193 deletions

View File

@ -63,7 +63,7 @@ class ConfigSection(Vertical):
yield hint
def on_mount(self) -> None:
"""组件挂载时设置Tab顺序"""
"""组件挂载时设置Tab顺序并加载配置"""
try:
api_key = self.query_one("#api-key-input", Input)
model = self.query_one("#model-input", Input)
@ -73,6 +73,14 @@ class ConfigSection(Vertical):
api_key.tab_index = 0
model.tab_index = 1
base_url.tab_index = 2
# 如果配置有值,更新输入框
if self._config.api_key:
api_key.value = self._config.api_key
if self._config.model:
model.value = self._config.model
if self._config.base_url:
base_url.value = self._config.base_url
except Exception:
pass

View File

@ -27,6 +27,15 @@ class MessageHistory(ScrollableContainer):
# 滚动到最新消息
self.scroll_to_widget(message_widget, animate=False)
def update_latest_message(self, content: str) -> None:
"""更新最新消息的内容"""
if self.children:
latest_widget = self.children[-1]
if isinstance(latest_widget, MessageWidget):
latest_widget.update_content(content)
# 确保滚动到最新消息
self.scroll_to_widget(latest_widget, animate=False)
def clear_messages(self) -> None:
"""清空消息历史"""
self._messages.clear()

View File

@ -13,6 +13,7 @@ class MessageWidget(Container):
super().__init__(**kwargs)
self._message = message
self._show_tool_details = False
self._content_widget = None # 保存内容组件的引用
def compose(self):
"""构建消息组件"""
@ -20,12 +21,16 @@ class MessageWidget(Container):
role_emoji = "🟠" if self._message.role == "user" else "🔵"
timestamp_str = self._message.timestamp.strftime("%H:%M:%S")
yield Static(
f"{role_emoji} [{self._message.role}] {timestamp_str}",
f"{role_emoji} {timestamp_str}",
classes="message-header"
)
# 消息内容
yield Static(self._message.content, classes="message-content")
# 消息内容 - 保存引用以便后续更新
self._content_widget = Static(
self._message.content,
classes="message-content"
)
yield self._content_widget
# 工具调用指示器
if self._message.tool_calls:
@ -52,11 +57,22 @@ class MessageWidget(Container):
if self._message.tool_results:
for result in self._message.tool_results:
if result.tool_call_id == tool_call.id:
# 显示完整结果,不截断
result_text = result.result
# 如果结果太长只显示前1000字符但提供完整信息
if len(result_text) > 1000:
result_text = result_text[:1000] + f"\n... (共{len(result.result)}字符按F3查看完整内容)"
yield Static(
f"结果: {result.result[:200]}...",
f"结果: {result_text}",
classes="tool-result"
)
def update_content(self, new_content: str) -> None:
"""更新消息内容"""
self._message.content = new_content
if self._content_widget:
self._content_widget.update(new_content)
def toggle_tool_details(self) -> None:
"""切换工具详情显示状态"""
if self._message.tool_calls:

View File

@ -15,22 +15,13 @@ class StatusBar(Static):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self._focus_indicator = "[Input]"
self._shortcuts = "F1:帮助 F2:侧边栏 F3:工具详情 F4:查询 F5:清屏 F6:退出"
self._license_info = "本项目由jianf设计以GPLv3形式开源"
def on_mount(self) -> None:
"""组件挂载时"""
self._update_display()
def update_focus(self, focus_name: str) -> None:
"""更新焦点指示器"""
self._focus_indicator = f"[{focus_name}]"
self._update_display()
def _update_display(self) -> None:
"""更新显示"""
self.update(f"{self._shortcuts} | 焦点: {self._focus_indicator}")
def get_focus(self) -> str:
"""获取当前焦点"""
return self._focus_indicator
self.update(f"{self._license_info} | {self._shortcuts}")