群总结模板升级:新增Gemini风格卡片并优化Markdown富标签渲染
变更项: 1. 新增 templates/gemini_summary_card.html,按 Gemini 风格实现移动卡片化总结模板。 2. message_summary 渲染链路升级:支持更完整的 Markdown 富标签转 HTML(标题/列表/表格/代码块/引用等)。 3. 增加渲染后 HTML 安全过滤,清理 script/iframe/on* 事件与 javascript: 链接。 4. 增加 markdown 依赖缺失时的轻量回退解析,保证插件在最小环境下可用。 5. 默认输出配置切换为 template,并指向新 Gemini 风格模板。
This commit is contained in:
@@ -15,6 +15,6 @@ image_format = "png"
|
||||
# 图片渲染模式:
|
||||
# - template: 使用 HTML 模板渲染(模板样式稳定后再切换)
|
||||
# - markdown: 使用历史 md2image 样式
|
||||
summary_image_mode = "markdown"
|
||||
summary_image_mode = "template"
|
||||
# 总结卡片模板路径(相对项目根目录)
|
||||
summary_image_template_path = "plugins/message_summary/templates/summary_card.html"
|
||||
summary_image_template_path = "plugins/message_summary/templates/gemini_summary_card.html"
|
||||
|
||||
@@ -9,6 +9,11 @@ from typing import Dict, Any, Tuple, Optional, List
|
||||
|
||||
from loguru import logger
|
||||
from markupsafe import Markup
|
||||
try:
|
||||
# 优先使用 markdown 库做完整渲染(支持表格、代码块等)。
|
||||
import markdown as markdown_lib
|
||||
except Exception:
|
||||
markdown_lib = None
|
||||
|
||||
from base.plugin_common.message_plugin_interface import MessagePluginInterface
|
||||
from base.plugin_common.plugin_interface import PluginStatus
|
||||
@@ -410,11 +415,76 @@ class MessageSummaryPlugin(MessagePluginInterface):
|
||||
return cleaned
|
||||
|
||||
@staticmethod
|
||||
def _summary_markdown_to_html(summary_text: str) -> str:
|
||||
"""把总结 Markdown 转为基础 HTML 片段(模板内部展示用)。"""
|
||||
# 这里不依赖第三方 markdown 库,保证在最小运行环境也能稳定渲染。
|
||||
# 规则按“标题/列表/段落”三类做轻量转换,足够覆盖总结文本场景。
|
||||
lines = str(summary_text or "").splitlines()
|
||||
def _sanitize_rendered_html(rendered_html: str) -> str:
|
||||
"""对渲染后的 HTML 做最小安全过滤。
|
||||
|
||||
安全策略:
|
||||
1. 移除 script/style/iframe 等高风险标签,避免模板渲染执行脚本;
|
||||
2. 清除行内事件属性(onload/onerror/onclick...);
|
||||
3. 禁止 javascript: 协议链接。
|
||||
|
||||
说明:
|
||||
- 这里是“轻量过滤”,目标是平衡安全与展示完整度;
|
||||
- 若后续需要更严格过滤,可接入专门的 HTML Sanitizer。
|
||||
"""
|
||||
safe_html = str(rendered_html or "")
|
||||
# 删除高风险标签及其内容。
|
||||
safe_html = re.sub(
|
||||
r"<\s*(script|style|iframe|object|embed|form|link|meta)\b[^>]*>.*?<\s*/\s*\1\s*>",
|
||||
"",
|
||||
safe_html,
|
||||
flags=re.IGNORECASE | re.DOTALL,
|
||||
)
|
||||
# 删除自闭合高风险标签。
|
||||
safe_html = re.sub(
|
||||
r"<\s*(script|style|iframe|object|embed|form|link|meta)\b[^>]*/\s*>",
|
||||
"",
|
||||
safe_html,
|
||||
flags=re.IGNORECASE | re.DOTALL,
|
||||
)
|
||||
# 删除行内事件处理器属性。
|
||||
safe_html = re.sub(r"\son[a-zA-Z]+\s*=\s*(['\"]).*?\1", "", safe_html, flags=re.IGNORECASE | re.DOTALL)
|
||||
# 阻断 javascript: 链接。
|
||||
safe_html = re.sub(
|
||||
r"""(href|src)\s*=\s*(['"])\s*javascript:[^'"]*\2""",
|
||||
r'\1=\2#\2',
|
||||
safe_html,
|
||||
flags=re.IGNORECASE,
|
||||
)
|
||||
return safe_html
|
||||
|
||||
@classmethod
|
||||
def _summary_markdown_to_html(cls, summary_text: str) -> str:
|
||||
"""把总结 Markdown 转为 HTML 片段(模板内部展示用)。
|
||||
|
||||
升级点:
|
||||
1. 使用 markdown 库完整支持标题、列表、粗斜体、引用、代码块、表格等结构;
|
||||
2. 对 LLM 输出里常见的富标签 markdown(例如 ```、|表格|、> 引用)效果更好;
|
||||
3. 渲染后做一次轻量安全过滤,避免模板内注入脚本。
|
||||
"""
|
||||
text = str(summary_text or "").strip()
|
||||
if not text:
|
||||
return "<p>暂无总结内容。</p>"
|
||||
|
||||
# 兼容处理:
|
||||
# 1. 环境安装了 markdown 库时,走完整渲染;
|
||||
# 2. 未安装时自动降级到内置轻量转换,避免插件启动失败。
|
||||
if markdown_lib is not None:
|
||||
rendered = markdown_lib.markdown(
|
||||
text,
|
||||
extensions=[
|
||||
"extra", # 综合扩展:支持表格、定义列表等
|
||||
"fenced_code", # 支持 ``` 代码块
|
||||
"tables", # 支持 Markdown 表格
|
||||
"sane_lists", # 更稳定的列表解析
|
||||
"nl2br", # 保留换行,提升聊天总结可读性
|
||||
],
|
||||
output_format="html5",
|
||||
)
|
||||
return cls._sanitize_rendered_html(rendered)
|
||||
|
||||
# 轻量回退实现(兼容无 markdown 三方包的运行环境)。
|
||||
lines = text.splitlines()
|
||||
html_parts: List[str] = []
|
||||
list_items: List[str] = []
|
||||
|
||||
@@ -443,7 +513,8 @@ class MessageSummaryPlugin(MessagePluginInterface):
|
||||
flush_list()
|
||||
html_parts.append(f"<p>{html.escape(line)}</p>")
|
||||
flush_list()
|
||||
return "".join(html_parts)
|
||||
rendered = "".join(html_parts)
|
||||
return cls._sanitize_rendered_html(rendered)
|
||||
|
||||
def _render_summary_template_html(self, group_name: str, summary_text: str) -> str:
|
||||
"""根据模板路径渲染总结图片 HTML。"""
|
||||
|
||||
199
plugins/message_summary/templates/gemini-code-1776907723749.html
Normal file
199
plugins/message_summary/templates/gemini-code-1776907723749.html
Normal file
@@ -0,0 +1,199 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Group_Digest_Mobile_V4</title>
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||
<style>
|
||||
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700&family=JetBrains+Mono:wght@500&display=swap');
|
||||
body {
|
||||
background-color: #f8fafc;
|
||||
color: #334155;
|
||||
font-family: 'Inter', 'PingFang SC', -apple-system, sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
.report-container {
|
||||
width: 420px;
|
||||
margin: 0 auto;
|
||||
background: #ffffff;
|
||||
box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
.label-tiny {
|
||||
font-size: 9px;
|
||||
font-weight: 800;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.08em;
|
||||
color: #94a3b8;
|
||||
margin-bottom: 2px;
|
||||
display: block;
|
||||
}
|
||||
.card-inner {
|
||||
border: 1px solid #f1f5f9;
|
||||
border-radius: 4px;
|
||||
padding: 10px;
|
||||
}
|
||||
.mono { font-family: 'JetBrains Mono', monospace; }
|
||||
.conclusion-area {
|
||||
background-color: #f0fdf4;
|
||||
border-left: 2px solid #22c55e;
|
||||
padding: 8px;
|
||||
border-radius: 0 4px 4px 0;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="py-6">
|
||||
|
||||
<div class="report-container">
|
||||
|
||||
<header class="p-5 border-b border-slate-100">
|
||||
<div class="flex justify-between items-start mb-4">
|
||||
<div>
|
||||
<h1 class="text-base font-black text-slate-900 tracking-tight">CHAT INSIGHTS <span class="text-blue-600 font-medium">REPORT</span></h1>
|
||||
<p class="text-[10px] text-slate-400 font-medium mono uppercase">ID: 20260423-L-OPEN</p>
|
||||
</div>
|
||||
<div class="px-2 py-1 bg-slate-100 rounded text-[9px] font-bold text-slate-500 uppercase">
|
||||
Daily Archive
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-4 gap-2 text-center">
|
||||
<div class="bg-slate-50 py-2 rounded">
|
||||
<span class="label-tiny">Msgs</span>
|
||||
<span class="text-xs font-bold text-slate-700">1,402</span>
|
||||
</div>
|
||||
<div class="bg-slate-50 py-2 rounded">
|
||||
<span class="label-tiny">Links</span>
|
||||
<span class="text-xs font-bold text-blue-600">12</span>
|
||||
</div>
|
||||
<div class="bg-slate-50 py-2 rounded">
|
||||
<span class="label-tiny">Files</span>
|
||||
<span class="text-xs font-bold text-purple-600">5</span>
|
||||
</div>
|
||||
<div class="bg-slate-50 py-2 rounded">
|
||||
<span class="label-tiny">Alerts</span>
|
||||
<span class="text-xs font-bold text-rose-500">3</span>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="p-5 border-b border-slate-50">
|
||||
<span class="label-tiny mb-3"># Personal Interest Radar</span>
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<span class="text-[10px] px-2 py-0.5 bg-blue-50 text-blue-600 border border-blue-100 rounded font-bold italic">Gemma 4</span>
|
||||
<span class="text-[10px] px-2 py-0.5 bg-indigo-50 text-indigo-600 border border-indigo-100 rounded font-bold italic">RTX 5090</span>
|
||||
<span class="text-[10px] px-2 py-0.5 bg-slate-100 text-slate-500 rounded">Playwright</span>
|
||||
<span class="text-[10px] px-2 py-0.5 bg-emerald-50 text-emerald-600 border border-emerald-100 rounded font-bold italic">Nasdaq 100</span>
|
||||
<span class="text-[10px] px-2 py-0.5 bg-slate-100 text-slate-500 rounded">frp Tunnel</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="p-5 space-y-6">
|
||||
<span class="label-tiny"># Key Discussions</span>
|
||||
|
||||
<div class="space-y-2">
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="text-[11px] font-bold text-slate-900">01. LLM 本地部署显存压测</span>
|
||||
<div class="h-[1px] flex-grow bg-slate-100"></div>
|
||||
</div>
|
||||
<div class="pl-2 space-y-2">
|
||||
<div>
|
||||
<span class="label-tiny text-slate-400">Background</span>
|
||||
<p class="text-[11px] text-slate-600 leading-relaxed">群友在 Mac mini M1 (16G) 环境下运行 Gemma 4 26B,出现内存频繁交换导致系统卡顿。</p>
|
||||
</div>
|
||||
<div>
|
||||
<span class="label-tiny text-slate-400">Key Points</span>
|
||||
<ul class="text-[11px] text-slate-600 list-disc pl-3 space-y-0.5">
|
||||
<li>4-bit 量化(GGUF/Q4_K_M)显存占用约 17.5GB。</li>
|
||||
<li>需通过脚本强制清理物理内存 Swap 以缓解抖动。</li>
|
||||
<li>Ollama 推理时建议关闭 Electron 全家桶。</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="conclusion-area">
|
||||
<span class="label-tiny !text-emerald-600">Conclusion</span>
|
||||
<p class="text-[11px] font-bold text-emerald-800 italic">“16G 内存是底线,追求速度建议 32G 或 24G 显存 GPU 运行。”</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="space-y-2">
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="text-[11px] font-bold text-slate-900">02. Dota 2 击杀识别 OCR 算法</span>
|
||||
<div class="h-[1px] flex-grow bg-slate-100"></div>
|
||||
</div>
|
||||
<div class="pl-2 space-y-2 text-[11px]">
|
||||
<span class="label-tiny text-slate-400">Analysis</span>
|
||||
<p class="text-slate-600 leading-relaxed">弃用 Tesseract 改用 PaddleOCR。在处理 Douyu 直播间 1080P 高码率画面时,识别准确率从 72% 提升至 91%。</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="p-5 bg-slate-50/50 border-y border-slate-100">
|
||||
<span class="label-tiny mb-3"># Shared Resources</span>
|
||||
<div class="space-y-1">
|
||||
<div class="card-inner bg-white flex justify-between items-center py-1.5">
|
||||
<div class="flex items-center gap-2 overflow-hidden">
|
||||
<i class="fab fa-github text-slate-300 text-[12px]"></i>
|
||||
<span class="text-[10px] font-medium text-slate-600 truncate">ollama-python/v2.1-stable</span>
|
||||
</div>
|
||||
<i class="fas fa-chevron-right text-[8px] text-slate-300"></i>
|
||||
</div>
|
||||
<div class="card-inner bg-white flex justify-between items-center py-1.5">
|
||||
<div class="flex items-center gap-2 overflow-hidden">
|
||||
<i class="far fa-file-pdf text-slate-300 text-[12px]"></i>
|
||||
<span class="text-[10px] font-medium text-slate-600 truncate">RTX_5090_Whitepaper_Draft.pdf</span>
|
||||
</div>
|
||||
<i class="fas fa-chevron-right text-[8px] text-slate-300"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="p-5 grid grid-cols-2 gap-4">
|
||||
<div>
|
||||
<span class="label-tiny mb-2"># Marketplace</span>
|
||||
<div class="space-y-1 text-[10px]">
|
||||
<div class="flex justify-between border-b border-slate-100 pb-1">
|
||||
<span class="text-rose-500 font-bold">[出] 3060Ti</span>
|
||||
<span class="mono font-bold text-slate-700">¥1650</span>
|
||||
</div>
|
||||
<div class="flex justify-between border-b border-slate-100 pb-1 pt-1">
|
||||
<span class="text-blue-500 font-bold">[求] TI 饰品</span>
|
||||
<span class="mono text-slate-400">面议</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<span class="label-tiny mb-2"># Unresolved Pool</span>
|
||||
<div class="space-y-1 text-[10px]">
|
||||
<p class="text-slate-500 leading-tight border-l-2 border-amber-200 pl-2">"Ubuntu 24.04 驱动掉线如何修复?"</p>
|
||||
<p class="text-slate-500 leading-tight border-l-2 border-amber-200 pl-2">"Nasdaq 100 ETF 最优定投点?"</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="px-5 pb-5">
|
||||
<div class="card-inner bg-slate-900 text-white border-none p-3 shadow-inner">
|
||||
<span class="label-tiny text-slate-500 mb-1"># Core Knowledge Points</span>
|
||||
<p class="text-[10px] leading-relaxed text-slate-300">
|
||||
<span class="text-blue-400 font-bold">FRP 配置项优化:</span> 针对内网穿透超时,需将 <code class="mono text-[9px] bg-slate-800 px-1">tcp_mux</code> 设置为 <code class="mono text-[9px] bg-slate-800 px-1">true</code>,并检查服务端防火墙 MTU 限制。
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<footer class="p-5 bg-slate-50 border-t border-slate-100 flex justify-between items-center">
|
||||
<span class="label-tiny">Top Contributors</span>
|
||||
<div class="flex -space-x-2">
|
||||
<div class="w-6 h-6 rounded-full border-2 border-white bg-blue-100 flex items-center justify-center text-[9px] font-bold text-blue-600">L</div>
|
||||
<div class="w-6 h-6 rounded-full border-2 border-white bg-indigo-100 flex items-center justify-center text-[9px] font-bold text-indigo-600">G</div>
|
||||
<div class="w-6 h-6 rounded-full border-2 border-white bg-slate-200 flex items-center justify-center text-[9px] font-bold text-slate-500">T</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="text-center mt-4 text-[9px] text-slate-400">
|
||||
Engine: Playwright 1.42 / Renderer: Webkit / User: Liuwei
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
298
plugins/message_summary/templates/gemini_summary_card.html
Normal file
298
plugins/message_summary/templates/gemini_summary_card.html
Normal file
@@ -0,0 +1,298 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{{ title }}</title>
|
||||
<style>
|
||||
:root {
|
||||
--bg: #f8fafc;
|
||||
--surface: #ffffff;
|
||||
--line: #eef2f7;
|
||||
--text: #334155;
|
||||
--text-soft: #64748b;
|
||||
--text-faint: #94a3b8;
|
||||
--title: #0f172a;
|
||||
--brand: #2563eb;
|
||||
--brand-soft: #dbeafe;
|
||||
--ok-soft: #f0fdf4;
|
||||
--ok-line: #22c55e;
|
||||
--quote-bg: #f8fafc;
|
||||
--quote-line: #cbd5e1;
|
||||
--code-bg: #0f172a;
|
||||
--code-text: #e2e8f0;
|
||||
--shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
* { box-sizing: border-box; }
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 24px 0;
|
||||
background: var(--bg);
|
||||
color: var(--text);
|
||||
font-family: "Inter", "PingFang SC", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
.report-container {
|
||||
width: 440px;
|
||||
margin: 0 auto;
|
||||
background: var(--surface);
|
||||
box-shadow: var(--shadow);
|
||||
border: 1px solid #e2e8f0;
|
||||
}
|
||||
.label-tiny {
|
||||
font-size: 9px;
|
||||
font-weight: 800;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: .08em;
|
||||
color: var(--text-faint);
|
||||
margin-bottom: 2px;
|
||||
display: block;
|
||||
}
|
||||
.header {
|
||||
padding: 18px 18px 14px;
|
||||
border-bottom: 1px solid var(--line);
|
||||
}
|
||||
.header-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
margin-bottom: 12px;
|
||||
gap: 10px;
|
||||
}
|
||||
.header-title {
|
||||
font-size: 15px;
|
||||
font-weight: 900;
|
||||
color: var(--title);
|
||||
letter-spacing: -.02em;
|
||||
line-height: 1.35;
|
||||
}
|
||||
.header-title .accent {
|
||||
color: var(--brand);
|
||||
font-weight: 600;
|
||||
}
|
||||
.header-id {
|
||||
margin-top: 4px;
|
||||
font-size: 10px;
|
||||
color: var(--text-faint);
|
||||
font-weight: 600;
|
||||
letter-spacing: .03em;
|
||||
}
|
||||
.header-tag {
|
||||
padding: 4px 8px;
|
||||
background: #f1f5f9;
|
||||
border-radius: 4px;
|
||||
font-size: 9px;
|
||||
font-weight: 800;
|
||||
color: #64748b;
|
||||
text-transform: uppercase;
|
||||
white-space: nowrap;
|
||||
border: 1px solid #e2e8f0;
|
||||
}
|
||||
.meta-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||
gap: 8px;
|
||||
text-align: center;
|
||||
}
|
||||
.meta-item {
|
||||
background: #f8fafc;
|
||||
border: 1px solid #f1f5f9;
|
||||
border-radius: 4px;
|
||||
padding: 8px 4px 6px;
|
||||
}
|
||||
.meta-value {
|
||||
font-size: 11px;
|
||||
font-weight: 800;
|
||||
color: #475569;
|
||||
}
|
||||
.meta-value.brand {
|
||||
color: var(--brand);
|
||||
}
|
||||
.summary-body {
|
||||
padding: 16px 18px 18px;
|
||||
}
|
||||
.markdown-body {
|
||||
font-size: 12px;
|
||||
line-height: 1.82;
|
||||
color: var(--text);
|
||||
word-wrap: break-word;
|
||||
}
|
||||
.markdown-body > *:first-child { margin-top: 0; }
|
||||
.markdown-body > *:last-child { margin-bottom: 0; }
|
||||
.markdown-body h1,
|
||||
.markdown-body h2,
|
||||
.markdown-body h3,
|
||||
.markdown-body h4,
|
||||
.markdown-body h5,
|
||||
.markdown-body h6 {
|
||||
color: var(--title);
|
||||
margin: 16px 0 8px;
|
||||
line-height: 1.42;
|
||||
font-weight: 800;
|
||||
letter-spacing: -.01em;
|
||||
}
|
||||
.markdown-body h1 { font-size: 18px; border-bottom: 1px solid var(--line); padding-bottom: 6px; }
|
||||
.markdown-body h2 { font-size: 16px; }
|
||||
.markdown-body h3 { font-size: 14px; }
|
||||
.markdown-body h4,
|
||||
.markdown-body h5,
|
||||
.markdown-body h6 { font-size: 13px; }
|
||||
.markdown-body p { margin: 8px 0; }
|
||||
.markdown-body ul,
|
||||
.markdown-body ol {
|
||||
margin: 8px 0 12px;
|
||||
padding-left: 18px;
|
||||
}
|
||||
.markdown-body li { margin: 4px 0; }
|
||||
.markdown-body blockquote {
|
||||
margin: 10px 0;
|
||||
padding: 8px 10px;
|
||||
border-left: 3px solid var(--quote-line);
|
||||
background: var(--quote-bg);
|
||||
color: #475569;
|
||||
border-radius: 0 4px 4px 0;
|
||||
}
|
||||
.markdown-body hr {
|
||||
border: none;
|
||||
border-top: 1px dashed #dbe3ee;
|
||||
margin: 14px 0;
|
||||
}
|
||||
.markdown-body a {
|
||||
color: var(--brand);
|
||||
text-decoration: none;
|
||||
border-bottom: 1px dotted rgba(37, 99, 235, .5);
|
||||
}
|
||||
.markdown-body strong {
|
||||
color: #1e293b;
|
||||
font-weight: 800;
|
||||
}
|
||||
.markdown-body em {
|
||||
color: #475569;
|
||||
font-style: italic;
|
||||
}
|
||||
.markdown-body code {
|
||||
font-family: "JetBrains Mono", "SFMono-Regular", Menlo, Consolas, monospace;
|
||||
font-size: 11px;
|
||||
background: #eff6ff;
|
||||
color: #1e40af;
|
||||
padding: 1px 4px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.markdown-body pre {
|
||||
margin: 10px 0;
|
||||
padding: 10px 11px;
|
||||
border-radius: 6px;
|
||||
background: var(--code-bg);
|
||||
color: var(--code-text);
|
||||
overflow-x: auto;
|
||||
border: 1px solid #1e293b;
|
||||
}
|
||||
.markdown-body pre code {
|
||||
background: transparent;
|
||||
color: inherit;
|
||||
padding: 0;
|
||||
border-radius: 0;
|
||||
font-size: 11px;
|
||||
line-height: 1.7;
|
||||
}
|
||||
.markdown-body table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
margin: 10px 0;
|
||||
font-size: 11px;
|
||||
border: 1px solid #e2e8f0;
|
||||
}
|
||||
.markdown-body th,
|
||||
.markdown-body td {
|
||||
border: 1px solid #e2e8f0;
|
||||
padding: 6px 7px;
|
||||
text-align: left;
|
||||
vertical-align: top;
|
||||
}
|
||||
.markdown-body th {
|
||||
background: #f8fafc;
|
||||
color: #334155;
|
||||
font-weight: 700;
|
||||
}
|
||||
.markdown-body img {
|
||||
max-width: 100%;
|
||||
border-radius: 6px;
|
||||
border: 1px solid #e2e8f0;
|
||||
margin: 6px 0;
|
||||
}
|
||||
.summary-footer {
|
||||
margin-top: 14px;
|
||||
padding-top: 10px;
|
||||
border-top: 1px dashed #e2e8f0;
|
||||
font-size: 10px;
|
||||
color: var(--text-faint);
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
.summary-footer .right {
|
||||
font-size: 9px;
|
||||
color: #94a3b8;
|
||||
letter-spacing: .04em;
|
||||
text-transform: uppercase;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.safe-callout {
|
||||
margin-top: 10px;
|
||||
padding: 8px 10px;
|
||||
background: var(--ok-soft);
|
||||
border-left: 2px solid var(--ok-line);
|
||||
border-radius: 0 4px 4px 0;
|
||||
font-size: 11px;
|
||||
color: #166534;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="report-container">
|
||||
<header class="header">
|
||||
<div class="header-row">
|
||||
<div>
|
||||
<div class="header-title">CHAT INSIGHTS <span class="accent">SUMMARY</span></div>
|
||||
<div class="header-id">{{ generated_at }}</div>
|
||||
</div>
|
||||
<div class="header-tag">Daily Archive</div>
|
||||
</div>
|
||||
<div class="meta-grid">
|
||||
<div class="meta-item">
|
||||
<span class="label-tiny">Type</span>
|
||||
<div class="meta-value">群总结</div>
|
||||
</div>
|
||||
<div class="meta-item">
|
||||
<span class="label-tiny">Mode</span>
|
||||
<div class="meta-value brand">Template</div>
|
||||
</div>
|
||||
<div class="meta-item">
|
||||
<span class="label-tiny">Render</span>
|
||||
<div class="meta-value">HTML</div>
|
||||
</div>
|
||||
<div class="meta-item">
|
||||
<span class="label-tiny">Engine</span>
|
||||
<div class="meta-value">LLM</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<section class="summary-body">
|
||||
<span class="label-tiny"># {{ title }}</span>
|
||||
<div class="markdown-body">
|
||||
{{ summary_html }}
|
||||
</div>
|
||||
<div class="safe-callout">
|
||||
内容已按 Markdown 富标签样式渲染(标题、列表、表格、代码块、引用)。
|
||||
</div>
|
||||
<div class="summary-footer">
|
||||
<span>ABOT · Message Summary Gemini Style</span>
|
||||
<span class="right">Renderer: Playwright</span>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user