Files
zhenxun_bot/zhenxun/builtin_plugins/superuser/ui_manager.py
5e30694663 ♻️ refactor(ui): 重构 UI 渲染系统并优化资源管理 (#2094)
* ♻️ refactor(ui): 重构 UI 渲染系统并优化资源管理

- 【重构】重构 `RendererService` 架构,解耦模板引擎、主题管理与截图引擎
- 【重构】重构 `ui` 模块,采用组件注册机制与数据模型驱动,移除旧版 `builders`
- 【功能】新增 UI 热重载模式,支持在不重启的情况下实时预览 HTML/CSS 修改
- 【功能】增强启动项资源检查,支持基于 `resources.spec` 的版本校验与自动更新
- 【优化】统一内置插件的 UI 渲染逻辑,迁移至新的 `ui.table`、`ui.markdown` 等工厂接口
- 【优化】优化日志脱敏工具,支持自动折叠调试输出中冗长的样式标签
- 【优化】引入 `AssetResolutionService`,完善皮肤、组件、主题间的多级资源回退机制
- 【优化】新增组件生命周期钩子 `prepare`,支持渲染前的异步数据预处理

* 🚨 auto fix by pre-commit hooks

---------

Co-authored-by: webjoin111 <455457521@qq.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2026-02-06 20:13:40 +08:00

152 lines
5.2 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
from nonebot.permission import SUPERUSER
from nonebot.plugin import PluginMetadata
from nonebot.rule import to_me
from nonebot_plugin_alconna import (
Alconna,
AlconnaMatch,
Args,
Arparma,
Match,
Subcommand,
on_alconna,
)
from zhenxun.configs.config import Config
from zhenxun.configs.utils import PluginExtraData, RegisterConfig
from zhenxun.services import renderer_service
from zhenxun.services.log import logger
from zhenxun.utils.enum import PluginType
from zhenxun.utils.message import MessageUtils
__plugin_meta__ = PluginMetadata(
name="UI管理",
description="管理UI、主题和渲染服务的相关配置",
usage="""
指令:
ui reload / 重载主题: 重新加载当前主题的配置和资源。
ui theme / 主题列表: 显示所有可用的主题,并高亮显示当前主题。
ui theme [主题名称] / 切换主题 [主题名称]: 将UI主题切换为指定主题。
""".strip(),
extra=PluginExtraData(
author="HibiKier",
version="0.1",
plugin_type=PluginType.SUPERUSER,
configs=[
RegisterConfig(
module="UI",
key="THEME",
value="default",
help="设置渲染服务使用的全局主题名称(对应 resources/themes/下的目录名)",
default_value="default",
type=str,
),
RegisterConfig(
module="UI",
key="CACHE",
value=True,
help="是否为渲染服务生成的图片启用文件缓存",
default_value=True,
type=bool,
),
RegisterConfig(
module="UI",
key="DEBUG_MODE",
value=False,
help="是否在日志中输出渲染组件的完整HTML源码,用于调试",
default_value=False,
type=bool,
),
RegisterConfig(
module="UI",
key="HOT_RELOAD",
value=False,
help="是否开启UI热重载模式 (修改HTML/CSS后立即生效,"
"性能较低,仅建议开发时开启)",
default_value=False,
type=bool,
),
],
).to_dict(),
)
ui_matcher = on_alconna(
Alconna(
"ui",
Subcommand("reload", help_text="重载当前主题"),
Subcommand("theme", Args["theme_name?", str], help_text="查看或切换主题"),
),
aliases={"主题管理"},
rule=to_me(),
permission=SUPERUSER,
priority=1,
block=True,
)
ui_matcher.shortcut("重载主题", command="ui reload")
ui_matcher.shortcut("主题列表", command="ui theme")
ui_matcher.shortcut("切换主题", command="ui theme", arguments=["{%0}"])
@ui_matcher.assign("reload")
async def handle_reload(arparma: Arparma):
theme_name = await renderer_service.reload_theme()
logger.info(
f"UI主题已重载为: {theme_name}", "UI管理器", session=arparma.header_result
)
await MessageUtils.build_message(f"UI主题已成功重载为 '{theme_name}'!").send(
reply_to=True
)
@ui_matcher.assign("theme")
async def handle_theme(
arparma: Arparma, theme_name_match: Match[str] = AlconnaMatch("theme_name")
):
if theme_name_match.available:
new_theme_name = theme_name_match.result
try:
await renderer_service.switch_theme(new_theme_name)
logger.info(
f"UI主题已切换为: {new_theme_name}",
"UI管理器",
session=arparma.header_result,
)
await MessageUtils.build_message(
f"🎨 主题已成功切换为 '{new_theme_name}'!"
).send(reply_to=True)
except FileNotFoundError as e:
logger.warning(
f"尝试切换到不存在的主题: {new_theme_name}",
"UI管理器",
session=arparma.header_result,
)
await MessageUtils.build_message(str(e)).send(reply_to=True)
except Exception as e:
logger.error(
f"切换主题时发生错误: {e}",
"UI管理器",
session=arparma.header_result,
e=e,
)
await MessageUtils.build_message(f"切换主题失败: {e}").send(reply_to=True)
else:
try:
available_themes = renderer_service.list_available_themes()
current_theme = Config.get_config("UI", "THEME", "default")
theme_list_str = "\n".join(
f" - {theme}{' <- 当前' if theme == current_theme else ''}"
for theme in sorted(available_themes)
)
response = f"🎨 可用主题列表:\n{theme_list_str}"
await MessageUtils.build_message(response).send(reply_to=True)
except Exception as e:
logger.error(
f"获取主题列表时发生错误: {e}",
"UI管理器",
session=arparma.header_result,
e=e,
)
await MessageUtils.build_message("获取主题列表失败。").send(reply_to=True)