diff --git a/plugins/robot_menu/config.toml b/plugins/robot_menu/config.toml
index 71644f8..0e08ac3 100644
--- a/plugins/robot_menu/config.toml
+++ b/plugins/robot_menu/config.toml
@@ -12,6 +12,9 @@ image_fallback_to_text = false
# md2image 渲染参数:可按服务器性能调整
image_render_timeout_seconds = 45
image_render_retries = 1
+# 菜单图片 HTML 模板路径(相对仓库根目录)
+# 后续 UI 改版只需要改模板文件,无需改 Python 代码
+image_template_path = "plugins/robot_menu/templates/menu_cards.html"
command-format = """
📝功能菜单指令:
菜单 - 显示功能菜单
diff --git a/plugins/robot_menu/main.py b/plugins/robot_menu/main.py
index 3d52b6e..1a75b33 100644
--- a/plugins/robot_menu/main.py
+++ b/plugins/robot_menu/main.py
@@ -87,12 +87,21 @@ class RobotMenuPlugin(MessagePluginInterface):
self.image_render_retries = int(
self._config.get("RobotMenu", {}).get("image_render_retries", 1)
)
+ # 菜单图片模板文件路径(相对仓库根目录):
+ # 调整样式和布局时只改模板,不改 Python 逻辑。
+ self.image_template_path = str(
+ self._config.get("RobotMenu", {}).get(
+ "image_template_path",
+ "plugins/robot_menu/templates/menu_cards.html",
+ )
+ ).strip() or "plugins/robot_menu/templates/menu_cards.html"
# 初始化“菜单渲染工具”,后续菜单图片与文本发送统一由该工具负责。
self.menu_renderer = RobotMenuRenderTool(
output_mode=output_mode,
image_fallback_to_text=self.image_fallback_to_text,
image_render_timeout_seconds=self.image_render_timeout_seconds,
image_render_retries=self.image_render_retries,
+ image_template_path=self.image_template_path,
log=self.LOG,
)
diff --git a/plugins/robot_menu/menu_render_tool.py b/plugins/robot_menu/menu_render_tool.py
index 9b3aea1..bdb6b0e 100644
--- a/plugins/robot_menu/menu_render_tool.py
+++ b/plugins/robot_menu/menu_render_tool.py
@@ -1,5 +1,4 @@
import asyncio
-import html
import os
import re
import time
@@ -11,6 +10,7 @@ from loguru import logger as default_logger
from utils.markdown_to_image import convert_md_str_to_image, html_to_image
from utils.revoke.message_auto_revoke import MessageAutoRevoke
from utils.robot_cmd.robot_command import Feature, GroupBotManager, PermissionStatus
+from utils.html_template_renderer import HtmlTemplateRenderer
from wechat_ipad import WechatAPIClient
@@ -29,6 +29,7 @@ class RobotMenuRenderTool:
image_fallback_to_text: bool,
image_render_timeout_seconds: int,
image_render_retries: int,
+ image_template_path: str,
log=default_logger,
):
# 输出模式:支持 text / image,非法值自动归一化为 text。
@@ -40,6 +41,9 @@ class RobotMenuRenderTool:
self.image_render_retries = int(image_render_retries)
# 注入日志对象,便于主插件统一控制日志风格与输出目标。
self.log = log or default_logger
+ # 菜单图片模板路径(相对仓库根目录),支持仅改模板文件完成 UI 更新。
+ self.image_template_path = str(image_template_path or "").strip() or "plugins/robot_menu/templates/menu_cards.html"
+ self.template_renderer = HtmlTemplateRenderer()
@staticmethod
def normalize_output_mode(raw_mode: Any) -> str:
@@ -132,268 +136,58 @@ class RobotMenuRenderTool:
lines.append("")
return "\n".join(lines)
- def _get_feature_command_examples(self, feature: Feature) -> str:
- """返回单个功能在卡片中的紧凑使用说明(不包含管理员内容)。"""
+ def _build_feature_command_payload(self, feature: Feature) -> dict:
+ """构建功能卡片中的命令展示数据。"""
_, cmd_hint = self._split_feature_description(feature.description)
tokens = self._extract_command_tokens(cmd_hint)
if not tokens:
- return "自动/定时触发,无需发送命令"
-
- rows = [f"指令: {command_examples}{html.escape(tokens[0])}"]
- if len(tokens) > 1:
- alias_text = " / ".join([f"{html.escape(t)}" for t in tokens[1:4]])
- rows.append(f"别名:{alias_text}")
- return "
".join(rows)
+ return {
+ "is_auto": True,
+ "primary": "",
+ "aliases": [],
+ "tip": "自动/定时触发,无需发送命令",
+ }
+ return {
+ "is_auto": False,
+ "primary": tokens[0],
+ "aliases": tokens[1:4],
+ "tip": "",
+ }
def build_feature_status_html(self, group_id: str) -> str:
- """构建 PlayStation 风格菜单 HTML(黑-白-蓝三段式)。"""
- # 设计实现说明(基于 DESIGN-playstation.md):
- # 1. 三段式表面:黑色英雄区 -> 白色内容区 -> 蓝色底部;
- # 2. 主色锚点固定为 PlayStation Blue #0070cc,交互强调色为 #1eaedb;
- # 3. 组件采用圆角胶囊/卡片体系,保持信息密度与可读性平衡。
+ """基于外部 HTML 模板构建菜单页面。"""
user_features = self._iter_user_command_features()
feature_cards = []
enabled_count = 0
for feature in user_features:
title, _ = self._split_feature_description(feature.description)
- command_examples = self._get_feature_command_examples(feature)
status = GroupBotManager.get_group_permission(group_id, feature)
is_enabled = status == PermissionStatus.ENABLED
if is_enabled:
enabled_count += 1
- status_text = "开启" if is_enabled else "关闭"
- status_class = "status-on" if is_enabled else "status-off"
+ command_payload = self._build_feature_command_payload(feature)
feature_cards.append(
- f"""
-
用户功能查看中心 · 直达命令与状态一屏可见
- -用户功能查看中心 · 直达命令与状态一屏可见
+ +{{ item.command.tip }}
+ {% else %} +
+ 指令:{{ item.command.primary }}
+ {% if item.command.aliases %}
+
+ 别名:
+ {% for alias in item.command.aliases %}
+ {{ alias }}{% if not loop.last %} / {% endif %}
+ {% endfor %}
+
+ {% endif %}
+