补充斗鱼粉丝日报本地预览脚本并压缩版面
1. 为本地测试脚本增加粉丝日报 HTML 预览输出,统一映射新版模板需要的数据结构\n2. 内置稳定的预览文案拼装逻辑,方便不依赖LLM也能本地验收页面效果\n3. 压缩粉丝日报模板的卡片间距、字号、行高和高度,让同样的信息更紧凑简约地展示
This commit is contained in:
@@ -12,6 +12,7 @@
|
|||||||
import importlib.util
|
import importlib.util
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
|
import sys
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any, Dict, List
|
from typing import Any, Dict, List
|
||||||
@@ -27,6 +28,25 @@ def _load_helper():
|
|||||||
return module.DouyuDanmuSummaryHelper
|
return module.DouyuDanmuSummaryHelper
|
||||||
|
|
||||||
|
|
||||||
|
def _load_report_template_module():
|
||||||
|
"""
|
||||||
|
单独按文件路径加载模板模块。
|
||||||
|
这样本地预览不需要完整初始化插件,也不依赖 Redis 或其他运行时对象。
|
||||||
|
"""
|
||||||
|
current_dir = Path(__file__).resolve().parent
|
||||||
|
project_root = current_dir.parent.parent
|
||||||
|
project_root_str = str(project_root)
|
||||||
|
# 把项目根目录补进 sys.path,保证 report_template.py 内部引用 utils 等项目模块时可正常导入。
|
||||||
|
if project_root_str not in sys.path:
|
||||||
|
sys.path.insert(0, project_root_str)
|
||||||
|
module_path = current_dir / "report_template.py"
|
||||||
|
spec = importlib.util.spec_from_file_location("douyu_report_template_local", module_path)
|
||||||
|
module = importlib.util.module_from_spec(spec)
|
||||||
|
assert spec.loader is not None
|
||||||
|
spec.loader.exec_module(module)
|
||||||
|
return module
|
||||||
|
|
||||||
|
|
||||||
def _build_session(room_id: str, anchor_day: str, messages: List[Dict[str, Any]]) -> Dict[str, Any]:
|
def _build_session(room_id: str, anchor_day: str, messages: List[Dict[str, Any]]) -> Dict[str, Any]:
|
||||||
ordered = sorted(messages, key=lambda item: item.get("timestamp") or datetime.min)
|
ordered = sorted(messages, key=lambda item: item.get("timestamp") or datetime.min)
|
||||||
if not ordered:
|
if not ordered:
|
||||||
@@ -51,6 +71,181 @@ def _build_session(room_id: str, anchor_day: str, messages: List[Dict[str, Any]]
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _build_preview_template_payload(local_result: Dict[str, Any]) -> Dict[str, Any]:
|
||||||
|
"""
|
||||||
|
将本地测试结果转成粉丝日报模板真正需要的结构。
|
||||||
|
这样预览链路和正式模板共用同一套字段命名,后续查问题更直观。
|
||||||
|
"""
|
||||||
|
session_meta = local_result.get("session_meta", {}) or {}
|
||||||
|
local_stats_preview = local_result.get("local_stats_preview", {}) or {}
|
||||||
|
topic_clusters = local_result.get("topic_evidence_clusters", []) or []
|
||||||
|
hero_mentions = local_result.get("hero_mentions", []) or []
|
||||||
|
content_cues = local_result.get("content_cues", []) or []
|
||||||
|
timeline_digest = local_result.get("timeline_digest", []) or []
|
||||||
|
representative_messages = local_result.get("representative_messages", []) or []
|
||||||
|
|
||||||
|
return {
|
||||||
|
"report_meta": {
|
||||||
|
"room_id": str(session_meta.get("room_id") or "").strip(),
|
||||||
|
"anchor_day": str(session_meta.get("anchor_day") or "").strip(),
|
||||||
|
"nickname": str(session_meta.get("nickname") or "").strip(),
|
||||||
|
"room_name": str(session_meta.get("room_name") or "").strip(),
|
||||||
|
"session_count": 1,
|
||||||
|
"message_count": int(session_meta.get("message_count", 0) or 0),
|
||||||
|
"unique_user_count": int(session_meta.get("unique_user_count", 0) or 0),
|
||||||
|
},
|
||||||
|
"local_stats": {
|
||||||
|
"message_count": int(session_meta.get("message_count", 0) or 0),
|
||||||
|
"unique_user_count": int(session_meta.get("unique_user_count", 0) or 0),
|
||||||
|
"top_emotion_bursts": [
|
||||||
|
{
|
||||||
|
"text": str(item.get("text") or "").strip(),
|
||||||
|
"count": int(item.get("count", 0) or 0),
|
||||||
|
}
|
||||||
|
for item in content_cues
|
||||||
|
if str(item.get("kind") or "").strip() == "emotion"
|
||||||
|
][:8],
|
||||||
|
"top_repeated_messages": [
|
||||||
|
{
|
||||||
|
"text": str(item.get("text") or "").strip(),
|
||||||
|
"count": int(item.get("count", 0) or 0),
|
||||||
|
"user_count": int(item.get("user_count", 0) or 0),
|
||||||
|
}
|
||||||
|
for item in local_stats_preview.get("top_repeated_messages", [])[:8]
|
||||||
|
],
|
||||||
|
"peak_windows": [
|
||||||
|
{
|
||||||
|
"start_time": str(item.get("start_time") or "").strip(),
|
||||||
|
"message_count": int(item.get("message_count", 0) or 0),
|
||||||
|
"user_count": int(item.get("user_count", 0) or 0),
|
||||||
|
}
|
||||||
|
for item in local_stats_preview.get("peak_buckets", [])[:6]
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"topic_evidence_clusters": [
|
||||||
|
{
|
||||||
|
"label": str(item.get("label") or "").strip(),
|
||||||
|
"count": int(item.get("match_count", item.get("count", 0)) or 0),
|
||||||
|
"user_count": int(item.get("user_count", 0) or 0),
|
||||||
|
"time_range": (
|
||||||
|
f"{str(item.get('first_hm') or '').strip()}-{str(item.get('last_hm') or '').strip()}"
|
||||||
|
).strip("-"),
|
||||||
|
"keywords": item.get("keywords", []) or [],
|
||||||
|
"samples": item.get("samples", []) or [],
|
||||||
|
}
|
||||||
|
for item in topic_clusters[:6]
|
||||||
|
],
|
||||||
|
"compact_scene_material": {
|
||||||
|
"semantic_fact_hints": {
|
||||||
|
"hero_mentions": hero_mentions[:6],
|
||||||
|
},
|
||||||
|
"content_cues": content_cues[:18],
|
||||||
|
"timeline_digest": timeline_digest[:20],
|
||||||
|
},
|
||||||
|
"representative_messages": representative_messages[:12],
|
||||||
|
"repeated_messages": [
|
||||||
|
{
|
||||||
|
"text": str(item.get("text") or "").strip(),
|
||||||
|
"count": int(item.get("count", 0) or 0),
|
||||||
|
"user_count": int(item.get("user_count", 0) or 0),
|
||||||
|
}
|
||||||
|
for item in local_stats_preview.get("top_repeated_messages", [])[:12]
|
||||||
|
],
|
||||||
|
"burst_terms": [
|
||||||
|
{
|
||||||
|
"text": str(item.get("text") or "").strip(),
|
||||||
|
"count": int(item.get("count", 0) or 0),
|
||||||
|
}
|
||||||
|
for item in local_stats_preview.get("top_burst_terms", [])[:12]
|
||||||
|
],
|
||||||
|
"peak_buckets": local_stats_preview.get("peak_buckets", [])[:6],
|
||||||
|
"top_terms": [
|
||||||
|
{"term": str(keyword).strip(), "count": 0}
|
||||||
|
for item in topic_clusters[:4]
|
||||||
|
for keyword in (item.get("keywords", []) or [])[:2]
|
||||||
|
if str(keyword).strip()
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _build_preview_report_text(payload: Dict[str, Any]) -> str:
|
||||||
|
"""
|
||||||
|
为本地模板预览提供一份稳定的示例文本。
|
||||||
|
这里不依赖真实 LLM,只用已经提纯好的结果拼装固定结构,
|
||||||
|
方便我们快速检查模板是否把关键信息展示完整。
|
||||||
|
"""
|
||||||
|
meta = payload.get("report_meta", {}) or {}
|
||||||
|
topic_clusters = payload.get("topic_evidence_clusters", []) or []
|
||||||
|
hero_mentions = (
|
||||||
|
payload.get("compact_scene_material", {})
|
||||||
|
.get("semantic_fact_hints", {})
|
||||||
|
.get("hero_mentions", [])
|
||||||
|
or []
|
||||||
|
)
|
||||||
|
repeated_messages = payload.get("repeated_messages", []) or []
|
||||||
|
burst_terms = payload.get("burst_terms", []) or []
|
||||||
|
peak_buckets = payload.get("peak_buckets", []) or []
|
||||||
|
representative_messages = payload.get("representative_messages", []) or []
|
||||||
|
anchor_day = str(meta.get("anchor_day") or "").strip()
|
||||||
|
|
||||||
|
lines = [
|
||||||
|
f"{anchor_day} 这场直播的弹幕不只是热闹,核心信息也很密:赛事、位置、英雄、团播人物和摄像头梗都有人追着聊。",
|
||||||
|
"【今日重点信息】",
|
||||||
|
]
|
||||||
|
for item in topic_clusters[:5]:
|
||||||
|
label = str(item.get("label") or "").strip()
|
||||||
|
time_range = str(item.get("time_range") or "").strip()
|
||||||
|
count = int(item.get("count", 0) or 0)
|
||||||
|
samples = item.get("samples", []) or []
|
||||||
|
sample_text = str(samples[0].get("content") or "").strip()[:42] if samples else ""
|
||||||
|
if label and sample_text:
|
||||||
|
lines.append(f"- {label}从 {time_range or '全场'} 一直有人聊,相关弹幕约 {count} 条,代表说法是「{sample_text}」。")
|
||||||
|
|
||||||
|
lines.append("【核心讨论话题】")
|
||||||
|
for item in topic_clusters[:4]:
|
||||||
|
label = str(item.get("label") or "").strip()
|
||||||
|
keywords = [str(keyword).strip() for keyword in (item.get("keywords", []) or [])[:5] if str(keyword).strip()]
|
||||||
|
if label and keywords:
|
||||||
|
lines.append(f"- 大家围着 {label} 打转,关键词主要是 {'、'.join(keywords)}。")
|
||||||
|
|
||||||
|
lines.append("【英雄与对局焦点】")
|
||||||
|
for item in hero_mentions[:4]:
|
||||||
|
hero_name = str(item.get("hero") or "").strip()
|
||||||
|
mention_count = int(item.get("mention_count", 0) or 0)
|
||||||
|
samples = item.get("samples", []) or []
|
||||||
|
sample_text = str(samples[0].get("content") or "").strip()[:36] if samples else ""
|
||||||
|
if hero_name and sample_text:
|
||||||
|
lines.append(f"- {hero_name}被点名 {mention_count} 次,弹幕现场直接聊到「{sample_text}」。")
|
||||||
|
|
||||||
|
lines.append("【今日笑点】")
|
||||||
|
if peak_buckets:
|
||||||
|
top_bucket = peak_buckets[0]
|
||||||
|
lines.append(
|
||||||
|
f"- {str(top_bucket.get('start_time') or '')[-8:-3]} 前后是最热窗口,弹幕量直接冲到 {int(top_bucket.get('message_count', 0) or 0)} 条。"
|
||||||
|
)
|
||||||
|
if repeated_messages:
|
||||||
|
item = repeated_messages[0]
|
||||||
|
lines.append(f"- 复读冠军是「{str(item.get('text') or '').strip()[:24]}」,一天被刷了 {int(item.get('count', 0) or 0)} 次。")
|
||||||
|
if burst_terms:
|
||||||
|
item = burst_terms[0]
|
||||||
|
lines.append(f"- 情绪词「{str(item.get('text') or '').strip()}」集中爆了 {int(item.get('count', 0) or 0)} 次。")
|
||||||
|
|
||||||
|
lines.append("【弹幕名场面】")
|
||||||
|
for item in representative_messages[:5]:
|
||||||
|
nickname = str(item.get("nickname") or "").strip() or "观众"
|
||||||
|
content = str(item.get("content") or "").strip()
|
||||||
|
if content:
|
||||||
|
lines.append(f"- {nickname}:{content[:44]}")
|
||||||
|
|
||||||
|
lines.append("【梗王榜】")
|
||||||
|
for item in repeated_messages[:3]:
|
||||||
|
lines.append(f"- {str(item.get('text') or '').strip()[:28]}|复读 {int(item.get('count', 0) or 0)} 次")
|
||||||
|
|
||||||
|
lines.append("【收尾播报】")
|
||||||
|
lines.append("- 本地预览版已经把有效信息和乐子一起塞进同一张图里了。")
|
||||||
|
return "\n".join(lines)
|
||||||
|
|
||||||
|
|
||||||
def run_local_test(file_path: str) -> str:
|
def run_local_test(file_path: str) -> str:
|
||||||
helper = _load_helper()
|
helper = _load_helper()
|
||||||
resolved_path = str(Path(file_path).resolve())
|
resolved_path = str(Path(file_path).resolve())
|
||||||
@@ -85,11 +280,36 @@ def run_local_test(file_path: str) -> str:
|
|||||||
return str(output_path)
|
return str(output_path)
|
||||||
|
|
||||||
|
|
||||||
|
def render_fans_preview_from_file(file_path: str) -> str:
|
||||||
|
"""
|
||||||
|
读取本地弹幕文件并直接产出新版粉丝日报 HTML 预览。
|
||||||
|
这样我们每次调整提纯逻辑或模板后,都能用同一条命令快速验收最终展示效果。
|
||||||
|
"""
|
||||||
|
local_result_path = Path(run_local_test(file_path))
|
||||||
|
local_result = json.loads(local_result_path.read_text(encoding="utf-8"))
|
||||||
|
payload = _build_preview_template_payload(local_result)
|
||||||
|
report_text = _build_preview_report_text(payload)
|
||||||
|
report_template = _load_report_template_module()
|
||||||
|
html_content = report_template.render_fans_daily_report_html(
|
||||||
|
payload=payload,
|
||||||
|
fans_report_text=report_text,
|
||||||
|
)
|
||||||
|
|
||||||
|
output_dir = Path(os.getcwd()) / "temp" / "douyu_materials"
|
||||||
|
output_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
file_name = Path(file_path).stem
|
||||||
|
output_path = output_dir / f"{file_name}_fans_template_preview.html"
|
||||||
|
output_path.write_text(html_content, encoding="utf-8")
|
||||||
|
return str(output_path)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
sample_files = [
|
sample_files = [
|
||||||
r"plugins\douyu\danmu_test\52876_20260428.txt",
|
r"plugins\douyu\danmu_test\52876_20260428.txt",
|
||||||
r"plugins\douyu\danmu_test\52876_20260429.txt",
|
r"plugins\douyu\danmu_test\52876_20260429.txt",
|
||||||
]
|
]
|
||||||
for sample in sample_files:
|
for sample in sample_files:
|
||||||
path = run_local_test(sample)
|
result_path = run_local_test(sample)
|
||||||
print(path)
|
preview_path = render_fans_preview_from_file(sample)
|
||||||
|
print(result_path)
|
||||||
|
print(preview_path)
|
||||||
|
|||||||
@@ -105,12 +105,12 @@
|
|||||||
.fans-metric-grid {
|
.fans-metric-grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||||
gap: 14px;
|
gap: 10px;
|
||||||
margin-top: 16px;
|
margin-top: 12px;
|
||||||
}
|
}
|
||||||
.fans-metric-card {
|
.fans-metric-card {
|
||||||
padding: 18px 18px 16px;
|
padding: 14px 15px 12px;
|
||||||
border-radius: 20px;
|
border-radius: 16px;
|
||||||
background: linear-gradient(180deg, rgba(255,255,255,.96), rgba(255,245,239,.92));
|
background: linear-gradient(180deg, rgba(255,255,255,.96), rgba(255,245,239,.92));
|
||||||
border: 1px solid rgba(219,95,65,.12);
|
border: 1px solid rgba(219,95,65,.12);
|
||||||
}
|
}
|
||||||
@@ -120,48 +120,48 @@
|
|||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
}
|
}
|
||||||
.fans-metric-value {
|
.fans-metric-value {
|
||||||
font-size: 28px;
|
font-size: 24px;
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
font-weight: 900;
|
font-weight: 900;
|
||||||
color: #cf593e;
|
color: #cf593e;
|
||||||
word-break: break-all;
|
word-break: break-all;
|
||||||
}
|
}
|
||||||
.fans-metric-hint {
|
.fans-metric-hint {
|
||||||
margin-top: 8px;
|
margin-top: 6px;
|
||||||
font-size: 12px;
|
font-size: 11px;
|
||||||
color: #8f7368;
|
color: #8f7368;
|
||||||
line-height: 1.5;
|
line-height: 1.4;
|
||||||
}
|
}
|
||||||
.section {
|
.section {
|
||||||
margin-top: 18px;
|
margin-top: 14px;
|
||||||
padding: 22px;
|
padding: 16px 18px;
|
||||||
border-radius: 26px;
|
border-radius: 20px;
|
||||||
background: linear-gradient(180deg, rgba(255,255,255,.96), rgba(255,249,244,.94));
|
background: linear-gradient(180deg, rgba(255,255,255,.96), rgba(255,249,244,.94));
|
||||||
border: 1px solid var(--line);
|
border: 1px solid var(--line);
|
||||||
}
|
}
|
||||||
.section-summary-list {
|
.section-summary-list {
|
||||||
margin: 0 0 14px;
|
margin: 0 0 10px;
|
||||||
padding-left: 22px;
|
padding-left: 18px;
|
||||||
}
|
}
|
||||||
.section-summary-list li {
|
.section-summary-list li {
|
||||||
margin: 8px 0;
|
margin: 5px 0;
|
||||||
color: #5a3e37;
|
color: #5a3e37;
|
||||||
font-size: 15px;
|
font-size: 14px;
|
||||||
line-height: 1.76;
|
line-height: 1.58;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
.section-title {
|
.section-title {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 10px;
|
gap: 8px;
|
||||||
margin-bottom: 16px;
|
margin-bottom: 12px;
|
||||||
font-size: 24px;
|
font-size: 21px;
|
||||||
font-weight: 900;
|
font-weight: 900;
|
||||||
color: #5b2d23;
|
color: #5b2d23;
|
||||||
}
|
}
|
||||||
.section-title .icon {
|
.section-title .icon {
|
||||||
width: 14px;
|
width: 12px;
|
||||||
height: 28px;
|
height: 22px;
|
||||||
border-radius: 999px;
|
border-radius: 999px;
|
||||||
background: linear-gradient(180deg, var(--accent), var(--accent-2));
|
background: linear-gradient(180deg, var(--accent), var(--accent-2));
|
||||||
box-shadow: 0 6px 14px rgba(219,95,65,.2);
|
box-shadow: 0 6px 14px rgba(219,95,65,.2);
|
||||||
@@ -169,108 +169,108 @@
|
|||||||
.info-grid {
|
.info-grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||||
gap: 12px;
|
gap: 10px;
|
||||||
}
|
}
|
||||||
.fans-info-card {
|
.fans-info-card {
|
||||||
padding: 16px 16px 15px;
|
padding: 12px 13px;
|
||||||
border-radius: 18px;
|
border-radius: 14px;
|
||||||
background: linear-gradient(180deg, rgba(255,255,255,.98), rgba(255,245,240,.94));
|
background: linear-gradient(180deg, rgba(255,255,255,.98), rgba(255,245,240,.94));
|
||||||
border: 1px solid rgba(219,95,65,.12);
|
border: 1px solid rgba(219,95,65,.12);
|
||||||
min-height: 112px;
|
min-height: 84px;
|
||||||
}
|
}
|
||||||
.fans-info-text {
|
.fans-info-text {
|
||||||
font-size: 15px;
|
font-size: 14px;
|
||||||
line-height: 1.76;
|
line-height: 1.58;
|
||||||
color: #523935;
|
color: #523935;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
.topic-grid {
|
.topic-grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||||
gap: 14px;
|
gap: 10px;
|
||||||
}
|
}
|
||||||
.topic-cluster-card {
|
.topic-cluster-card {
|
||||||
padding: 18px 18px 14px;
|
padding: 13px 14px 11px;
|
||||||
border-radius: 20px;
|
border-radius: 16px;
|
||||||
background: linear-gradient(180deg, rgba(255,255,255,.98), rgba(255,244,237,.94));
|
background: linear-gradient(180deg, rgba(255,255,255,.98), rgba(255,244,237,.94));
|
||||||
border: 1px solid rgba(219,95,65,.12);
|
border: 1px solid rgba(219,95,65,.12);
|
||||||
}
|
}
|
||||||
.topic-cluster-title {
|
.topic-cluster-title {
|
||||||
font-size: 18px;
|
font-size: 16px;
|
||||||
font-weight: 900;
|
font-weight: 900;
|
||||||
color: #6d2f22;
|
color: #6d2f22;
|
||||||
margin-bottom: 8px;
|
margin-bottom: 5px;
|
||||||
}
|
}
|
||||||
.topic-cluster-meta {
|
.topic-cluster-meta {
|
||||||
font-size: 12px;
|
font-size: 11px;
|
||||||
color: #8d6c61;
|
color: #8d6c61;
|
||||||
margin-bottom: 10px;
|
margin-bottom: 7px;
|
||||||
}
|
}
|
||||||
.topic-cluster-list {
|
.topic-cluster-list {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding-left: 18px;
|
padding-left: 16px;
|
||||||
}
|
}
|
||||||
.topic-cluster-list li {
|
.topic-cluster-list li {
|
||||||
margin: 8px 0;
|
margin: 5px 0;
|
||||||
font-size: 14px;
|
font-size: 13px;
|
||||||
line-height: 1.66;
|
line-height: 1.52;
|
||||||
color: #4d3832;
|
color: #4d3832;
|
||||||
}
|
}
|
||||||
.hero-grid {
|
.hero-grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||||
gap: 12px;
|
gap: 10px;
|
||||||
}
|
}
|
||||||
.hero-mention-card {
|
.hero-mention-card {
|
||||||
padding: 16px;
|
padding: 12px 13px;
|
||||||
border-radius: 18px;
|
border-radius: 14px;
|
||||||
background: linear-gradient(135deg, rgba(255,255,255,.98), rgba(255,241,232,.94));
|
background: linear-gradient(135deg, rgba(255,255,255,.98), rgba(255,241,232,.94));
|
||||||
border: 1px solid rgba(243,164,71,.22);
|
border: 1px solid rgba(243,164,71,.22);
|
||||||
}
|
}
|
||||||
.hero-mention-name {
|
.hero-mention-name {
|
||||||
font-size: 17px;
|
font-size: 15px;
|
||||||
line-height: 1.4;
|
line-height: 1.4;
|
||||||
font-weight: 900;
|
font-weight: 900;
|
||||||
color: #6b311f;
|
color: #6b311f;
|
||||||
margin-bottom: 6px;
|
margin-bottom: 4px;
|
||||||
}
|
}
|
||||||
.hero-mention-meta {
|
.hero-mention-meta {
|
||||||
font-size: 12px;
|
font-size: 11px;
|
||||||
color: #8c6d61;
|
color: #8c6d61;
|
||||||
margin-bottom: 8px;
|
margin-bottom: 6px;
|
||||||
}
|
}
|
||||||
.hero-mention-sample {
|
.hero-mention-sample {
|
||||||
font-size: 14px;
|
font-size: 13px;
|
||||||
line-height: 1.65;
|
line-height: 1.5;
|
||||||
color: #533833;
|
color: #533833;
|
||||||
}
|
}
|
||||||
.timeline-layout {
|
.timeline-layout {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: minmax(0, .95fr) minmax(0, 1.05fr);
|
grid-template-columns: minmax(0, .95fr) minmax(0, 1.05fr);
|
||||||
gap: 14px;
|
gap: 10px;
|
||||||
}
|
}
|
||||||
.hot-window-stack,
|
.hot-window-stack,
|
||||||
.repeat-chip-wrap,
|
.repeat-chip-wrap,
|
||||||
.meme-rank-stack {
|
.meme-rank-stack {
|
||||||
display: grid;
|
display: grid;
|
||||||
gap: 10px;
|
gap: 8px;
|
||||||
}
|
}
|
||||||
.fans-hot-window-card,
|
.fans-hot-window-card,
|
||||||
.repeat-digest-panel,
|
.repeat-digest-panel,
|
||||||
.rank-panel {
|
.rank-panel {
|
||||||
padding: 14px 16px;
|
padding: 11px 12px;
|
||||||
border-radius: 18px;
|
border-radius: 14px;
|
||||||
background: linear-gradient(180deg, rgba(255,255,255,.98), rgba(255,245,239,.94));
|
background: linear-gradient(180deg, rgba(255,255,255,.98), rgba(255,245,239,.94));
|
||||||
border: 1px solid rgba(219,95,65,.12);
|
border: 1px solid rgba(219,95,65,.12);
|
||||||
}
|
}
|
||||||
.fans-hot-window-time {
|
.fans-hot-window-time {
|
||||||
font-size: 18px;
|
font-size: 16px;
|
||||||
font-weight: 900;
|
font-weight: 900;
|
||||||
color: #c84f3f;
|
color: #c84f3f;
|
||||||
margin-bottom: 6px;
|
margin-bottom: 4px;
|
||||||
}
|
}
|
||||||
.fans-hot-window-meta {
|
.fans-hot-window-meta {
|
||||||
font-size: 14px;
|
font-size: 12px;
|
||||||
color: #5c433b;
|
color: #5c433b;
|
||||||
}
|
}
|
||||||
.repeat-chip-wrap {
|
.repeat-chip-wrap {
|
||||||
@@ -280,9 +280,9 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
gap: 10px;
|
gap: 8px;
|
||||||
padding: 12px 14px;
|
padding: 9px 11px;
|
||||||
border-radius: 14px;
|
border-radius: 12px;
|
||||||
background: rgba(255,255,255,.9);
|
background: rgba(255,255,255,.9);
|
||||||
border: 1px solid rgba(219,95,65,.1);
|
border: 1px solid rgba(219,95,65,.1);
|
||||||
}
|
}
|
||||||
@@ -290,35 +290,35 @@
|
|||||||
background: rgba(255,247,236,.94);
|
background: rgba(255,247,236,.94);
|
||||||
}
|
}
|
||||||
.repeat-chip-text {
|
.repeat-chip-text {
|
||||||
font-size: 14px;
|
font-size: 12px;
|
||||||
line-height: 1.5;
|
line-height: 1.4;
|
||||||
color: #4d3932;
|
color: #4d3932;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
}
|
}
|
||||||
.repeat-chip-count {
|
.repeat-chip-count {
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
font-size: 12px;
|
font-size: 11px;
|
||||||
color: #a05441;
|
color: #a05441;
|
||||||
font-weight: 800;
|
font-weight: 800;
|
||||||
}
|
}
|
||||||
.two-col {
|
.two-col {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: minmax(0, 1.08fr) minmax(280px, .92fr);
|
grid-template-columns: minmax(0, 1.08fr) minmax(280px, .92fr);
|
||||||
gap: 16px;
|
gap: 12px;
|
||||||
}
|
}
|
||||||
.funny-list {
|
.funny-list {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding-left: 22px;
|
padding-left: 18px;
|
||||||
}
|
}
|
||||||
.funny-list li {
|
.funny-list li {
|
||||||
margin: 10px 0;
|
margin: 6px 0;
|
||||||
color: #533835;
|
color: #533835;
|
||||||
font-size: 15px;
|
font-size: 14px;
|
||||||
line-height: 1.76;
|
line-height: 1.58;
|
||||||
}
|
}
|
||||||
.meme-rank-card {
|
.meme-rank-card {
|
||||||
padding: 14px 16px;
|
padding: 11px 12px;
|
||||||
border-radius: 16px;
|
border-radius: 13px;
|
||||||
background: linear-gradient(135deg, rgba(255,255,255,.98), rgba(255,240,232,.95));
|
background: linear-gradient(135deg, rgba(255,255,255,.98), rgba(255,240,232,.95));
|
||||||
border: 1px solid rgba(242,95,92,.18);
|
border: 1px solid rgba(242,95,92,.18);
|
||||||
}
|
}
|
||||||
@@ -327,35 +327,35 @@
|
|||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
letter-spacing: .08em;
|
letter-spacing: .08em;
|
||||||
font-weight: 900;
|
font-weight: 900;
|
||||||
margin-bottom: 8px;
|
margin-bottom: 5px;
|
||||||
}
|
}
|
||||||
.meme-rank-text {
|
.meme-rank-text {
|
||||||
color: #4b2e2b;
|
color: #4b2e2b;
|
||||||
font-size: 15px;
|
font-size: 13px;
|
||||||
line-height: 1.66;
|
line-height: 1.5;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
}
|
}
|
||||||
.scene-grid {
|
.scene-grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||||
gap: 12px;
|
gap: 10px;
|
||||||
}
|
}
|
||||||
.fans-scene-card {
|
.fans-scene-card {
|
||||||
padding: 16px 16px 15px;
|
padding: 12px 13px;
|
||||||
border-radius: 18px;
|
border-radius: 14px;
|
||||||
background: linear-gradient(180deg, rgba(255,255,255,.98), rgba(255,245,240,.95));
|
background: linear-gradient(180deg, rgba(255,255,255,.98), rgba(255,245,240,.95));
|
||||||
border: 1px solid rgba(255,138,61,.16);
|
border: 1px solid rgba(255,138,61,.16);
|
||||||
min-height: 102px;
|
min-height: 78px;
|
||||||
}
|
}
|
||||||
.fans-scene-quote {
|
.fans-scene-quote {
|
||||||
color: #5b3c37;
|
color: #5b3c37;
|
||||||
font-size: 15px;
|
font-size: 13px;
|
||||||
line-height: 1.75;
|
line-height: 1.54;
|
||||||
}
|
}
|
||||||
.closing-box {
|
.closing-box {
|
||||||
margin-top: 16px;
|
margin-top: 14px;
|
||||||
padding: 20px 22px;
|
padding: 15px 17px;
|
||||||
border-radius: 24px;
|
border-radius: 18px;
|
||||||
background: linear-gradient(135deg, rgba(255,126,103,.1), rgba(243,164,71,.14));
|
background: linear-gradient(135deg, rgba(255,126,103,.1), rgba(243,164,71,.14));
|
||||||
border: 1px solid rgba(255,126,103,.16);
|
border: 1px solid rgba(255,126,103,.16);
|
||||||
}
|
}
|
||||||
@@ -364,16 +364,16 @@
|
|||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
letter-spacing: .08em;
|
letter-spacing: .08em;
|
||||||
font-weight: 900;
|
font-weight: 900;
|
||||||
margin-bottom: 10px;
|
margin-bottom: 6px;
|
||||||
}
|
}
|
||||||
.closing-text {
|
.closing-text {
|
||||||
color: #613b34;
|
color: #613b34;
|
||||||
font-size: 18px;
|
font-size: 15px;
|
||||||
line-height: 1.8;
|
line-height: 1.6;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
}
|
}
|
||||||
.footer-note {
|
.footer-note {
|
||||||
margin-top: 18px;
|
margin-top: 14px;
|
||||||
text-align: right;
|
text-align: right;
|
||||||
color: #8f736c;
|
color: #8f736c;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
|
|||||||
Reference in New Issue
Block a user