import os import sys import threading import time from pathlib import Path from oss.logger.logger import Log from oss.plugin.types import Plugin, Response, register_plugin_type from oss.config import get_config from oss.tui.converter import TUIManager, TUIRenderer, HTMLToTUIConverter class TUIPlugin(Plugin): self.webui = webui def set_http_api(self, http_api): Log.info("tui", "TUI 插件初始化中...") config = get_config() width = config.get("TUI_WIDTH", 80) height = config.get("TUI_HEIGHT", 24) self.tui_manager = TUIManager.get_instance(width, height) if self.http_api and self.http_api.router: self.http_api.router.get("/tui/index.html", self._handle_tui_index) self.http_api.router.get("/tui/page", self._handle_tui_page) self.http_api.router.get("/tui/css", self._handle_tui_css) self.http_api.router.get("/tui/js", self._handle_tui_js) self.http_api.router.post("/tui/interact", self._handle_tui_interact) self.http_api.router.get("/tui/pages", self._handle_tui_pages) Log.ok("tui", "已注册 TUI API 路由 (/tui/*)") else: Log.warn("tui", "警告:未找到 http-api 依赖") self._load_default_pages() Log.ok("tui", "TUI 插件初始化完成 - 强大的转换层已就绪") def _load_default_pages(self): 此方法模拟访问 WebUI 页面并获取 HTML,然后由 TUI 转换层解析。 WebUI 开放的 /tui 接口会返回带有特殊标记的 HTML,不含用户可见内容, 但包含 data-tui-* 属性和 script[type='application/x-tui-*'] 配置。 if not self.webui or not hasattr(self.webui, 'server'): return "" try: from oss.plugin.types import Request request = Request(method="GET", path=path, headers={}, body="") router = self.webui.server.router if hasattr(router, 'routes'): for route_path, handler in router.routes.items(): if route_path == path or (route_path.endswith('*') and path.startswith(route_path[:-1])): response = handler(request) if response and hasattr(response, 'body'): return response.body.decode('utf-8') if isinstance(response.body, bytes) else response.body except Exception as e: Log.debug("tui", f"获取 WebUI 页面失败:{e}") return "" def start(self): try: self._show_welcome() self._event_loop() except Exception as e: Log.error("tui", f"TUI 循环异常:{e}") finally: self.running = False def _show_welcome(self): NebulaShell TUI

👋 欢迎使用 NebulaShell TUI

终端用户界面已启动

WebUI 同时运行在:http://localhost:8080

self.tui_manager.load_page("/welcome", welcome_html) self._render_current("/welcome") def _render_current(self, path: str = None): import sys import tty import termios fd = sys.stdin.fileno() old_settings = termios.tcgetattr(fd) try: tty.setraw(fd) while self.running: char = sys.stdin.read(1) if char == '\x03': break elif char == '\x04': break elif char == 'q': Log.info("tui", "用户退出 TUI") break elif char == '1': self._render_current("/") elif char == '2': self._render_current("/dashboard") elif char == '3': self._render_current("/logs") elif char == '4': self._render_current("/terminal") elif char == '5': self._render_current("/plugins") elif char == 'r': self._load_default_pages() self._render_current() elif char == '\n' or char == '\r': self._render_current() except Exception as e: Log.error("tui", f"事件循环错误:{e}") finally: termios.tcsetattr(fd, termios.TCSADRAIN, old_settings) def _handle_tui_index(self, request): html = return Response( status=200, headers={"Content-Type": "text/html; charset=utf-8"}, body=html ) def _handle_tui_page(self, request): from urllib.parse import parse_qs, urlparse parsed = urlparse(request.path) params = parse_qs(parsed.query) page_path = params.get('path', ['/'])[0] html = self._fetch_webui_page(page_path) if html: html = html.replace('', '') return Response( status=200, headers={"Content-Type": "text/html; charset=utf-8"}, body=html ) else: error_html = return Response( status=404, headers={"Content-Type": "text/html; charset=utf-8"}, body=error_html ) def _handle_tui_css(self, request): css = // TUI JS 模拟配置 // 仅支持基础交互功能 const TUI = { // 鼠标支持 mouse: { enabled: true, getPosition: () => ({ x: 0, y: 0 }), onClick: (handler) => {}, }, // 键盘支持 keyboard: { enabled: true, onKeyPress: (handler) => {}, bindings: {}, }, // DOM 操作(简化版) querySelector: (selector) => null, querySelectorAll: (selector) => [], // 事件系统 addEventListener: (event, handler) => {}, removeEventListener: (event, handler) => {}, }; // 导出配置 export default TUI; return Response( status=200, headers={"Content-Type": "application/javascript"}, body=js_config ) def _handle_tui_interact(self, request): import json pages = [] if self.webui and hasattr(self.webui, 'server'): router = self.webui.server.router if hasattr(router, 'routes'): pages = list(router.routes.keys()) return Response( status=200, headers={"Content-Type": "application/json"}, body=json.dumps({ 'success': True, 'pages': pages, 'current': self.tui_manager.current_page if self.tui_manager else None }) ) def wait_for_exit(self): Log.info("tui", "TUI 停止中...") self.running = False if self.tui_thread: self.tui_thread.join(timeout=2) Log.ok("tui", "TUI 已停止") register_plugin_type("TUIPlugin", TUIPlugin) def New(): return TUIPlugin()