refactor: 优化插件HTML输出结构和样式,引入通用HTML包装器实现模块化。
This commit is contained in:
@@ -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]]],
|
||||
|
||||
Reference in New Issue
Block a user