Files
abot/plugins/message_summary/templates/gemini_summary_card.html
liuwei c4f29084bc 按design_summary规范重构群总结卡片模板
- gemini_summary_card 调整为 420px 固定宽度与高密度小字号布局,贴合 Clean Technical 规范\n- 话题卡片严格采用 Background/Key Points/Conclusion 三段论结构并保留结论高亮块\n- 个人雷达标签改为药丸样式并增加彩色高亮逻辑,未命中维持 Slate 背景\n- 新增资源库列表的 GitHub 风格行结构(左图标、中标题、右箭头)\n- 双栏挂件恢复 Marketplace 与 Unresolved Pool 并排展示,节省纵向空间\n- 核心知识点改为深色高反差模块,强化长图视觉焦点\n- main.py 新增模板命名模块与资源库结构化提取,保障模板字段与数据一致
2026-04-23 11:02:45 +08:00

541 lines
16 KiB
HTML
Raw 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.
<!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>
{{ local_font_css }}
:root {
--bg: #F8FAFC;
--surface: #FFFFFF;
--text: #334155;
--heading: #0F172A;
--line: #F1F5F9;
--blue: #2563EB;
--green: #22C55E;
--rose: #F43F5E;
--shadow-sm: 0 1px 3px rgba(15, 23, 42, 0.05);
}
* { box-sizing: border-box; }
body {
margin: 0;
padding: 10px 0;
background: var(--bg);
color: var(--text);
font-family: var(--abot-font-sans, 'Inter', 'PingFang SC', system-ui, sans-serif);
-webkit-font-smoothing: antialiased;
}
.report-container {
width: 420px;
margin: 0 auto;
background: var(--surface);
border: 1px solid var(--line);
box-shadow: var(--shadow-sm);
}
.label-tiny {
display: block;
font-size: 9px;
line-height: 1.2;
font-weight: 800;
text-transform: uppercase;
letter-spacing: 0.08em;
color: #94a3b8;
margin-bottom: 3px;
}
.mono { font-family: var(--abot-font-code, 'JetBrains Mono', 'SF Mono', monospace); }
.header {
padding: 12px;
border-bottom: 1px solid var(--line);
}
.header-top {
display: flex;
justify-content: space-between;
align-items: flex-start;
gap: 8px;
margin-bottom: 8px;
}
.header-title {
margin: 0;
font-size: 13px;
line-height: 1.25;
font-weight: 900;
color: var(--heading);
letter-spacing: -0.02em;
}
.header-title .accent { color: var(--blue); font-weight: 600; }
.header-id {
margin: 3px 0 0;
font-size: 9px;
color: #94a3b8;
letter-spacing: 0.03em;
}
.header-tag {
padding: 2px 6px;
border-radius: 4px;
background: #f1f5f9;
border: 1px solid #e2e8f0;
font-size: 9px;
color: #64748b;
font-weight: 700;
white-space: nowrap;
text-transform: uppercase;
}
.kpi-grid {
display: grid;
grid-template-columns: repeat(4, minmax(0, 1fr));
gap: 6px;
}
.kpi {
background: #f8fafc;
border: 1px solid var(--line);
border-radius: 4px;
padding: 5px 4px;
text-align: center;
}
.kpi .value {
font-size: 11px;
font-weight: 800;
color: #475569;
}
.kpi .value.is-blue { color: var(--blue); }
.kpi .value.is-indigo { color: #4f46e5; }
.kpi .value.is-emerald { color: #059669; }
.radar {
padding: 10px 12px;
border-bottom: 1px solid var(--line);
}
.radar-tags {
display: flex;
flex-wrap: wrap;
gap: 6px;
margin-top: 2px;
}
.radar-tag {
font-size: 9px;
line-height: 1.2;
padding: 2px 7px;
border-radius: 999px;
border: 1px solid #e2e8f0;
background: #f1f5f9;
color: #64748b;
font-weight: 700;
}
.radar-tag.hit-blue { background: #eff6ff; border-color: #dbeafe; color: #2563eb; }
.radar-tag.hit-indigo { background: #eef2ff; border-color: #e0e7ff; color: #4f46e5; }
.radar-tag.hit-emerald { background: #ecfdf5; border-color: #d1fae5; color: #059669; }
.content {
padding: 12px;
}
.lead {
margin: 0 0 10px;
background: #f0fdf4;
border-left: 2px solid var(--green);
border-radius: 0 4px 4px 0;
padding: 7px 8px;
font-size: 11px;
line-height: 1.55;
color: #166534;
font-weight: 700;
}
.topic-list {
display: flex;
flex-direction: column;
gap: 8px;
}
.topic-card {
border: 1px solid #e2e8f0;
border-radius: 6px;
padding: 8px;
background: #fff;
}
.topic-title-row {
display: flex;
align-items: center;
gap: 7px;
margin-bottom: 6px;
}
.topic-title {
margin: 0;
font-size: 13px;
font-weight: 800;
color: var(--heading);
line-height: 1.3;
}
.topic-divider {
flex: 1;
height: 1px;
background: var(--line);
}
.topic-meta {
margin: 0 0 6px;
font-size: 10px;
color: #64748b;
line-height: 1.45;
padding: 5px 6px;
border: 1px dashed #e2e8f0;
border-radius: 4px;
background: #f8fafc;
}
.topic-block { margin-bottom: 6px; }
.topic-block:last-child { margin-bottom: 0; }
.topic-text {
margin: 0;
font-size: 11px;
color: #475569;
line-height: 1.55;
}
.topic-bullets {
margin: 0;
padding-left: 10px;
font-size: 11px;
color: #475569;
line-height: 1.5;
}
.topic-bullets li { margin: 1px 0; }
.conclusion-area {
background: #f0fdf4;
border-left: 2px solid var(--green);
border-radius: 0 4px 4px 0;
padding: 6px 7px;
}
.conclusion-text {
margin: 0;
font-size: 11px;
line-height: 1.5;
font-weight: 700;
color: #166534;
font-style: italic;
}
.resource-wrap {
margin-top: 10px;
padding: 10px 0;
border-top: 1px solid var(--line);
border-bottom: 1px solid var(--line);
background: rgba(248, 250, 252, 0.5);
}
.resource-list {
display: flex;
flex-direction: column;
gap: 4px;
padding: 0 8px;
}
.resource-item {
border: 1px solid var(--line);
border-radius: 4px;
padding: 5px 6px;
background: #fff;
display: flex;
align-items: center;
justify-content: space-between;
gap: 6px;
min-height: 24px;
}
.resource-left {
display: flex;
align-items: center;
gap: 5px;
min-width: 0;
}
.resource-icon {
width: 14px;
text-align: center;
font-size: 10px;
color: #94a3b8;
flex-shrink: 0;
}
.resource-title {
font-size: 10px;
color: #475569;
line-height: 1.2;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.resource-arrow {
font-size: 9px;
color: #cbd5e1;
flex-shrink: 0;
}
.widget-grid {
margin-top: 10px;
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 8px;
}
.widget {
border: 1px solid var(--line);
border-radius: 4px;
padding: 6px;
background: #fff;
min-height: 62px;
}
.widget-item {
margin: 0 0 4px;
font-size: 10px;
line-height: 1.45;
color: #475569;
}
.widget-item:last-child { margin-bottom: 0; }
.widget-item.trade { color: var(--rose); font-weight: 700; }
.core-wrap { margin-top: 10px; }
.core-card {
border-radius: 4px;
background: #0F172A;
border: 1px solid #1e293b;
padding: 8px;
}
.core-item {
margin: 0 0 5px;
font-size: 10px;
line-height: 1.5;
color: #cbd5e1;
}
.core-item:last-child { margin-bottom: 0; }
.contributors {
margin-top: 10px;
padding: 8px;
background: #f8fafc;
border-top: 1px solid var(--line);
border-bottom: 1px solid var(--line);
display: flex;
justify-content: space-between;
align-items: center;
gap: 8px;
}
.avatar-list {
display: flex;
margin-left: auto;
}
.avatar {
width: 22px;
height: 22px;
border-radius: 50%;
border: 2px solid #fff;
display: inline-flex;
align-items: center;
justify-content: center;
margin-left: -5px;
font-size: 9px;
font-weight: 800;
}
.avatar:nth-child(1) { background: #dbeafe; color: #2563eb; }
.avatar:nth-child(2) { background: #e0e7ff; color: #4f46e5; }
.avatar:nth-child(3) { background: #e2e8f0; color: #64748b; }
.footer-note {
padding: 8px 0 10px;
text-align: center;
font-size: 9px;
color: #94a3b8;
letter-spacing: 0.03em;
text-transform: uppercase;
}
</style>
</head>
<body>
<div class="report-container">
<header class="header">
<div class="header-top">
<div>
<h1 class="header-title">CHAT INSIGHTS <span class="accent">REPORT</span></h1>
<p class="header-id mono">ID: {{ generated_at }}</p>
</div>
<div class="header-tag">{{ summary_metrics.activity_badge or "Daily Archive" }}</div>
</div>
<div class="kpi-grid">
{% for card in summary_metrics.kpi_cards|default([]) %}
<div class="kpi">
<span class="label-tiny">{{ card.label }}</span>
<span class="value {% if card.tone == 'blue' %}is-blue{% elif card.tone == 'violet' %}is-indigo{% elif card.tone == 'emerald' %}is-emerald{% endif %}">{{ card.value }}</span>
</div>
{% endfor %}
</div>
</header>
<section class="radar">
<span class="label-tiny"># Personal Interest Radar</span>
<div class="radar-tags">
{% for tag in summary_metrics.topic_tags|default([]) %}
<span class="radar-tag {% if loop.index0 % 4 == 0 %}hit-blue{% elif loop.index0 % 4 == 1 %}hit-indigo{% elif loop.index0 % 4 == 2 %}hit-emerald{% else %}{% endif %}">{{ tag }}</span>
{% endfor %}
{% if not (summary_metrics.topic_tags|default([])) %}
<span class="radar-tag">暂无热点标签</span>
{% endif %}
</div>
</section>
<section class="content">
<span class="label-tiny"># {{ summary_doc_title or title }}</span>
<p class="lead">{{ summary_lead or "暂无总结内容。" }}</p>
<span class="label-tiny"># Key Discussions</span>
<div class="topic-list">
{% for topic in summary_topics|default([]) %}
<article class="topic-card">
<div class="topic-title-row">
<h3 class="topic-title">{{ "%02d"|format(loop.index) }}. {{ topic["title"] }}</h3>
<div class="topic-divider"></div>
</div>
{% if topic["time_range"] or topic["participants"] %}
<div class="topic-meta">
{% if topic["time_range"] %}<div>时段:{{ topic["time_range"] }}</div>{% endif %}
{% if topic["participants"] %}<div>参与人数:{{ topic["participants"] }}</div>{% endif %}
</div>
{% endif %}
{% if topic["analysis_points"]|default([]) %}
<div class="topic-block">
<span class="label-tiny">Background</span>
<p class="topic-text">{{ topic["analysis_points"][0] }}</p>
</div>
{% endif %}
{% if topic["overview_points"]|default([]) %}
<div class="topic-block">
<span class="label-tiny">Key Points</span>
<ul class="topic-bullets">
{% for line in topic["overview_points"] %}
<li>{{ line }}</li>
{% endfor %}
</ul>
</div>
{% endif %}
{% if topic["quote_text"] %}
<div class="conclusion-area">
<span class="label-tiny" style="color:#16a34a;">Conclusion</span>
<p class="conclusion-text">{{ topic["quote_text"] }}</p>
</div>
{% endif %}
</article>
{% endfor %}
</div>
{% if not (summary_topics|default([])) %}
<p class="widget-item">{{ summary_fallback_text }}</p>
{% endif %}
{% if summary_resource_hub|default([]) %}
<section class="resource-wrap">
<span class="label-tiny" style="padding: 0 8px 4px;"># Shared Resources</span>
<div class="resource-list">
{% for item in summary_resource_hub %}
<div class="resource-item">
<div class="resource-left">
<span class="resource-icon">{{ item.icon }}</span>
<span class="resource-title">{{ item.title }}</span>
</div>
<span class="resource-arrow"></span>
</div>
{% endfor %}
</div>
</section>
{% endif %}
<section class="widget-grid">
<article class="widget">
<span class="label-tiny"># Marketplace</span>
{% for line in summary_marketplace|default([]) %}
<p class="widget-item trade">{{ line }}</p>
{% endfor %}
{% if not (summary_marketplace|default([])) %}
<p class="widget-item">暂无交易信息</p>
{% endif %}
</article>
<article class="widget">
<span class="label-tiny"># Unresolved Pool</span>
{% for line in summary_unresolved_pool|default([]) %}
<p class="widget-item">{{ line }}</p>
{% endfor %}
{% if not (summary_unresolved_pool|default([])) %}
<p class="widget-item">暂无待解问题</p>
{% endif %}
</article>
</section>
<section class="core-wrap">
<div class="core-card">
<span class="label-tiny" style="color:#64748b;"># Core Knowledge Points</span>
{% for line in summary_core_points|default([]) %}
<p class="core-item">{{ line }}</p>
{% endfor %}
{% if not (summary_core_points|default([])) %}
<p class="core-item">暂无核心知识点</p>
{% endif %}
</div>
</section>
<div class="contributors">
<span class="label-tiny" style="margin:0;">Top Contributors</span>
<div class="avatar-list">
{% for name in summary_top_contributors|default([]) %}
<span class="avatar">{{ name[:1] }}</span>
{% endfor %}
{% if not (summary_top_contributors|default([])) %}
<span class="avatar">A</span><span class="avatar">B</span><span class="avatar">C</span>
{% endif %}
</div>
</div>
<div class="footer-note">Engine: Playwright · Tokens: {{ summary_metrics.token_total }} · Latency: {{ summary_metrics.latency_text }}</div>
</section>
</div>
</body>
</html>