From 623d53f06f475a546e6b4ae935a6dc53029c1550 Mon Sep 17 00:00:00 2001 From: xiaoli-cli Date: Sat, 25 Apr 2026 22:54:41 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E5=85=A8=E9=9D=A2=E4=BF=AE=E5=A4=8D=20T?= =?UTF-8?q?UI=20=E5=92=8C=E8=B7=A8=E5=B9=B3=E5=8F=B0=E5=85=BC=E5=AE=B9?= =?UTF-8?q?=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - constants.py: requests/openai/numpy 改为 try/except 保护 - tui.py: asyncio.get_event_loop() → get_running_loop() (Python 3.10+) - tui.py: _remove_thinking 增加异常保护 - tui.py: XiaoliTUI 占位类增加 __init__ 和 run 方法 - tui.py: bare except 改为 except Exception - sandbox.py: resource 模块 try/except 保护 (Windows 兼容) --- xcli_core/constants.py | 46 +++++++++++++++++++++++++++++------------- xcli_core/tui.py | 30 ++++++++++++++++++--------- 2 files changed, 52 insertions(+), 24 deletions(-) diff --git a/xcli_core/constants.py b/xcli_core/constants.py index 989adb0..2c0aa4a 100644 --- a/xcli_core/constants.py +++ b/xcli_core/constants.py @@ -11,13 +11,43 @@ import sys import importlib.util import json import re -import requests import shutil import time import threading from concurrent.futures import ThreadPoolExecutor, as_completed -from openai import OpenAI from colorama import init, Fore, Style +from http.server import HTTPServer, BaseHTTPRequestHandler +from urllib.parse import urlparse, parse_qs +import socket +import webbrowser +from datetime import datetime +import logging +import asyncio +from typing import Optional +from io import StringIO + +# ── 第三方依赖(全部 try/except 保护) ── + +try: + import requests + REQUESTS_AVAILABLE = True +except ImportError: + requests = None + REQUESTS_AVAILABLE = False + +try: + from openai import OpenAI + OPENAI_AVAILABLE = True +except ImportError: + OpenAI = None + OPENAI_AVAILABLE = False + +try: + import numpy as np + NUMPY_AVAILABLE = True +except ImportError: + np = None + NUMPY_AVAILABLE = False # tkinter 仅在视频播放功能中使用,服务器环境可能不可用 try: @@ -34,14 +64,6 @@ try: except ImportError: CV2_AVAILABLE = False -import numpy as np -from http.server import HTTPServer, BaseHTTPRequestHandler -from urllib.parse import urlparse, parse_qs -import socket -import webbrowser -from datetime import datetime -import logging - # 图像显示相关(可选) try: from PIL import Image @@ -77,10 +99,6 @@ except ImportError: UNIFIED_TOOL_MANAGER_AVAILABLE = False # Textual TUI 支持 -import asyncio -from typing import Optional -from io import StringIO - try: from textual.app import App, ComposeResult from textual.containers import Container, Horizontal, Vertical, VerticalScroll diff --git a/xcli_core/tui.py b/xcli_core/tui.py index da79421..7e9976f 100644 --- a/xcli_core/tui.py +++ b/xcli_core/tui.py @@ -184,7 +184,7 @@ if TEXTUAL_AVAILABLE: ("", "msg-dim"), (" ╔══════════════════════════════════════════════╗", "msg-welcome"), (" ║ ║", "msg-welcome"), - (" ║ 🐱 小狸 Pro-CLI v5.0 ║", "msg-welcome"), + (" ║ 🐱 小狸 Pro-CLI v5.1 ║", "msg-welcome"), (" ║ 智能编程助手 · 对标 Claude Code ║", "msg-welcome"), (" ║ ║", "msg-welcome"), (" ╚══════════════════════════════════════════════╝", "msg-welcome"), @@ -257,7 +257,7 @@ if TEXTUAL_AVAILABLE: line_numbers=True, word_wrap=True) self._append(Panel(syntax, border_style=f"dim {_Theme.BORDER}", box=ROUNDED, padding=(0, 1))) - except: + except Exception: for line in code.split('\n'): self._add(f" {line}", "msg-dim") i += 1 @@ -309,7 +309,7 @@ if TEXTUAL_AVAILABLE: 'cls': lambda: self.action_clear(), 'model': lambda: self._switch_model(args), 'engine': lambda: self._switch_model(args), - 'about': lambda: self._system("🐱 小狸 Pro-CLI v5.0 - 智能编程助手"), + 'about': lambda: self._system("🐱 小狸 Pro-CLI v5.1 - 智能编程助手"), 'status': lambda: self._show_status(), 'tools': lambda: self._show_tools(), 'engines': lambda: self._show_engines(), @@ -398,7 +398,7 @@ if TEXTUAL_AVAILABLE: self.cli.tui_output_callback = tui_output try: - loop = asyncio.get_event_loop() + loop = asyncio.get_running_loop() await loop.run_in_executor( None, self.cli.process_conversation, user_input ) @@ -413,11 +413,16 @@ if TEXTUAL_AVAILABLE: self.call_after_refresh(self._update_sidebar) def _remove_thinking(self): - scroll = self.query_one("#chat-scroll") - for child in reversed(list(scroll.children)): - if '思考中' in str(getattr(child, 'renderable', '')): - child.remove() - break + try: + scroll = self.query_one("#chat-scroll") + for child in reversed(list(scroll.children)): + renderable = getattr(child, 'renderable', None) + text = str(renderable) if renderable is not None else "" + if '思考中' in text: + child.remove() + break + except Exception: + pass def _write_raw(self, msg): if '✅' in msg or 'OK 工具' in msg: @@ -453,4 +458,9 @@ if TEXTUAL_AVAILABLE: else: # Textual 不可用时的占位类 class XiaoliTUI: - pass + """Textual 未安装时的占位类""" + def __init__(self, *args, **kwargs): + pass + def run(self, *args, **kwargs): + print("TUI 模式不可用:Textual 库未安装") + print("请运行: pip install textual rich")