feat: TUI 深度(文件树 + 会话列表 + Plan 状态栏,headless 自验)
This commit is contained in:
1 parent
d0915ab895
commit
addf119ac5
3 files changed
+367
-13
No files matched your search
@@ -35,6 +35,25 @@ class _MockMgr:
|
||||
]
|
||||
|
||||
|
||||
class _MockSession:
|
||||
def __init__(self, title, updated_at, sid):
|
||||
self.title = title
|
||||
self.updated_at = updated_at
|
||||
self.id = sid
|
||||
|
||||
|
||||
class _MockSessionManager:
|
||||
"""模拟 SessionManager.list_sessions,返回几个示例会话。"""
|
||||
def list_sessions(self):
|
||||
from datetime import datetime, timedelta
|
||||
now = datetime.now()
|
||||
return [
|
||||
_MockSession("修复内存泄漏", (now - timedelta(minutes=5)).isoformat(), "s1"),
|
||||
_MockSession("重构 CLI 入口", (now - timedelta(hours=2)).isoformat(), "s2"),
|
||||
_MockSession("接入 MCP 客户端", (now - timedelta(days=1)).isoformat(), "s3"),
|
||||
]
|
||||
|
||||
|
||||
class _MockCLI:
|
||||
def __init__(self):
|
||||
self.engines = {"openai": _MockEngine()}
|
||||
@@ -43,6 +62,10 @@ class _MockCLI:
|
||||
self.shared_conversation_history = []
|
||||
self.liugin_commands = {}
|
||||
self.tui_output_callback = None
|
||||
# TUI 深度:会话列表 / Plan 状态依赖
|
||||
self.session_manager = _MockSessionManager()
|
||||
self.plan_mode = False
|
||||
self.current_plan = ""
|
||||
|
||||
def get_current_engine_name(self):
|
||||
return "openai"
|
||||
@@ -69,6 +92,18 @@ def _txt(w):
|
||||
return str(getattr(w, "content", "") or getattr(w, "renderable", "") or "")
|
||||
|
||||
|
||||
async def _svg_to_png(svg_path, png_path, width=1280, height=720):
|
||||
"""用 Playwright Chromium 把 textual 导出的 SVG 渲染成 PNG(AI 肉眼核对用)。"""
|
||||
from playwright.async_api import async_playwright
|
||||
async with async_playwright() as p:
|
||||
browser = await p.chromium.launch()
|
||||
page = await browser.new_page(viewport={"width": width, "height": height})
|
||||
await page.goto("file://" + os.path.abspath(svg_path))
|
||||
await page.wait_for_timeout(400)
|
||||
await page.screenshot(path=png_path, full_page=True)
|
||||
await browser.close()
|
||||
|
||||
|
||||
async def run():
|
||||
app = XiaoliTUI(_MockCLI())
|
||||
async with app.run_test(size=(120, 64)) as pilot:
|
||||
@@ -107,6 +142,35 @@ async def run():
|
||||
for k, ok in checks.items():
|
||||
print(f"[{'PASS' if ok else 'FAIL'}] {k}")
|
||||
|
||||
# 3.5) 侧栏文件树 + 会话列表(TUI 深度)
|
||||
file_tree = app.query_one("#file-tree")
|
||||
session_list = app.query_one("#session-list")
|
||||
checks["文件树渲染"] = len(file_tree.children) > 1
|
||||
checks["会话列表渲染"] = len(session_list.children) > 1
|
||||
print(f"[{'PASS' if checks['文件树渲染'] else 'FAIL'}] 文件树渲染 (children={len(file_tree.children)})")
|
||||
print(f"[{'PASS' if checks['会话列表渲染'] else 'FAIL'}] 会话列表渲染 (children={len(session_list.children)})")
|
||||
|
||||
# 3.6) 进入 PLAN 模式,断言状态栏 + 侧栏同步
|
||||
ta.text = "/plan 重构核心模块"
|
||||
app.action_send_message()
|
||||
for _ in range(80):
|
||||
await pilot.pause(0.05)
|
||||
if not app.is_generating:
|
||||
break
|
||||
await pilot.pause(0.2)
|
||||
status_txt_plan = _txt(app.query_one("#status-bar"))
|
||||
sidebar_widget = app.query_one("#status-info")
|
||||
sidebar_txt_plan = "\n".join(_txt(w) for w in sidebar_widget.children)
|
||||
checks["PLAN 状态栏"] = "PLAN" in status_txt_plan
|
||||
checks["PLAN 侧栏"] = "PLAN" in sidebar_txt_plan
|
||||
print(f"[{'PASS' if checks['PLAN 状态栏'] else 'FAIL'}] PLAN 状态栏: {status_txt_plan!r}")
|
||||
print(f"[{'PASS' if checks['PLAN 侧栏'] else 'FAIL'}] PLAN 侧栏: {sidebar_txt_plan!r}")
|
||||
|
||||
# 3.7) 导出侧栏完整文本(文件树 / 会话列表 / 状态)供核对
|
||||
sidebar_full = "\n".join(_txt(w) for w in app.query_one("#sidebar").walk_children())
|
||||
with open(os.path.join(OUT_DIR, "real_demo_sidebar.txt"), "w", encoding="utf-8") as f:
|
||||
f.write(sidebar_full)
|
||||
|
||||
# 4) 导出 SVG 截图(AI/人都能看)
|
||||
os.makedirs(OUT_DIR, exist_ok=True)
|
||||
svg_path = os.path.join(OUT_DIR, "real_demo.svg")
|
||||
@@ -115,6 +179,14 @@ async def run():
|
||||
f.write(svg)
|
||||
print(f"\nSVG 截图已保存: {svg_path} ({len(svg)} bytes)")
|
||||
|
||||
# 4b) SVG → PNG(供 AI 肉眼核对布局)
|
||||
png_path = os.path.join(OUT_DIR, "real_demo.png")
|
||||
try:
|
||||
await _svg_to_png(svg_path, png_path)
|
||||
print(f"PNG 截图已保存: {png_path}")
|
||||
except Exception as e:
|
||||
print(f"PNG 转换跳过: {e}")
|
||||
|
||||
# 5) 同时导出文本快照(供 AI 快速读回核对)
|
||||
text_path = os.path.join(OUT_DIR, "real_demo.txt")
|
||||
with open(text_path, "w", encoding="utf-8") as f:
|
||||
|
||||
Reference in new issue
Block a user