refactor: 优化插件HTML输出结构和样式,引入通用HTML包装器实现模块化。

This commit is contained in:
fujie
2025-12-20 15:43:58 +08:00
parent 533a6dee5d
commit afaa25d441
8 changed files with 1348 additions and 510 deletions

View File

@@ -48,26 +48,52 @@ USER_PROMPT_TEMPLATE = """
{content}
"""
# 用于在聊天中渲染结果的 HTML 模板
HTML_TEMPLATE = """
# HTML 容器模板 (支持多插件共存与网格布局)
HTML_WRAPPER_TEMPLATE = """
<!-- OPENWEBUI_PLUGIN_OUTPUT -->
<!DOCTYPE html>
<html lang="{user_language}">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>[插件标题]</title>
<style>
/* 在此处添加 CSS 样式 */
body { font-family: sans-serif; padding: 20px; }
.container { border: 1px solid #ccc; padding: 20px; border-radius: 8px; }
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
margin: 0;
padding: 10px;
background-color: transparent;
}
#main-container {
display: flex;
flex-wrap: wrap;
gap: 20px;
align-items: flex-start;
width: 100%;
}
.plugin-item {
flex: 1 1 400px; /* 默认宽度,允许伸缩 */
min-width: 300px;
background: white;
border-radius: 12px;
box-shadow: 0 4px 6px rgba(0,0,0,0.05);
overflow: hidden;
border: 1px solid #e5e7eb;
transition: all 0.3s ease;
}
.plugin-item:hover {
box-shadow: 0 10px 15px rgba(0,0,0,0.1);
}
@media (max-width: 768px) {
.plugin-item { flex: 1 1 100%; }
}
/* STYLES_INSERTION_POINT */
</style>
</head>
<body>
<div class="container">
<h1>[结果标题]</h1>
<div id="content">{result_content}</div>
<div id="main-container">
<!-- CONTENT_INSERTION_POINT -->
</div>
<!-- SCRIPTS_INSERTION_POINT -->
</body>
</html>
"""
@@ -89,7 +115,7 @@ class Action:
)
CLEAR_PREVIOUS_HTML: bool = Field(
default=False,
description="是否在追加新结果前清除消息中已有的插件生成 HTML 内容 (通过标记识别)",
description="是否强制清除旧的插件结果(如果为 True则不合并直接覆盖",
)
# 根据需要添加其他配置字段
# MAX_TEXT_LENGTH: int = Field(default=2000, description="...")
@@ -121,11 +147,21 @@ class Action:
except Exception:
now = datetime.now()
weekday_map = {
"Monday": "星期一",
"Tuesday": "星期二",
"Wednesday": "星期三",
"Thursday": "星期四",
"Friday": "星期五",
"Saturday": "星期六",
"Sunday": "星期日",
}
weekday_en = now.strftime("%A")
return {
"current_date_time_str": now.strftime("%Y-%m-%d %H:%M:%S"),
"current_weekday": now.strftime("%A"),
"current_date_time_str": now.strftime("%Y%m%d %H:%M:%S"),
"current_weekday": weekday_map.get(weekday_en, weekday_en),
"current_year": now.strftime("%Y"),
"current_timezone_str": str(now.tzinfo) if now.tzinfo else "Unknown",
"current_timezone_str": str(now.tzinfo) if now.tzinfo else "未知时区",
}
def _process_llm_output(self, llm_output: str) -> Any:
@@ -150,6 +186,55 @@ class Action:
pattern = r"```html\s*<!-- OPENWEBUI_PLUGIN_OUTPUT -->[\s\S]*?```"
return re.sub(pattern, "", content).strip()
def _merge_html(
self,
existing_html_code: str,
new_content: str,
new_styles: str = "",
new_scripts: str = "",
user_language: str = "zh-CN",
) -> str:
"""
将新内容合并到现有的 HTML 容器中,或者创建一个新的容器。
"""
# 检查是否存在兼容的容器标记
if (
"<!-- OPENWEBUI_PLUGIN_OUTPUT -->" in existing_html_code
and "<!-- CONTENT_INSERTION_POINT -->" in existing_html_code
):
base_html = existing_html_code
# 移除代码块标记 ```html ... ``` 以便处理
base_html = re.sub(r"^```html\s*", "", base_html)
base_html = re.sub(r"\s*```$", "", base_html)
else:
# 初始化新容器
base_html = HTML_WRAPPER_TEMPLATE.replace("{user_language}", user_language)
# 包装新内容
wrapped_content = f'<div class="plugin-item">\n{new_content}\n</div>'
# 注入样式
if new_styles:
base_html = base_html.replace(
"/* STYLES_INSERTION_POINT */",
f"{new_styles}\n/* STYLES_INSERTION_POINT */",
)
# 注入内容
base_html = base_html.replace(
"<!-- CONTENT_INSERTION_POINT -->",
f"{wrapped_content}\n<!-- CONTENT_INSERTION_POINT -->",
)
# 注入脚本
if new_scripts:
base_html = base_html.replace(
"<!-- SCRIPTS_INSERTION_POINT -->",
f"{new_scripts}\n<!-- SCRIPTS_INSERTION_POINT -->",
)
return base_html.strip()
async def _emit_status(
self,
emitter: Optional[Callable[[Any], Awaitable[None]]],