Просмотр исходного кода

chore: add genek skills (extract-figures, paper-2-slide, paper-2-web, split-figures)

Made-with: Cursor
zhxd 2 месяцев назад
Сommit
f9e51892fe

+ 176 - 0
genek-extract-figures/SKILL.md

@@ -0,0 +1,176 @@
+---
+name: genek-extract-figures
+description: 从学术论文 PDF 中提取高质量 Figure 图片。优先从出版商网站下载原始图,备选 pdftoppm 整页转图+裁剪。支持 Springer Nature、Elsevier、Wiley、PLOS 等主流出版商。当用户提及"提取论文图片"、"下载论文 figure"、"论文插图"、"extract figures"时使用此技能。
+---
+
+# 学术论文图片提取
+
+## 工作流程
+
+```
+1. 从论文 PDF 中识别 DOI 和 Figure 数量
+   │
+2. 判断出版商 → 选择提取方式
+   ├── 已知出版商 URL 模板 → 方法一:直接下载(推荐)
+   ├── 未知出版商 → 方法二:浏览器访问论文网页版,手动定位图片 URL
+   └── 无法访问网页版 → 方法三:pdftoppm 整页转图 + 裁剪
+   │
+3. 输出到 papers/{Author}{Year}/figures/ 目录,命名 fig1.png ~ figN.png
+4. 删除中间文件,验证文件大小和完整性
+```
+
+## 方法优先级
+
+| 优先级 | 方法 | 质量 | 适用场景 |
+|--------|------|------|----------|
+| **1** | 出版商网站下载 | 最佳,纯图表 | 已发表论文,出版商 URL 可推断 |
+| **2** | 浏览器访问网页版 | 最佳 | 未知出版商,但论文有网页版 |
+| **3** | `pdftoppm` + 裁剪 | 需后处理 | 无法访问网页版(预印本、内部文档等) |
+| **4** | `pdfimages` | 碎片化,不推荐 | 仅作最后手段 |
+
+## 方法一:出版商网站下载(推荐)
+
+### 第一步:识别出版商
+
+从论文 PDF 中提取 DOI(通常在首页页脚或页眉),根据 DOI 前缀判断出版商:
+
+| DOI 前缀 | 出版商 | URL 模板可用 |
+|----------|--------|-------------|
+| `10.1038/` | Springer Nature | ✅ |
+| `10.1186/` | BMC (Springer) | ✅ |
+| `10.1007/` | Springer | ✅ |
+| `10.1016/` | Elsevier | ⚠️ 需从网页解析 |
+| `10.1002/` | Wiley | ⚠️ 需从网页解析 |
+| `10.1371/` | PLOS | ✅ |
+| `10.3390/` | MDPI | ✅ |
+| `10.1093/` | Oxford Univ Press | ⚠️ 需从网页解析 |
+
+### 第二步:构造 URL 并下载
+
+**Springer Nature / BMC 系列**(Nature, BMC Biology, Genome Biology 等):
+
+```
+https://media.springernature.com/full/springer-static/image/art%3A{DOI_ENCODED}/MediaObjects/{JOURNAL_ID}_{YEAR}_{ARTICLE_ID}_Fig{N}_HTML.png
+```
+
+DOI 解析规则(`10.1186/s12915-022-01301-7` 为例):
+- `JOURNAL_ID` = `12915`(`/s` 后到首个 `-` 之间的数字)
+- `YEAR` = `2022`
+- `ARTICLE_ID` = `1301`(去掉末尾修订号 `-7`)
+
+下载脚本(**替换前5个变量为目标论文参数**):
+
+```bash
+#!/bin/bash
+AUTHOR_YEAR="Li2022"                # ← 替换:第一作者姓氏+年份
+DOI="10.1186/s12915-022-01301-7"    # ← 替换
+JOURNAL_ID="12915"                  # ← 替换
+YEAR="2022"                         # ← 替换
+ARTICLE_ID="1301"                   # ← 替换
+FIG_COUNT=6                         # ← 替换为实际 Figure 数量
+
+OUT="papers/${AUTHOR_YEAR}/figures"
+DOI_ENCODED=$(echo "$DOI" | sed 's|/|%2F|g; s|:|%3A|g')
+mkdir -p "$OUT"
+for i in $(seq 1 $FIG_COUNT); do
+  url="https://media.springernature.com/full/springer-static/image/art%3A${DOI_ENCODED}/MediaObjects/${JOURNAL_ID}_${YEAR}_${ARTICLE_ID}_Fig${i}_HTML.png"
+  curl -sL -o "${OUT}/fig${i}.png" "$url"
+  size=$(wc -c < "${OUT}/fig${i}.png" 2>/dev/null || echo 0)
+  if [ "$size" -gt 10000 ]; then
+    echo "fig${i}.png: OK (${size} bytes)"
+  else
+    echo "fig${i}.png: FAILED (${size} bytes) - URL 可能不正确"
+    rm -f "${OUT}/fig${i}.png"
+  fi
+done
+```
+
+其他出版商 URL 模板详见 → [references/publishers.md](references/publishers.md)
+
+## 方法二:浏览器访问网页版
+
+URL 模板未知或下载失败时:
+
+1. 用浏览器打开 `https://doi.org/{DOI}` 跳转到论文网页版
+2. 在文章中找到 Figure,点击 "Full size image" / "Download" / "High resolution"
+3. 用开发者工具(F12 → Network)获取图片真实 URL
+4. 用 `curl -sL -o figures/figN.png "URL"` 逐张下载
+5. **发现新的 URL 规律时**,补充到 [references/publishers.md](references/publishers.md)
+
+## 方法三:PDF 整页转图(备选)
+
+前置条件:`brew install poppler`(macOS)或 `apt install poppler-utils`(Linux)
+
+```bash
+pdftoppm -png -r 300 paper.pdf figures/page
+```
+
+转换后人工确认 Figure 对应的页码,然后重命名:
+
+```bash
+mv figures/page-03.png figures/fig1.png
+mv figures/page-05.png figures/fig2.png
+# ...按实际页码调整
+rm figures/page-*.png
+```
+
+### 裁剪(精度差,尽量避免)
+
+```python
+from PIL import Image
+img = Image.open("figures/fig1.png")
+# (left, upper, right, lower) — 需逐张手动确定坐标
+cropped = img.crop((35, 210, 2445, 2815))
+cropped.save("figures/fig1.png")
+```
+
+已知问题:页眉位置因期刊不同而变化;图注与正文分界不清晰;耗时且效果不稳定。
+
+## 方法四:pdfimages(不推荐)
+
+```bash
+pdfimages -png paper.pdf figures/img
+```
+
+学术 Figure 通常由多个子面板(A, B, C...)+ 向量图形混合存储,`pdfimages` 只能提取嵌入位图,结果碎片化。
+
+## 输出目录规范
+
+每篇论文以 `papers/{Author}{Year}/` 为根目录,PDF 和提取图片均放在该目录下:
+
+```
+papers/
+├── Li2022/
+│   ├── paper.pdf
+│   └── figures/
+│       ├── fig1.png
+│       └── fig2.png
+├── Zhang2023/
+│   ├── paper.pdf
+│   └── figures/
+│       ├── fig1.png
+│       └── fig2.png
+└── Wang2024a/              ← 同姓同年加 a/b 后缀
+    ├── paper.pdf
+    └── figures/
+        └── fig1.png
+```
+
+命名规则:
+- 目录名取第一作者 **姓氏**(首字母大写)+ 发表年份,如 `Li2022`
+- 同姓同年冲突时追加小写字母:`Wang2024a`、`Wang2024b`
+- 从论文 PDF 首页提取作者和年份信息
+
+### 文件命名
+
+- 主图:`fig1.png` ~ `fig{N}.png`
+- 补充图:`sfig1.png` ~ `sfig{N}.png`
+- 删除所有中间文件(`page-*.png`、`tmp-*.png`、`img-*.png`)
+
+## 验证清单
+
+- [ ] 每张图片 > 10KB(空文件说明 URL 错误)
+- [ ] 图片数量与论文 Figure 数一致
+- [ ] 图片内容是纯图表(无页眉页脚、无正文)
+- [ ] 命名连续且与论文编号对应
+- [ ] 多论文时各子目录命名正确(作者姓氏+年份)

+ 136 - 0
genek-extract-figures/references/publishers.md

@@ -0,0 +1,136 @@
+# 出版商图片 URL 模板
+
+> 遇到新出版商时,访问论文网页版,查找 "Full size image" 链接,
+> 用浏览器 DevTools 获取图片真实 URL,总结模板后补充到此文档。
+
+## Springer Nature / BMC
+
+适用:Nature, Nature Methods, BMC Biology, Genome Biology, Scientific Reports 等。
+
+**URL 格式**:
+
+```
+https://media.springernature.com/full/springer-static/image/art%3A{DOI_ENCODED}/MediaObjects/{JOURNAL_ID}_{YEAR}_{ARTICLE_ID}_Fig{N}_HTML.png
+```
+
+**DOI 解析规则**:
+
+```
+DOI:  10.1186/s12915-022-01301-7
+               ^^^^^  ^^^^  ^^^^
+               |      |     |
+               |      |     ARTICLE_ID = 1301(去掉 -7 修订号)
+               |      YEAR = 2022
+               JOURNAL_ID = 12915(/s 后到首个 - 之间)
+```
+
+| DOI 示例 | JOURNAL_ID | YEAR | ARTICLE_ID |
+|----------|-----------|------|------------|
+| `10.1186/s12915-022-01301-7` | 12915 | 2022 | 1301 |
+| `10.1038/s41586-023-06004-9` | 41586 | 2023 | 6004 |
+| `10.1186/s13059-021-02500-z` | 13059 | 2021 | 2500 |
+| `10.1038/s41467-024-45678-3` | 41467 | 2024 | 45678 |
+
+**注意**:部分旧文章或 review 的 URL 可能不符合此模板,需回退到浏览器方式。
+
+---
+
+## PLOS
+
+适用:PLOS ONE, PLOS Biology, PLOS Genetics 等。
+
+**URL 格式**:
+
+```
+https://journals.plos.org/plosone/article/figure/image?id=10.1371/journal.pone.{ARTICLE_ID}.g{NNN}
+```
+
+其中 `NNN` 为 3 位数 Figure 编号(`001`, `002`, ...)。
+
+示例(DOI `10.1371/journal.pone.0250000`):
+
+```bash
+for i in $(seq 1 5); do
+  n=$(printf "%03d" $i)
+  curl -sL -o "figures/fig${i}.png" \
+    "https://journals.plos.org/plosone/article/figure/image?id=10.1371/journal.pone.0250000.g${n}"
+done
+```
+
+**注意**:URL 中的期刊名需匹配(`plosone` / `plosbiology` / `plosgenetics` 等)。
+
+---
+
+## MDPI
+
+适用:所有 MDPI 期刊(Cells, IJMS, Genes, Cancers 等)。
+
+**URL 格式**:
+
+```
+https://www.mdpi.com/{JOURNAL_PATH}/{VOLUME}/{ISSUE}/{ARTICLE_ID}/{ARTICLE_SLUG}-g{NNN}.png
+```
+
+MDPI 文章网页版图片直接嵌入且可右键保存,URL 规律性强。建议直接访问网页版下载。
+
+---
+
+## Elsevier (Cell, Lancet, etc.)
+
+**无固定 URL 模板**,图片通过 CDN 分发(`ars.els-cdn.com`),路径含随机 hash。
+
+提取策略:
+1. 访问 `https://doi.org/{DOI}` → 跳转到 ScienceDirect 文章页
+2. 点击 Figure → "Download high-res image"
+3. 从 DevTools Network 面板复制图片 URL
+4. URL 通常形如:`https://ars.els-cdn.com/content/image/1-s2.0-S{ID}-gr{N}_lrg.jpg`
+
+---
+
+## Wiley
+
+**无固定 URL 模板**。
+
+提取策略:
+1. 访问 `https://doi.org/{DOI}` → 跳转到 Wiley Online Library
+2. 文章页内图片通常可直接右键保存
+3. 高分辨率图片需点击 "Open in figure viewer" → "Download PowerPoint"
+
+---
+
+## Oxford University Press (OUP)
+
+适用:Nucleic Acids Research, Bioinformatics 等。
+
+**URL 格式**(部分期刊):
+
+```
+https://oup.silverchair-cdn.com/oup/backfile/Content_public/Journal/{JOURNAL}/{VOLUME}/{ISSUE}/{DOI_SUFFIX}/{FIGURE_FILE}
+```
+
+路径不稳定,建议优先从文章网页版下载。
+
+---
+
+## 预印本(bioRxiv / medRxiv)
+
+**URL 格式**:
+
+```
+https://www.biorxiv.org/content/biorxiv/early/{YEAR}/{MONTH}/{DAY}/{ID}/F{N}.large.jpg
+```
+
+预印本图片质量通常较低(JPEG 压缩),建议:
+1. 若已正式发表,从正式出版商下载
+2. 若仅预印本可用,从网页版下载 `.large.jpg` 版本
+
+---
+
+## 通用回退策略
+
+当上述模板均不适用时:
+
+1. **浏览器访问** `https://doi.org/{DOI}`
+2. **搜索页面** 中的 "Full size image"、"Download figure"、"High resolution" 链接
+3. **DevTools (F12)** → Network → 过滤 `image` → 刷新页面 → 找到图片请求
+4. **记录 URL 规律** → 补充到本文档对应出版商章节

+ 219 - 0
genek-paper-2-slide/SKILL.md

@@ -0,0 +1,219 @@
+---
+name: genek-paper-2-slide
+description: 从论文生成中英文两版 PPTX 演示文稿(presentation_en.pptx + presentation_cn.pptx)。每张 panel 子图对应一页幻灯片,含详细解读。当用户提及"论文转PPT"、"论文转幻灯片"、"学术演示"、"presentation"时使用此技能。
+---
+
+# Paper2Slide — 论文 → PPTX 演示文稿(中英文双版本)
+
+从单篇论文生成**中英文两版**结构化 PPTX 演示文稿。PPT 使用**拆分后的 panel 图**(`figures/panels/`),每张子图对应一页幻灯片,逐张讲解。两版 PPT 页数、页序、图片完全一致,仅文字语言不同。
+
+## 适用边界(职责分层)
+
+- `genek-paper-2-slide` 定义 **What**(内容结构、映射规则、命名规范、验收门禁)
+- `pptx` skill 负责 **How**(具体排版渲染与 PPT 文件生成)
+- `genek-paper-2-slide` 不重复实现 `pptx` 内部逻辑,只通过契约调用
+- 不涉及 HTML 海报生成(海报由 `genek-paper-2-web` 负责)
+
+## 目录结构
+
+每篇论文以 **第一作者姓氏 + 年份** 命名独立子目录:
+
+```
+papers/
+└── {Author}{Year}/
+    ├── paper.pdf
+    ├── figures/
+    │   ├── fig1.png ... figN.png            (原图,genek-extract-figures)
+    │   └── panels/
+    │       ├── fig1_A.png                   (拆分后的 panel)
+    │       ├── fig1_BC.png
+    │       └── ...
+    ├── create_pptx.js
+    ├── presentation_en.pptx                 (英文版,本 skill 产物)
+    └── presentation_cn.pptx                 (中文版,本 skill 产物)
+```
+
+## 内容源
+
+- 从 `paper.pdf` 直接抽取结构化内容,不依赖 `poster.html` 或其他中间产物
+
+## 执行流程
+
+| 步骤 | 目标 | 输入 | 输出 | 成功判据 |
+|------|------|------|------|----------|
+| **S0** | 建立论文目录 | `paper.pdf` | `papers/{Author}{Year}/` | 目录结构正确 |
+| **S1** | 抽取结构化内容 | `paper.pdf` | 标题/作者/Background/Design/Methods/Results/Conclusions | 字段齐全,无空段 |
+| **S2** | 提取原图 | `paper.pdf` | `figures/fig1..figN.png` | 图片数与主图数一致 |
+| **S3** | 拆分 panel | `figures/figN.png` | `figures/panels/fig{N}_{group}.png` | **必须**执行 `genek-split-figures`,产出真实子图;禁止整图占位(如 `_whole`) |
+| **S4** | 生成 PPT(双版本) | 结构化内容 + panel 图 | `presentation_en.pptx` + `presentation_cn.pptx` | 两版均可打开,页序与映射一致 |
+| **S5** | QA 门禁 | 两版 pptx | 通过/不通过 | 两版均满足本文件 QA Gate |
+
+跳过策略:
+- S2:若 `figures/fig{N}.png` 已存在且连续完整,跳过提取
+- S3:**禁止跳过**。必须按 `genek-split-figures` 技能**严格执行**:对每张 `figures/fig{N}.png` 进行 LLM 看图 → 分组与粗坐标 → 像素精修 → 按 panel 裁剪,输出 `fig{N}_{group}.png`(group 为论文图中真实 panel 标签,如 A、BC、DEFG)。**禁止**将整图复制为 `fig{N}_whole.png`、`fig{N}_full.png` 等占位文件以代替拆分;凡仅以整图充当单一「panel」的,S3 视为未执行,不得进入 S4。
+
+失败策略:
+- S2 失败:回退到 `genek-extract-figures` 的备选路径
+- S3 失败:允许人工校正 panel 分组后重跑
+- S4 失败:返回到 PPT 契约检查,定位为"输入缺失"或"渲染失败"。若其中一版生成成功、另一版失败,仍判定为 S4 失败,需两版都通过
+
+## 图片使用规则
+
+| 格式 | 使用图片 | 路径 | Result 对应 |
+|------|---------|------|------------|
+| presentation.pptx | **panel 图** | `figures/panels/fig{N}_{panels}.png` | **1 张子图 = 1 页 PPT** |
+
+每页 PPT 对应一张拆分后的 panel 子图,逐张讲解。一张 Figure 可能拆分为多个 panel,因此 PPT 页数通常多于论文主图数。
+
+### Panel 命名规范
+
+- 单 panel:`fig1_A.png`
+- 多 panel 合并为一组:`fig1_BC.png`、`fig1_DEFG.png`
+
+## PPT 调用契约(核心)
+
+### 1) 固定输入契约
+
+- 论文目录:`papers/{Author}{Year}/`
+- panel 输入:`papers/{Author}{Year}/figures/panels/*.png`
+- 命名格式:`fig{N}_{GROUP}.png`
+- 结构化内容必须包含:
+  - `title`, `authors`, `affiliation`
+  - `background`, `study_design`, `methods`, `results[]`, `conclusions[]`
+  - `results[]` 中每项需含 `result_id`, `headline`, `key_points`, `source_figure`
+
+### 2) 固定输出契约
+
+- 产物(必须同时交付两版):
+  - `papers/{Author}{Year}/presentation_en.pptx`(英文版)
+  - `papers/{Author}{Year}/presentation_cn.pptx`(中文版)
+- 平台兼容产物(macOS 默认启用,两版各一份):
+  - `papers/{Author}{Year}/presentation_en_keynote_compatible.pptx`
+  - `papers/{Author}{Year}/presentation_cn_keynote_compatible.pptx`
+- 两版共享:相同页数、相同页序、相同图片、相同配色/版式
+- 页序:
+  1. Title
+  2. Background
+  3. Study Design
+  4. Methods
+  5. Result Panels(1 panel = 1 页)
+  6. Conclusion
+  7. End / Acknowledgment(可与 Conclusion 合并)
+
+### 3) 各页内容规格
+
+- **Title**:论文标题、作者列表、单位、期刊/年份
+- **Background**(1–2 页):领域背景 2–3 句;高亮知识缺口/未解决问题(加粗框);本研究切入点
+- **Study Design**(1 页):Stat box 关键数字 + 汇总表;多物种时含 cladogram
+- **Methods**(1–2 页):按 3 阶段组织(实验 → 数据处理 → 分析);每步标注软件工具名;推荐流程图/SmartArt
+- **Conclusions**(1 页):编号列表,每条 1–2 句,与 Result 发现对应;可选 Future Directions
+
+### 4) panel → slide 映射规则
+
+- 规则:**1 张 panel 图 = 1 张 Result 幻灯片**
+- 排序:先按 `figN` 数字升序,再按 `GROUP` 字典序(A < B < BC < DEFG)
+- 推荐标题:`Result {k}: {headline}`,副标题显示来源 `Figure {N} / Panel {GROUP}`
+- 详细解读要求:
+  - **必选 3 条**:What is shown(该子图展示了什么数据/现象)、Evidence(关键数值、统计关系或对照结果)、Take-home message(一句话结论)
+  - **可选**(按论文类型选取):Biological interpretation、Cross-species implication、Clinical significance、Computational novelty 等
+- 若 `results[]` 条目数与 panel 数不一致:
+  1. 若 panel 更多:允许复用同一 `headline`,但必须在 notes 标注"same finding, different panel"
+  2. 若 panel 更少:阻断交付,返回"不通过(输入不完整)"
+
+### 5) 双版本语言契约
+
+输出两个独立 PPT 文件,而非在同一文件内混合双语:
+
+| 版本 | 文件名 | 幻灯片正文 | Speaker Notes |
+|------|--------|-----------|---------------|
+| 英文版 | `presentation_en.pptx` | English | English(可选附中文摘要) |
+| 中文版 | `presentation_cn.pptx` | 中文 | 中文 |
+
+- **英文版**:所有标题、要点、表格、结论均为英文。专业术语保留原文。Speaker notes 用英文书写。
+- **中文版**:所有标题、要点、表格、结论翻译为中文。学术术语可保留英文原文并括注中文(如 "TADs (拓扑关联结构域)")。Speaker notes 用中文书写,适合教学演讲。
+- **图片标注不翻译**:panel 图本身为论文原图,内部标注保持英文原样,两版共用相同图片文件。
+- **实现建议**:在 `create_pptx.js` 中准备中英文两套文本数据结构,通过循环或函数参数化生成两版 PPT,避免手动维护两份独立代码。
+
+### 6) 错误与降级契约
+
+- 缺失 panel 文件:禁止"空白页占位",直接 fail-fast
+- panel 文件损坏:跳过该文件并计入错误,最终判定不通过
+- 渲染后页数不一致:按 Gate 判定不通过,不可直接交付
+- **两版一致性**:英文版和中文版的页数必须相等,panel 图顺序必须相同。若一版渲染失败,两版均判定不通过
+- macOS 导出策略(两版各执行一次):
+  - 先生成标准 `presentation_en.pptx` / `presentation_cn.pptx`
+  - 默认执行一次兼容中转导出,生成 `*_keynote_compatible.pptx`
+
+## 项目设计(Study Design)
+
+从论文 Methods/Results 中提取实验设计信息,以**表格 + 关键统计数字**呈现。
+
+常见字段(按论文实际内容选取):
+
+| 字段 | 适用场景 | 示例 |
+|------|---------|------|
+| 物种 (Species) | 动物/植物/微生物研究 | Human, Mouse, Chicken 等 |
+| 样本类型 (Sample) | 所有实验研究 | Fibroblast cell lines, Tumor tissue, Serum |
+| 样本数量 (Replicates) | 所有实验研究 | 1–5 replicates per species |
+| 患者/个体 (Cohort) | 临床/群体研究 | 500 patients, 1200 healthy controls |
+| 实验技术 (Technology) | 所有组学研究 | Hi-C, RNA-seq, ATAC-seq, WGS |
+| 测序平台 (Platform) | 测序类研究 | Illumina HiSeq X Ten |
+| 测序量 (Sequencing depth) | 测序类研究 | ~230M contacts per library |
+| 分辨率 (Resolution) | 组学数据分析 | 20 kb, single-cell |
+| 参考基因组 (Reference) | 基因组学研究 | GRCh38, GRCm38 等 |
+| 时间点 (Timepoints) | 纵向/发育研究 | E8.5, E10.5, E14.5, P0, Adult |
+| 处理条件 (Treatment) | 干预/药物研究 | Control vs. Drug (10 μM, 48h) |
+
+如论文涉及多物种/多样本,优先提取为一张汇总表。
+
+### 多物种研究的进化关系要求
+
+当论文涉及 **≥3 个物种的跨物种比较** 时,Study Design 幻灯片中必须包含:
+
+1. **简易系统发育树(cladogram)**:用图形/SmartArt 展示物种间拓扑关系
+2. **类群分组着色**:用颜色区分主要分类群(如哺乳类/鸟类/鱼类)
+3. **外群标识**:明确标出外群物种
+
+不涉及跨物种比较的论文不需要 cladogram。
+
+## 依赖 skill
+
+- **图片提取**:`genek-extract-figures` → 原图到 `figures/`
+- **Panel 拆分**:`genek-split-figures` → 子图到 `figures/panels/`
+- **PPT 生成**:`pptx` skill(pptxgenjs)→ 生成 .pptx
+
+## 组件与参考
+
+- PPT 调用手册(参数、重试、问题排查)→ [references/pptx-playbook.md](references/pptx-playbook.md)
+- QA 门禁清单 → [references/qa-checklists.md](references/qa-checklists.md)
+
+## QA 门禁(交付前必须通过)
+
+1) 资产完整性
+- `figures/panels/*.png` 存在且非空
+- **Panel 必须来自真实拆分**:文件名须为 `fig{N}_{group}.png`,其中 `group` 为论文图中实际存在的 panel 标签(单字母或字母组合,如 A、BC、DEFG)。**禁止**使用 `_whole`、`_full`、`_all` 等占位组名;若存在此类文件,QA 不通过。
+- 不存在临时/验证残留文件
+
+2) 内容完整性(两版均需满足)
+- 每版 PPT 含 Title / Background / Study Design / Methods / Result / Conclusion
+- 每个 Result 页至少包含:标题、图、详细解读(建议 4-5 条)
+- 无占位文本
+
+3) 一致性校验
+- 两版 PPT Result 页数 = panel 图数量
+- 编号顺序一致,不跳号
+- 每页可追溯到对应 Figure/Panel(标题或备注中标记来源)
+- **英文版与中文版页数相同、panel 图顺序相同**
+
+4) 双版本语言
+- 英文版:所有幻灯片正文为英文
+- 中文版:所有幻灯片正文为中文(学术术语可保留英文括注)
+- 两版 Speaker Notes 分别使用对应语言
+
+5) 版式质量底线(两版均需满足)
+- 无明显文本溢出/重叠
+- 图片未被拉伸变形
+- 无空白结果页、无破图
+
+6) 交付清单
+- 必交付:`presentation_en.pptx` + `presentation_cn.pptx`

+ 100 - 0
genek-paper-2-slide/references/pptx-playbook.md

@@ -0,0 +1,100 @@
+# PPTX 生成操作手册(Paper2All)
+
+本手册用于 `genek-paper-2-slide` 调用 `pptx` skill 时的最小执行标准。  
+注意:本手册只定义论文场景规则,不覆盖 `pptx` skill 的通用排版细节。
+
+## 1. 参数约定
+
+建议统一使用以下参数:
+
+| 参数 | 必填 | 默认值 | 说明 |
+|------|------|--------|------|
+| `AUTHOR_YEAR` | 是 | - | 例如 `Li2022` |
+| `LANG_MODE` | 否 | `en_notes_zh` | `en_notes_zh` 或 `dual_slides` |
+| `THEME` | 否 | `scientific_clean` | 与论文主题一致的配色 |
+| `PANEL_SORT` | 否 | `fig_then_group` | `figN` 升序,再 `GROUP` 升序 |
+| `NOTES_MODE` | 否 | `required_zh` | `required_zh` / `optional_zh` |
+
+## 2. 生成前检查(Preflight)
+
+必须全部通过再进入 PPT 生成:
+
+1. 目录存在:`papers/{AUTHOR_YEAR}/`
+2. panel 目录存在:`papers/{AUTHOR_YEAR}/figures/panels/`
+3. panel 命名合法:`fig{N}_{GROUP}.png`
+4. 结构化内容齐全:`title/authors/background/study_design/methods/results/conclusions`
+5. `results` 至少 1 条,且每条有 `headline` 和 `key_points`
+
+## 3. 生成规则(最小)
+
+固定页序建议:
+
+1. Title
+2. Background
+3. Study Design
+4. Methods
+5. Result slides(每张 panel 一页)
+6. Conclusion
+7. End / Acknowledgment
+
+Result 页要求:
+
+- 每页绑定唯一 panel 文件
+- 页标题必须可追溯:`Result {k}` + `Figure {N}/Panel {GROUP}`
+- 每页 2-3 条要点(避免整段长文本)
+
+## 4. 失败重试策略
+
+### A. panel 缺失或不合法
+
+- 行为:立即停止(fail-fast)
+- 处理:回到 panel 拆分流程,修复后重试
+
+### B. 页数与 panel 数不一致
+
+- 行为:判定失败,不交付
+- 处理:检查排序和过滤逻辑,确保 1:1 映射
+
+### C. 中文 notes 缺失(默认模式)
+
+- 行为:判定失败
+- 处理:补齐对应页 notes 后重试导出
+
+## 5. 常见问题速查
+
+| 问题 | 根因 | 修复动作 |
+|------|------|----------|
+| panel 顺序错乱 | 文件排序规则未统一 | 强制 `fig_then_group` 排序 |
+| 图片拉伸 | 未保持纵横比 | 统一等比缩放,保留白边 |
+| 文本溢出 | 单页信息过多 | 压缩要点到 2-3 条,长句拆分 |
+| notes 缺中文 | 仅填英文正文 | 增加中文 notes 校验步骤 |
+| 空白结果页 | panel 路径引用失败 | 检查路径和文件存在性 |
+
+## 6. 最小调用示例(伪流程)
+
+1) 从 `papers/{AUTHOR_YEAR}/figures/panels/` 收集并排序 panel 列表  
+2) 组装幻灯片数据:`meta + sections + result_panels`  
+3) 调用 `pptx` skill 生成 `presentation.pptx`  
+4) 执行 QA:页数、映射、可读性、notes 完整性  
+5) 通过后与 `poster.html` 一并交付
+
+## 7. macOS 兼容导出(默认)
+
+当运行环境为 macOS 时,默认执行兼容导出,减少 Keynote 打开失败风险。
+
+### 标准流程
+
+1. 先生成标准文件:`presentation.pptx`  
+2. 使用 LibreOffice/soffice 进行兼容中转,输出:`presentation_keynote_compatible.pptx`  
+3. 若仍不兼容,再导出:`presentation_keynote_compatible.ppt`
+
+### 建议命令(示例)
+
+```bash
+soffice --headless --convert-to ppt --outdir "papers/${AUTHOR_YEAR}" "papers/${AUTHOR_YEAR}/presentation.pptx"
+soffice --headless --convert-to pptx --outdir "papers/${AUTHOR_YEAR}" "papers/${AUTHOR_YEAR}/presentation_keynote_compatible.ppt"
+```
+
+说明:
+- 兼容中转后务必保留原始 `presentation.pptx`,避免信息丢失
+- 交付时优先提供 `presentation_keynote_compatible.pptx`

+ 44 - 0
genek-paper-2-slide/references/qa-checklists.md

@@ -0,0 +1,44 @@
+# Paper2Slide QA 门禁清单
+
+交付前,按以下清单逐项核对。任一硬门禁失败即不允许交付。
+
+## A. 资产门禁(Hard Gate)
+
+- [ ] `paper.pdf` 存在于 `papers/{AuthorYear}/`
+- [ ] `figures/fig{N}.png` 连续、无缺号、可打开
+- [ ] `figures/panels/*.png` 存在且文件非空
+- [ ] 无临时残留:`tmp-*`、`page-*`、`*_verify.png`
+
+## B. 内容门禁(Hard Gate)
+
+- [ ] `presentation.pptx` 包含:Title / Background / Study Design / Methods / Result / Conclusion
+- [ ] 每个 Result 幻灯片包含:标题 + 图 + 2-3 条要点
+- [ ] 无占位文本(例如 lorem、xxxx、template)
+
+## C. 一致性门禁(Hard Gate)
+
+- [ ] PPT Result 页数 = panel 文件数
+- [ ] Result 页编号连续且与 panel 顺序一致
+- [ ] Figure/Panel 溯源信息可追踪(至少在标题或备注中体现)
+
+## D. 双语门禁(Conditional Gate)
+
+默认模式 `en_notes_zh`:
+- [ ] 所有页面英文正文可读
+- [ ] 所有页面中文 notes 非空
+
+双套模式 `dual_slides`:
+- [ ] EN 套与 ZH 套页数一致
+- [ ] 同页内容语义一致,无遗漏
+
+## E. 视觉门禁(Soft Gate,建议全通过)
+
+- [ ] 无明显文字溢出/重叠
+- [ ] 图片未被拉伸变形
+- [ ] 字体大小在可读范围(正文建议 >= 14pt)
+- [ ] 关键信息未被页脚或图形遮挡
+
+## F. 交付清单(Hard Gate)
+
+- [ ] `presentation.pptx`
+- [ ] macOS 环境下额外交付 `presentation_keynote_compatible.pptx`

+ 156 - 0
genek-paper-2-web/SKILL.md

@@ -0,0 +1,156 @@
+---
+name: genek-paper-2-web
+description: 从论文生成 HTML 海报(poster.html)。三列 Grid 布局 + 交互组件,原图展示,中英双语切换。当用户提及"论文转海报"、"学术海报"、"论文转网页"、"poster"时使用此技能。
+---
+
+# Paper2Web — 论文 → HTML 海报
+
+从单篇论文生成一份交互式 HTML 学术海报。海报使用 **原图**(`figures/fig{N}.png`),采用三列 Grid 布局 + Lightbox + 中英双语切换。
+
+## 适用边界
+
+- `genek-paper-2-web` 定义海报的内容结构、布局规则、组件用法与验收门禁
+- 不涉及 PPT 生成(PPT 由 `genek-paper-2-slide` 负责)
+- 图片提取与 panel 拆分由上游 skill 完成
+
+## 目录结构
+
+每篇论文以 **第一作者姓氏 + 年份** 命名独立子目录:
+
+```
+papers/
+└── {Author}{Year}/
+    ├── paper.pdf
+    ├── figures/
+    │   ├── fig1.png ... figN.png            (原图,genek-extract-figures)
+    │   └── panels/                          (panel 图,genek-split-figures)
+    └── poster.html                          (本 skill 产物)
+```
+
+## 执行流程
+
+| 步骤 | 目标 | 输入 | 输出 | 成功判据 |
+|------|------|------|------|----------|
+| **S0** | 建立论文目录 | `paper.pdf` | `papers/{Author}{Year}/` | 目录结构正确 |
+| **S1** | 抽取结构化内容 | `paper.pdf` | 标题/作者/Background/Design/Methods/Results/Conclusions | 字段齐全,无空段 |
+| **S2** | 提取原图 | `paper.pdf` | `figures/fig1..figN.png` | 图片数与主图数一致 |
+| **S3** | 生成海报 | 结构化内容 + 原图 | `poster.html` | 页面可打开,图文完整 |
+| **S4** | QA 门禁 | poster.html | 通过/不通过 | 满足本文件 QA Gate |
+
+失败策略:
+- S2 失败:回退到 `genek-extract-figures` 的备选路径
+
+## 图片使用规则
+
+| 格式 | 使用图片 | 路径 | Result 对应 |
+|------|---------|------|------------|
+| poster.html | **原图** | `figures/fig{N}.png` | **1 张原图 = 1 个 Result 卡片** |
+
+每个 Result 卡片对应论文的一张完整 Figure 原图。Result 数量 = 论文主图数量(通常 4–7 张)。海报空间充裕,用原图展示完整 Figure 更直观。
+
+## 项目设计(Study Design)
+
+从论文 Methods/Results 中提取实验设计信息,以**表格 + 关键统计数字**呈现。
+
+常见字段(按论文实际内容选取):
+
+| 字段 | 适用场景 | 示例 |
+|------|---------|------|
+| 物种 (Species) | 动物/植物/微生物研究 | Human, Mouse, Chicken 等 |
+| 样本类型 (Sample) | 所有实验研究 | Fibroblast cell lines, Tumor tissue, Serum |
+| 样本数量 (Replicates) | 所有实验研究 | 1–5 replicates per species |
+| 患者/个体 (Cohort) | 临床/群体研究 | 500 patients, 1200 healthy controls |
+| 实验技术 (Technology) | 所有组学研究 | Hi-C, RNA-seq, ATAC-seq, WGS |
+| 测序平台 (Platform) | 测序类研究 | Illumina HiSeq X Ten |
+| 测序量 (Sequencing depth) | 测序类研究 | ~230M contacts per library |
+| 分辨率 (Resolution) | 组学数据分析 | 20 kb, single-cell |
+| 参考基因组 (Reference) | 基因组学研究 | GRCh38, GRCm38 等 |
+| 时间点 (Timepoints) | 纵向/发育研究 | E8.5, E10.5, E14.5, P0, Adult |
+| 处理条件 (Treatment) | 干预/药物研究 | Control vs. Drug (10 μM, 48h) |
+
+如论文涉及多物种/多样本,优先提取为一张汇总表。
+
+### 多物种研究的进化关系要求
+
+当论文涉及 **≥3 个物种的跨物种比较** 时,Study Design 中必须包含:
+
+1. **简易系统发育树(cladogram)**:用 CSS 组件展示物种间拓扑关系
+2. **类群分组着色**:用颜色区分主要分类群
+3. **外群标识**:明确标出外群物种
+
+组件实现见 [poster-components.md → Cladogram](references/poster-components.md)。
+
+不涉及跨物种比较的论文不需要 cladogram。
+
+## 海报布局规则
+
+### 三列 Grid 布局原则
+
+海报采用 48in 宽、3 列 grid。各区域固定分配:
+
+```
+         Col 1                Col 2                    Col 3
+Header   ──────────────────── 全宽 ─────────────────────
+Row 1    Background           Study Design (跨 2–3 列)
+Row 2    Methods              Result 1                 Result 2
+Row 3    Conclusions          Result 3                 Result 4
+Row 4                         Result 5                 Result 6
+Footer   ──────────────────── 全宽 ─────────────────────
+```
+
+**第 1 行 = 概览行**:Background(Col 1)和 Study Design(Col 2–3)并排。Study Design 跨 2 列以容纳表格和 cladogram。
+
+**第 1 列 Row 2 = Methods**:使用**分阶段流程图(Pipeline)**替代文字。分 3 个视觉阶段:Stage 1(实验,深色渐变)→ Stage 2(数据处理,浅灰 + tool-tag 标签)→ Stage 3(多维分析,白底蓝边分组框 + 2×N 分析卡片网格)。每步必须标注所使用的**软件工具名称**。详见 [poster-components.md → 分阶段流程图](references/poster-components.md)。
+
+**第 1 列 Row 3 = Conclusions**:深色渐变背景 + 编号列表,作为第 1 列的视觉终点。
+
+**第 2–3 列 = Result 区域**:从 Row 2 开始,严格按 **从左到右、从上到下** 排列:
+- 2 个 Result → 1 行 × 2 列
+- 3–4 个 → 2 行 × 2 列(奇数时最后一行只占 1 格)
+- 5–6 个 → 3 行 × 2 列
+- ≥7 个 → 考虑合并相近的 Result
+
+### 视觉风格
+
+- 现代交互风
+- 每个 Result 使用**独立 accent 色**作为顶部边框,通过颜色区分不同发现
+- Result 卡片带**编号圆标**(绝对定位),引导阅读顺序
+- 关键 Figure **默认展开**(`<details open>`),次要 Figure 折叠
+- Conclusions 使用**深色渐变背景 + 编号列表**
+- Footer 包含:数据可用性、致谢/资助、通讯作者联系方式
+
+## 双语要求
+
+- `data-lang="en"` / `data-lang="zh"` 标记文本
+- 右上角语言切换按钮,默认英文
+- 无需双语标记的元素:数字/统计值、图片、DOI、作者姓名
+
+## 依赖 skill
+
+- **图片提取**:`genek-extract-figures` → 原图到 `figures/`
+
+## 组件与参考
+
+- 海报布局与交互组件 → [references/poster-components.md](references/poster-components.md)
+- QA 门禁清单 → [references/qa-checklists.md](references/qa-checklists.md)
+
+## QA 门禁(交付前必须通过)
+
+1) 资产完整性
+- `figures/fig{N}.png` 连续且可读
+- 不存在临时/验证残留文件
+
+2) 内容完整性
+- `poster.html` 含 Background / Study Design / Methods / Results / Conclusions / Footer
+- 无占位文本(lorem、xxxx、template)
+
+3) 版式质量底线
+- 无明显文本溢出/重叠
+- 无空白 Result 卡片、无破图
+
+4) 双语
+- EN/ZH 切换功能正常
+- 默认英文显示
+
+5) 交付清单
+- 必交付:`poster.html`

+ 776 - 0
genek-paper-2-web/references/poster-components.md

@@ -0,0 +1,776 @@
+# HTML 海报可复用组件库
+
+## CSS 变量(配色方案)
+
+基础色板,根据论文或机构风格调整:
+
+```css
+:root {
+  --primary: #1B3A4B;
+  --secondary: #065A82;
+  --accent: #00A7E1;
+  --warm: #E8927C;
+  --gold: #D4A574;
+  --bg-light: #F4F7F9;
+  --bg-cream: #FAFBFC;
+  --text-dark: #1A1A2E;
+  --text-muted: #5A6B7D;
+}
+```
+
+### Result 独立 accent 色
+
+每个 Result 使用不同的 accent 色作为顶部边框和编号圆标背景,帮助读者区分不同发现。按论文主题选色,例如:
+
+```css
+:root {
+  --r1-color: #1B3A4B;
+  --r2-color: #065A82;
+  --r3-color: #2E8B57;
+  --r4-color: #00A7E1;
+  --r5-color: #D4A574;
+  --r6-color: #E8927C;
+}
+.result-1 { border-top-color: var(--r1-color); }
+.result-2 { border-top-color: var(--r2-color); }
+/* ... */
+```
+
+---
+
+## 三列 Grid 布局
+
+```css
+.poster { width: 48in; background: var(--bg-cream); }
+
+.content {
+  display: grid;
+  grid-template-columns: 1fr 1fr 1fr;
+  gap: 0.3in;
+  padding: 0.45in 0.55in;
+}
+```
+
+### 标准布局(6 个 Result 示例)
+
+第 1 列 = 叙事列,第 2–3 列 = Result + Conclusions。
+
+```css
+.background    { grid-column: 1; grid-row: 1; }
+.study-design  { grid-column: 1; grid-row: 2; }
+.methods       { grid-column: 1; grid-row: 3; }
+
+.result-1      { grid-column: 2; grid-row: 1; }
+.result-2      { grid-column: 3; grid-row: 1; }
+.result-3      { grid-column: 2; grid-row: 2; }
+.result-4      { grid-column: 3; grid-row: 2; }
+.result-5      { grid-column: 2; grid-row: 3; }
+.result-6      { grid-column: 3; grid-row: 3; }
+
+.conclusions   { grid-column: 2 / 4; grid-row: 4; }
+```
+
+Result 数量不同时调整行数,保持从左到右、从上到下排列:
+
+```css
+/* 4 个 Result */
+.result-1 { grid-column: 2; grid-row: 1; }
+.result-2 { grid-column: 3; grid-row: 1; }
+.result-3 { grid-column: 2; grid-row: 2; }
+.result-4 { grid-column: 3; grid-row: 2; }
+.conclusions { grid-column: 2 / 4; grid-row: 3; }
+
+/* 3 个 Result(最后行只占 1 格) */
+.result-1 { grid-column: 2; grid-row: 1; }
+.result-2 { grid-column: 3; grid-row: 1; }
+.result-3 { grid-column: 2; grid-row: 2; }
+.conclusions { grid-column: 2 / 4; grid-row: 3; }
+```
+
+---
+
+## Section 基础样式
+
+```css
+.section {
+  background: white;
+  border-radius: 0.08in;
+  padding: 0.3in;
+  position: relative;
+  box-shadow: 0 2px 10px rgba(0,0,0,0.04);
+  border: 1px solid rgba(0,0,0,0.05);
+  border-top: 4px solid var(--accent);
+}
+.section-title {
+  font-family: 'Playfair Display', serif;
+  font-size: 0.28in; font-weight: 700;
+  color: var(--primary);
+  margin-bottom: 0.18in; padding-bottom: 0.08in;
+  border-bottom: 3px solid var(--accent);
+  display: inline-block;
+}
+.section p, .section li {
+  font-size: 0.17in; line-height: 1.6; color: var(--text-dark);
+}
+```
+
+---
+
+## Result 编号圆标
+
+每个 Result section 带有绝对定位的编号圆标,引导阅读顺序:
+
+```css
+.result-number {
+  position: absolute;
+  top: -0.18in; left: 0.2in;
+  width: 0.4in; height: 0.4in;
+  border-radius: 50%;
+  display: flex; align-items: center; justify-content: center;
+  font-family: 'Playfair Display', serif;
+  font-size: 0.22in; font-weight: 900;
+  color: white;
+  box-shadow: 0 2px 8px rgba(0,0,0,0.15);
+  z-index: 2;
+}
+```
+
+HTML:
+
+```html
+<div class="section result-1" style="border-top-color: var(--r1-color)">
+  <div class="result-number" style="background: var(--r1-color)">1</div>
+  <h2 class="section-title">Result 标题</h2>
+  ...
+</div>
+```
+
+---
+
+## Conclusions 深色样式
+
+Conclusions 跨第 2–3 列,使用深色渐变背景作为视觉终点:
+
+```css
+.conclusions {
+  grid-column: 2 / 4;
+  background: linear-gradient(135deg, var(--primary) 0%, var(--secondary) 100%);
+  color: white;
+  border-top: none;
+}
+.conclusions .section-title {
+  color: white;
+  border-bottom-color: var(--gold);
+}
+
+.concl-item {
+  display: flex; align-items: flex-start;
+  gap: 0.12in; margin-bottom: 0.12in;
+  padding: 0.1in;
+  background: rgba(255,255,255,0.06);
+  border-radius: 0.05in;
+  border-left: 3px solid var(--gold);
+}
+.concl-num {
+  font-family: 'Playfair Display', serif;
+  font-size: 0.28in; font-weight: 900;
+  color: var(--accent); flex-shrink: 0;
+  width: 0.35in; text-align: center;
+}
+.concl-text {
+  font-size: 0.16in; line-height: 1.55;
+  color: rgba(255,255,255,0.92);
+}
+```
+
+HTML:
+
+```html
+<div class="section conclusions">
+  <h2 class="section-title">Conclusions</h2>
+  <div class="concl-item">
+    <div class="concl-num">1</div>
+    <div class="concl-text">结论内容...</div>
+  </div>
+  <div class="concl-item">
+    <div class="concl-num">2</div>
+    <div class="concl-text">结论内容...</div>
+  </div>
+</div>
+```
+
+---
+
+## Stat Box
+
+用于 Study Design 区域展示关键数字:
+
+```css
+.stat-row { display: flex; gap: 0.15in; flex-wrap: wrap; }
+.stat-box {
+  flex: 1; min-width: 1in;
+  background: var(--bg-light); border-radius: 0.06in;
+  padding: 0.14in; text-align: center;
+  border-left: 3px solid var(--accent);
+}
+.stat-box .number {
+  font-family: 'Playfair Display', serif;
+  font-size: 0.34in; font-weight: 900;
+  color: var(--secondary); display: block;
+}
+.stat-box .label {
+  font-size: 0.12in; color: var(--text-muted);
+  text-transform: uppercase; letter-spacing: 1px;
+}
+```
+
+---
+
+## Finding Card
+
+```css
+.finding-card {
+  background: var(--bg-light); border-radius: 0.06in;
+  padding: 0.15in; margin: 0.1in 0;
+  border-left: 4px solid var(--accent);
+}
+.finding-card.important {
+  border-left-color: var(--warm);
+  background: #FFF8F5;
+}
+```
+
+---
+
+## Cladogram(多物种系统发育树)
+
+仅用于涉及 ≥3 物种跨物种比较的论文。纯 CSS 实现,展示拓扑关系和类群分组,不标注分化时间。
+
+```css
+.cladogram {
+  padding: 0.15in;
+  background: var(--bg-light);
+  border-radius: 0.06in;
+  margin: 0.1in 0;
+  font-size: 0.12in;
+}
+.clade-group {
+  border-left: 2px solid var(--secondary);
+  margin-left: 0.15in;
+  padding-left: 0.15in;
+  padding-top: 0.04in;
+  padding-bottom: 0.04in;
+}
+.clade-label {
+  font-weight: 700;
+  color: var(--secondary);
+  font-size: 0.13in;
+  margin-bottom: 0.04in;
+}
+.clade-species {
+  display: flex; flex-wrap: wrap; gap: 0.06in;
+  margin: 0.04in 0;
+}
+.species-tag {
+  display: inline-flex; align-items: center; gap: 0.04in;
+  background: white; border-radius: 0.03in;
+  padding: 0.03in 0.08in;
+  font-size: 0.11in; font-weight: 600;
+  border: 1px solid #D8DEE4;
+}
+.species-tag .sp-dot {
+  width: 0.08in; height: 0.08in;
+  border-radius: 50%;
+}
+.outgroup-tag {
+  background: #FFF8F0;
+  border-color: var(--warm);
+}
+```
+
+HTML 示例(12 物种比较基因组学):
+
+```html
+<div class="cladogram">
+  <!-- 外群 -->
+  <div style="margin-bottom:0.06in">
+    <span class="species-tag outgroup-tag">
+      <span class="sp-dot" style="background:var(--green-a)"></span> Zebrafish
+    </span>
+    <span style="font-size:0.1in; color:var(--text-muted); margin-left:0.06in">distant outgroup</span>
+  </div>
+  <div style="margin-bottom:0.08in">
+    <span class="species-tag outgroup-tag">
+      <span class="sp-dot" style="background:var(--warm)"></span> Chicken
+    </span>
+    <span style="font-size:0.1in; color:var(--text-muted); margin-left:0.06in">close outgroup</span>
+  </div>
+  <!-- 哺乳类 -->
+  <div class="clade-group">
+    <div class="clade-label">Mammals</div>
+    <div class="clade-group">
+      <div class="clade-label">Euarchontoglires</div>
+      <div class="clade-species">
+        <span class="species-tag"><span class="sp-dot" style="background:var(--secondary)"></span> Human</span>
+        <span class="species-tag"><span class="sp-dot" style="background:var(--secondary)"></span> Rhesus</span>
+        <span class="species-tag"><span class="sp-dot" style="background:var(--secondary)"></span> Mouse</span>
+        <span class="species-tag"><span class="sp-dot" style="background:var(--secondary)"></span> Rat</span>
+        <span class="species-tag"><span class="sp-dot" style="background:var(--secondary)"></span> Rabbit</span>
+      </div>
+    </div>
+    <div class="clade-group">
+      <div class="clade-label">Laurasiatheria</div>
+      <div class="clade-species">
+        <span class="species-tag"><span class="sp-dot" style="background:var(--accent)"></span> Cow</span>
+        <span class="species-tag"><span class="sp-dot" style="background:var(--accent)"></span> Sheep</span>
+        <span class="species-tag"><span class="sp-dot" style="background:var(--accent)"></span> Pig</span>
+        <span class="species-tag"><span class="sp-dot" style="background:var(--accent)"></span> Cat</span>
+        <span class="species-tag"><span class="sp-dot" style="background:var(--accent)"></span> Dog</span>
+      </div>
+    </div>
+  </div>
+</div>
+```
+
+此组件的结构可适配各类物种分组:植物(单子叶/双子叶)、微生物(革兰氏阳性/阴性)等,只需调整 clade-label 和颜色。
+
+---
+
+## 分阶段流程图(Pipeline)
+
+采用 3 阶段分层设计,避免反复的分支箭头。每个阶段有独立视觉风格:
+
+- **Stage 1(实验)**:深色渐变背景,用 `→` 串联实验步骤
+- **Stage 2(数据处理)**:浅灰背景,tool-tag 标签展示软件工具
+- **Stage 3(多维分析)**:白底蓝边分组框,内含 2×N 网格的分析卡片
+
+```
+┌─────────────────────────────┐
+│ Stage 1 — Experiment        │  ← 深色渐变 (stage-exp)
+│ 实验步骤 → 用箭头串联       │
+└──────────────┬──────────────┘
+               ▼
+┌─────────────────────────────┐
+│ Stage 2 — Data Processing   │  ← 浅灰背景 (stage-proc)
+│ 处理步骤 + tool-tag 标签     │
+└──────────────┬──────────────┘
+               ▼
+┌─ Stage 3 — Analysis ────────┐  ← 白底蓝边 (stage-analysis)
+│ ┌─────────┐ ┌─────────┐    │
+│ │ Card 1  │ │ Card 2  │    │  ← analysis-card 网格
+│ ├─────────┤ ├─────────┤    │     每张卡片不同色彩左边框
+│ │ Card 3  │ │ Card 4  │    │
+│ ├─────────┤ ├─────────┤    │
+│ │ Card 5  │ │ Card 6  │    │
+│ └─────────┘ └─────────┘    │
+└──────────────────────────────┘
+```
+
+### CSS
+
+```css
+.pipeline {
+  display: flex; flex-direction: column;
+  gap: 0; margin: 0.1in 0;
+}
+.pipeline-stage {
+  position: relative;
+  padding: 0.14in;
+  border-radius: 0.06in;
+  margin-bottom: 0;
+}
+.pipeline-stage.stage-exp {
+  background: linear-gradient(135deg, var(--secondary), var(--primary));
+  color: white;
+}
+.pipeline-stage.stage-proc {
+  background: var(--bg-light);
+  border: 1.5px solid #D0D7DE;
+}
+.pipeline-stage.stage-analysis {
+  background: white;
+  border: 2px solid var(--secondary);
+}
+.stage-label {
+  font-size: 0.1in; font-weight: 700;
+  text-transform: uppercase; letter-spacing: 1.5px;
+  margin-bottom: 0.08in; opacity: 0.7;
+}
+.stage-exp .stage-label { color: rgba(255,255,255,0.7); }
+.stage-proc .stage-label { color: var(--text-muted); }
+.stage-analysis .stage-label { color: var(--secondary); }
+.stage-title {
+  font-family: 'Playfair Display', serif;
+  font-size: 0.2in; font-weight: 700;
+  margin-bottom: 0.04in;
+}
+.stage-exp .stage-title { color: white; }
+.stage-proc .stage-title { color: var(--primary); }
+.stage-detail {
+  font-size: 0.12in; opacity: 0.8; line-height: 1.5;
+}
+.stage-exp .stage-detail { color: rgba(255,255,255,0.8); }
+.stage-proc .stage-detail { color: var(--text-muted); }
+.stage-tools {
+  display: inline-flex; flex-wrap: wrap; gap: 0.04in;
+  margin-top: 0.05in;
+}
+.tool-tag {
+  display: inline-block;
+  font-size: 0.1in; font-weight: 600;
+  padding: 0.02in 0.07in;
+  border-radius: 3px;
+  letter-spacing: 0.3px;
+}
+.stage-exp .tool-tag {
+  background: rgba(255,255,255,0.18); color: white;
+}
+.stage-proc .tool-tag {
+  background: white; color: var(--secondary);
+  border: 1px solid #D0D7DE;
+}
+
+/* 阶段间连接线 */
+.pipeline-connector {
+  display: flex; justify-content: center;
+  padding: 0.04in 0;
+}
+.pipeline-connector .conn-line {
+  width: 2px; height: 0.14in;
+  background: var(--secondary);
+  position: relative;
+}
+.pipeline-connector .conn-line::after {
+  content: ''; position: absolute;
+  bottom: -4px; left: -3.5px;
+  border-left: 4.5px solid transparent;
+  border-right: 4.5px solid transparent;
+  border-top: 5px solid var(--secondary);
+}
+
+/* 分析卡片网格 */
+.analysis-grid {
+  display: grid;
+  grid-template-columns: 1fr 1fr;
+  gap: 0.08in;
+}
+.analysis-card {
+  background: var(--bg-light);
+  border-radius: 0.05in;
+  padding: 0.1in;
+  border-left: 3px solid var(--accent);
+}
+.analysis-card .ac-name {
+  font-size: 0.13in; font-weight: 700;
+  color: var(--primary); margin-bottom: 0.03in;
+}
+.analysis-card .ac-tools {
+  font-size: 0.1in; color: var(--text-muted);
+  line-height: 1.4;
+}
+```
+
+### HTML 示例
+
+```html
+<div class="pipeline">
+  <!-- Stage 1: Experiment -->
+  <div class="pipeline-stage stage-exp">
+    <div class="stage-label">Stage 1 — Experiment</div>
+    <div class="stage-title">Dilution Hi-C + RNA-seq</div>
+    <div class="stage-detail">Formaldehyde crosslinking &#x2192; HindIII digestion &#x2192; Biotin ligation &#x2192; Sequencing</div>
+  </div>
+
+  <div class="pipeline-connector"><div class="conn-line"></div></div>
+
+  <!-- Stage 2: Data Processing -->
+  <div class="pipeline-stage stage-proc">
+    <div class="stage-label">Stage 2 — Data Processing</div>
+    <div class="stage-title">Mapping &#x2192; Filtering &#x2192; Normalization</div>
+    <div class="stage-tools">
+      <span class="tool-tag">Bowtie2</span>
+      <span class="tool-tag">Hiclib</span>
+      <span class="tool-tag">ICE</span>
+    </div>
+  </div>
+
+  <div class="pipeline-connector"><div class="conn-line"></div></div>
+
+  <!-- Stage 3: Multi-dimensional Analysis -->
+  <div class="pipeline-stage stage-analysis">
+    <div class="stage-label">Stage 3 — Multi-dimensional Analysis</div>
+    <div class="analysis-grid">
+      <div class="analysis-card">
+        <div class="ac-name">A/B Compartments</div>
+        <div class="ac-tools">PCA, AB index, Phylo-HMGP</div>
+      </div>
+      <div class="analysis-card">
+        <div class="ac-name">TAD Calling</div>
+        <div class="ac-tools">DomainCaller (DI + HMM)</div>
+      </div>
+      <!-- 按需添加更多 analysis-card -->
+    </div>
+  </div>
+</div>
+```
+
+分析卡片数量按论文实际分析维度调整(常见 4–6 张),每张卡片可通过 `border-left-color` 赋予独立色彩以区分不同分析类型。
+
+---
+
+## 折叠图表
+
+海报使用 **原图**(`figures/fig{N}.png`)。关键 Figure 用 `<details open>` 默认展开,次要 Figure 折叠。
+
+```css
+details.fig-collapse {
+  margin-top: 0.12in;
+  border: 1px solid #D8DEE4;
+  border-radius: 0.06in;
+  overflow: hidden;
+}
+details.fig-collapse summary {
+  background: var(--bg-light);
+  padding: 0.1in 0.14in;
+  font-weight: 700; font-size: 0.14in;
+  color: var(--secondary);
+  cursor: pointer;
+  display: flex; align-items: center; gap: 0.08in;
+  list-style: none;
+}
+details.fig-collapse summary::-webkit-details-marker { display: none; }
+details.fig-collapse summary::before {
+  content: '\25B8'; font-size: 0.16in;
+  color: var(--accent);
+  transition: transform 0.2s;
+  display: inline-block;
+}
+details.fig-collapse[open] summary::before {
+  transform: rotate(90deg);
+}
+
+.fig-image-container {
+  text-align: center; margin: 0.08in 0;
+  border: 1px solid #E0E4E8; border-radius: 0.04in;
+  overflow: hidden;
+}
+.fig-image-container img {
+  max-width: 100%; height: auto;
+  display: block; margin: 0 auto;
+  cursor: zoom-in;
+  transition: opacity 0.2s;
+}
+.fig-image-container img:hover { opacity: 0.85; }
+.fig-caption {
+  font-size: 0.12in; color: var(--text-muted);
+  padding: 0.06in; background: var(--bg-light);
+  text-align: left; line-height: 1.5;
+}
+```
+
+```html
+<!-- 关键 Figure:默认展开 -->
+<details class="fig-collapse" open>
+  <summary>Figure 1 &#x2014; 标题</summary>
+  <div class="fig-content">
+    <div class="fig-image-container">
+      <img src="figures/fig1.png" alt="Figure 1">
+      <div class="fig-caption">图注</div>
+    </div>
+  </div>
+</details>
+
+<!-- 次要 Figure:折叠 -->
+<details class="fig-collapse">
+  <summary>Figure 2 &#x2014; 标题</summary>
+  ...
+</details>
+```
+
+---
+
+## Lightbox 放大查看
+
+```html
+<div class="lightbox-overlay" id="lightbox">
+  <img id="lightbox-img" src="" alt="">
+</div>
+```
+
+```css
+.lightbox-overlay {
+  display: none; position: fixed;
+  top: 0; left: 0; width: 100%; height: 100%;
+  background: rgba(0,0,0,0.88);
+  z-index: 9999;
+  justify-content: center; align-items: center;
+  cursor: zoom-out;
+}
+.lightbox-overlay.active { display: flex; }
+.lightbox-overlay img {
+  max-width: 92%; max-height: 92%;
+  object-fit: contain; border-radius: 4px;
+  box-shadow: 0 8px 40px rgba(0,0,0,0.5);
+}
+```
+
+```javascript
+(function(){
+  var overlay = document.getElementById('lightbox');
+  var lbImg = document.getElementById('lightbox-img');
+  document.querySelectorAll('.fig-image-container img').forEach(function(img){
+    img.addEventListener('click', function(){
+      lbImg.src = this.src;
+      lbImg.alt = this.alt;
+      overlay.classList.add('active');
+    });
+  });
+  overlay.addEventListener('click', function(){ overlay.classList.remove('active'); });
+  document.addEventListener('keydown', function(e){
+    if(e.key === 'Escape') overlay.classList.remove('active');
+  });
+})();
+```
+
+---
+
+## Footer
+
+```css
+.footer {
+  background: var(--primary);
+  padding: 0.25in 0.55in;
+  display: grid;
+  grid-template-columns: 1.5fr 1fr 1fr;
+  gap: 0.4in;
+  color: rgba(255,255,255,0.8);
+  font-size: 0.13in;
+  line-height: 1.6;
+}
+.footer strong {
+  color: var(--gold);
+  font-size: 0.14in;
+  letter-spacing: 1px;
+  text-transform: uppercase;
+}
+.footer a { color: var(--accent); text-decoration: none; }
+```
+
+```html
+<div class="footer">
+  <div>
+    <strong data-lang="en">Acknowledgments</strong>
+    <p data-lang="en">资助信息...</p>
+  </div>
+  <div>
+    <strong data-lang="en">Data Availability</strong>
+    <p data-lang="en">数据/代码链接...</p>
+  </div>
+  <div>
+    <strong data-lang="en">Contact</strong>
+    <p data-lang="en">通讯作者 email...</p>
+  </div>
+</div>
+```
+
+---
+
+## 中英文双语切换
+
+### 按钮样式
+
+```css
+.lang-switch {
+  position: fixed;
+  top: 0.2in; right: 0.3in;
+  z-index: 100;
+  display: flex; gap: 0;
+  background: rgba(255,255,255,0.97);
+  border-radius: 6px;
+  box-shadow: 0 2px 16px rgba(0,0,0,0.18);
+  overflow: hidden;
+  border: 1px solid #D0D7DE;
+}
+.lang-btn {
+  border: none; background: transparent;
+  padding: 8px 18px;
+  font-size: 15px; font-weight: 700;
+  color: var(--text-muted);
+  cursor: pointer;
+  transition: all 0.2s;
+}
+.lang-btn:hover { color: var(--secondary); background: var(--bg-light); }
+.lang-btn.active {
+  background: var(--secondary);
+  color: white;
+}
+```
+
+### HTML 标记
+
+```html
+<div class="lang-switch">
+  <button class="lang-btn active" data-target="en">EN</button>
+  <button class="lang-btn" data-target="zh">中文</button>
+</div>
+
+<!-- 需要翻译的元素加 data-lang -->
+<h2 data-lang="en">Introduction</h2>
+<h2 data-lang="zh">研究背景</h2>
+```
+
+无需双语标记的元素:数字/统计值、图片、DOI、作者姓名。
+
+### JS 切换逻辑
+
+```javascript
+(function(){
+  var btns = document.querySelectorAll('.lang-btn');
+  btns.forEach(function(btn){
+    btn.addEventListener('click', function(){
+      var lang = this.dataset.target;
+      document.querySelectorAll('[data-lang]').forEach(function(el){
+        el.style.display = el.dataset.lang === lang ? '' : 'none';
+      });
+      btns.forEach(function(b){ b.classList.remove('active'); });
+      this.classList.add('active');
+    });
+  });
+  document.querySelectorAll('[data-lang="zh"]').forEach(function(el){
+    el.style.display = 'none';
+  });
+})();
+```
+
+---
+
+## 打印与预览
+
+```css
+@media print {
+  body { background: white; padding: 0; }
+  .poster { box-shadow: none; }
+  details.fig-collapse[open] { break-inside: avoid; }
+  .lang-switch { display: none; }
+  -webkit-print-color-adjust: exact;
+  print-color-adjust: exact;
+}
+```
+
+```bash
+python3 -m http.server 8765
+# 访问 http://localhost:8765/papers/{Author}{Year}/poster.html
+```
+
+---
+
+## 特殊字符速查
+
+| 字符 | HTML 实体 | CSS content | 用途 |
+|------|----------|-------------|------|
+| ▸ | `&#x25B8;` | `\25B8` | 折叠箭头 |
+| → | `&#x2192;` | `\2192` | 右箭头 |
+| — | `&#x2014;` | `\2014` | 破折号 |
+| × | `&times;` | - | 乘号 |
+| ≥ | `&ge;` | - | 大于等于 |
+| ≤ | `&le;` | - | 小于等于 |

+ 33 - 0
genek-paper-2-web/references/qa-checklists.md

@@ -0,0 +1,33 @@
+# Paper2Web QA 门禁清单
+
+交付前,按以下清单逐项核对。任一硬门禁失败即不允许交付。
+
+## A. 资产门禁(Hard Gate)
+
+- [ ] `paper.pdf` 存在于 `papers/{AuthorYear}/`
+- [ ] `figures/fig{N}.png` 连续、无缺号、可打开
+- [ ] 无临时残留:`tmp-*`、`page-*`、`*_verify.png`
+
+## B. 内容门禁(Hard Gate)
+
+- [ ] `poster.html` 包含:Background / Study Design / Methods / Results / Conclusions / Footer
+- [ ] 无占位文本(例如 lorem、xxxx、template)
+- [ ] 每个 Result 卡片包含:编号圆标 + 标题 + Figure 图片 + 解读文字
+
+## C. 双语门禁(Hard Gate)
+
+- [ ] EN/ZH 切换按钮存在且功能正常
+- [ ] 默认显示英文
+- [ ] 中文内容非空
+
+## D. 视觉门禁(Soft Gate,建议全通过)
+
+- [ ] 无明显文字溢出/重叠
+- [ ] 图片未被拉伸变形
+- [ ] Lightbox 点击放大功能正常
+- [ ] 折叠/展开功能正常
+- [ ] 打印预览布局合理
+
+## E. 交付清单(Hard Gate)
+
+- [ ] `poster.html`

+ 301 - 0
genek-split-figures/SKILL.md

@@ -0,0 +1,301 @@
+---
+name: genek-split-figures
+description: 识别科研论文 Figure 中子图(panel)的边界并裁剪。采用 LLM 语义分组 + 像素精修的混合方案,输出每个 panel 组的精确矩形坐标并生成可视化验证图。当用户提及"子图边界"、"panel 识别"、"figure 拆分"、"提取子图"、"split figure"时使用此技能。
+---
+
+# 科研 Figure 子图边界识别
+
+支持两种模式:
+- **自动模式**:LLM 看图自行判断分组,**限制为 2–3 组(不超过 3 个)**
+- **用户指定模式**:用户给出分组方案(如 `A, DEFG, BCH`),LLM 据此定位,组数不受限制
+
+采用混合方案:语义理解(确定每个 panel 位置)+ 像素级白色间隙分析(精确定位边界)。
+
+## 目录约定
+
+与 `genek-extract-figures` 共用 `papers/{Author}{Year}/` 结构:
+
+- **输入**:`papers/{Author}{Year}/figures/fig{N}.png`(原图)
+- **输出**:`papers/{Author}{Year}/figures/panels/fig{N}_{panels}.png`(拆分后的 panel)
+
+```
+papers/Li2022/
+├── paper.pdf
+├── figures/
+│   ├── fig1.png                  ← 输入(extract-figures 产出)
+│   ├── fig2.png
+│   └── panels/                   ← 输出目录
+│       ├── fig1_A.png            ← 单 panel
+│       ├── fig1_DEFG.png         ← 多 panel 合并为一组
+│       ├── fig2_BC.png
+│       └── ...
+```
+
+Panel 文件命名:`fig{N}_{panels}.png`
+- 单 panel:`fig1_A.png`
+- 多 panel 合为一组:`fig1_BC.png`、`fig1_DEFG.png`(按原 panel 标签排列拼接)
+
+**严格要求(禁止规避)**:
+- 必须按下方流程**完整执行**:确定分组 → LLM 看图定位 → 像素精修 → 裁剪保存 → 验证图检查。不得仅将原图复制或重命名为「整图当一 panel」作为输出。
+- **禁止**使用 `_whole`、`_full`、`_all` 等占位组名(如 `fig1_whole.png`)。输出文件的 `{panels}` 必须为论文图中**真实存在的 panel 标签**(如 A、B、BC、DEFG),与图中标注一致。
+
+## 流程
+
+```
+1. 确定分组方案(用户指定 或 LLM 自动判断)
+2. LLM 看图 → 定位每个 panel 的位置 + 给出每组粗坐标
+3. 像素分析 → 运行 analyze_image,解读间隙输出,与粗坐标对齐确定精确裁剪坐标
+4. 裁剪并保存到 papers/{Author}{Year}/figures/panels/
+5. 生成带彩色边框的验证图 → Read 工具逐张查看
+6. 如有偏差 → 调整坐标,重新裁剪 + 验证
+7. 全部确认后删除验证图
+```
+
+## 第一步:确定分组 + LLM 定位
+
+### 模式 A:用户指定分组
+
+用户输入示例:`从 fig1.png 中提取 A, DEFG, BCH`
+
+解析规则:
+- 每个逗号分隔项为一组,组名即 panel 标签拼接(如 `BCH` = 面板 B + C + H)
+- LLM 看图后定位每个**单独 panel** 的矩形范围
+- 每组的边界 = 组内所有 panel 的最小外接矩形(min x1, min y1, max x2, max y2)
+
+输出定位表:
+
+```
+单 panel 定位:
+| Panel | 位置 (x1, y1, x2, y2) |
+|-------|----------------------|
+| A     | (0, 0, 545, 570)    |
+| B     | (0, 580, 540, 940)   |
+| C     | (540, 580, 1928, 940)|
+| D     | (545, 0, 1240, 310)  |
+| E     | (1240, 0, 1928, 310) |
+| F     | (545, 310, 1240, 570) |
+| G     | (1240, 310, 1928, 570)|
+| H     | (0, 950, 1928, 1310) |
+
+用户分组聚合:
+| 组名  | 包含 panel | 聚合坐标              |
+|-------|-----------|----------------------|
+| A     | A         | (0, 0, 545, 570)     |
+| DEFG  | D,E,F,G   | (545, 0, 1928, 570)  |
+| BCH   | B,C,H     | (0, 580, 1928, 1310) |
+```
+
+### 模式 B:LLM 自动分组(2–3 组硬上限)
+
+自动模式的核心约束:**每张 Figure 最终输出 2 或 3 个子图组,绝不超过 3 个。**
+
+LLM 看图后按以下步骤输出分组表:
+
+1. 识别 Figure 中所有 panel(A, B, C, D, …)
+2. 将全部 panel 合并为 **2–3 个组**,优先合并而非拆分
+3. 若 panel 数 ≤ 3,每个 panel 可单独成组
+4. 若 panel 数 > 3,必须聚合到 ≤ 3 组(按下方合并策略)
+
+```
+| 组名  | 包含 panel  | 分组理由                    |
+|-------|-----------|---------------------------|
+| ABC   | A, B, C   | 上层行:系统发育树 + 质量条形图 + 表格  |
+| DEFG  | D, E, F, G | 中层行:4 张相关散点图           |
+| H     | H         | 底层行:独立 3D 建模图          |
+```
+
+**合并策略**(按优先级):
+1. **空间邻近**:同一水平行或垂直列的 panel 优先合并
+2. **内容相似**:类型相近的图(如多张散点图、多张热图)合并
+3. **共享元素**:共用图例、共用坐标轴、箭头连接的 panel 合并
+4. **面积均衡**:避免组间面积差异过大(如一组占 80%,另一组占 20%)
+
+**禁止**:
+- 输出 > 3 个组(违反硬上限)
+- 输出 1 个组(无意义拆分,至少 2 个)
+- 将不相邻、不相关的 panel 强行合并仅为凑数
+
+两种模式共同输出每组的粗坐标估计 `(left, top, right, bottom)`。
+
+## 第二步:像素精修
+
+用 Python + PIL + numpy,对图像做白色间隙检测。
+
+### 核心算法
+
+```python
+from PIL import Image
+import numpy as np
+
+def find_gaps(arr_1d, min_len=10, thresh=252):
+    """在一维亮度数组中找连续白色区段。"""
+    gaps = []
+    i, n = 0, len(arr_1d)
+    while i < n:
+        if arr_1d[i] > thresh:
+            start = i
+            while i < n and arr_1d[i] > thresh:
+                i += 1
+            if i - start >= min_len:
+                gaps.append((start, i, i - start))
+        else:
+            i += 1
+    return gaps
+
+def analyze_image(img_path):
+    gray = np.array(Image.open(img_path).convert('L'))
+    h, w = gray.shape
+    row_mean = gray.mean(axis=1)
+    h_gaps = find_gaps(row_mean, min_len=12)
+    # 过滤边缘间隙
+    h_interior = [(s, e, l) for s, e, l in h_gaps if s > 10 and e < h - 10]
+
+    # 按间隙大小排序,取前 4-5 个主要间隙定义水平分带
+    h_interior.sort(key=lambda x: x[2], reverse=True)
+    major_h = sorted(h_interior[:5], key=lambda x: x[0])
+
+    # 对每个水平带做垂直间隙分析
+    edges = [0]
+    for s, e, _ in major_h:
+        edges.extend([s, e])
+    edges.append(h)
+
+    bands = []
+    for i in range(0, len(edges) - 1, 2):
+        y1, y2 = edges[i], edges[i + 1]
+        if y2 - y1 < 40:
+            continue
+        band_col = gray[y1:y2, :].mean(axis=0)
+        v_gaps = find_gaps(band_col, min_len=8)
+        v_interior = [(s, e, l) for s, e, l in v_gaps if s > 10 and e < w - 10]
+        v_interior.sort(key=lambda x: x[2], reverse=True)
+        bands.append({'y': (y1, y2), 'v_gaps': v_interior[:3]})
+
+    return w, h, major_h, bands
+```
+
+### 精修策略
+
+1. **水平分界**:取主要 H gap 的 `(start, end)` → 上方 panel 底边 = `start`,下方 panel 顶边 = `end`
+2. **垂直分界**:在每个水平带内独立计算 → 取最大 V gap 的 `(start, end)`
+3. **对齐粗坐标**:LLM 粗坐标用于选择"哪个间隙是真正的 panel 分界"(而非 panel 内部空白)
+
+### 关键注意事项
+
+- **panel 内部也有空白**(如 fig1 的 Contig N50 和 Scaffold N50 之间的 "//" 断裂),不能盲目选最大间隙
+- **数字表格会产生大量假水平间隙**(如 fig4 的 CTCF 数值表),需要 LLM 判断哪些间隙是真实分界
+- **不同水平带的垂直分界位置往往不同**(如 fig1 上半部分 A|DEFG 分界在 x=845,中间部分 B|C 分界完全不同)
+
+### 实操流程
+
+对每张 Figure 运行 `analyze_image`,输出格式如下:
+
+```
+=== fig1.png (1928x2000) ===
+Top H gaps (y): [(951, 1007, 56), (1494, 1543, 49)]
+Top V gaps (x): []
+  Band y=[0:951] V gaps: [(866, 891, 25), (1419, 1437, 18)]
+  Band y=[1007:1494] V gaps: [(511, 566, 55)]
+  Band y=[1543:2000] V gaps: [(1107, 1168, 61), (730, 769, 39)]
+```
+
+解读方法:
+
+1. **H gaps → 水平分带**:`(951, 1007)` 表示 y=951~1007 是白色间隙,将图像分成上 `[0:951]`、中 `[1007:1494]`、下 `[1543:2000]` 三个水平带
+2. **每个带内的 V gaps → 垂直分界**:上层带 V gap `(866, 891)` 表示 x=866~891 是垂直白色间隙
+3. **与 LLM 粗坐标对齐**:LLM 判断上层带左侧为 A、右侧为 DEFG → 精确边界为 A 右边 = x=866,DEFG 左边 = x=891
+4. **确定裁剪坐标**:`A = (0, 0, 866, 951)`,`DEFG = (891, 0, 1928, 951)`
+
+关键原则:
+- 间隙的 `start` 是上/左 panel 的底/右边界,`end` 是下/右 panel 的顶/左边界
+- 选择哪个间隙作为分界由 LLM 粗坐标决定(选最接近粗坐标的间隙)
+- 如果一个带内没有 V gap,说明该带只有一个 panel 跨全宽
+
+## 第三步:裁剪并保存
+
+确定所有坐标后,批量裁剪:
+
+```python
+from PIL import Image
+import os
+
+def crop_panels(fig_dir, panel_dir, fig_num, panels):
+    """
+    panels: dict of {group_name: (x1, y1, x2, y2)}
+    例如: {"A": (0, 0, 866, 951), "DEFG": (891, 0, 1928, 951)}
+    """
+    os.makedirs(panel_dir, exist_ok=True)
+    img = Image.open(f"{fig_dir}/fig{fig_num}.png")
+    for name, box in panels.items():
+        cropped = img.crop(box)
+        out = f"{panel_dir}/fig{fig_num}_{name}.png"
+        cropped.save(out)
+        size = os.path.getsize(out)
+        print(f"  fig{fig_num}_{name}.png: {cropped.size[0]}x{cropped.size[1]} ({size} bytes)")
+```
+
+调用示例(以 Li2022 Fig 1 为例):
+
+```python
+fig_dir = "papers/Li2022/figures"
+panel_dir = "papers/Li2022/figures/panels"
+
+crop_panels(fig_dir, panel_dir, 1, {
+    "A":    (0,   0,    866, 951),
+    "DEFG": (891, 0,    1928, 951),
+    "B":    (0,   1007, 511, 1494),
+    "C":    (566, 1007, 1928, 1494),
+    "H":    (0,   1543, 1928, 2000),
+})
+```
+
+## 第四步:可视化验证
+
+```python
+from PIL import Image, ImageDraw, ImageFont
+
+COLORS = ["#E74C3C", "#2980B9", "#27AE60", "#8E44AD", "#E67E22", "#1ABC9C"]
+LINE_W = 5
+
+def draw_boundaries(img_path, panels, out_path):
+    """panels: dict of {name: (x1, y1, x2, y2)}"""
+    img = Image.open(img_path).convert('RGB')
+    draw = ImageDraw.Draw(img)
+    try:
+        font = ImageFont.truetype("/System/Library/Fonts/Helvetica.ttc", 28)
+    except Exception:
+        font = ImageFont.load_default()
+
+    for i, (name, box) in enumerate(panels.items()):
+        c = COLORS[i % len(COLORS)]
+        x1, y1, x2, y2 = box
+        for j in range(LINE_W):
+            draw.rectangle([x1 + j, y1 + j, x2 - j, y2 - j], outline=c)
+        bb = draw.textbbox((0, 0), name, font=font)
+        tw, th = bb[2] - bb[0], bb[3] - bb[1]
+        tx, ty = x1 + 8, y1 + 8
+        draw.rectangle([tx - 2, ty - 2, tx + tw + 4, ty + th + 4],
+                        fill='white', outline=c)
+        draw.text((tx, ty), name, fill=c, font=font)
+    img.save(out_path)
+```
+
+### 验证流程
+
+1. 对每张 Figure 调用 `draw_boundaries` 生成 `fig{N}_verify.png`
+2. 用 Read 工具逐张查看验证图,确认彩色边框准确框住对应 panel
+3. 如有问题:
+   - 框太大(侵入相邻 panel)→ 缩小对应边
+   - 框太小(截断内容)→ 检查是否选错了间隙,或 panel 内部有大空白被误判为分界
+   - 框位置完全错误 → 重新看图修正 LLM 分组
+4. 修正后重新裁剪(第三步)并重新验证
+5. 全部确认后删除验证图:`rm papers/{Author}{Year}/figures/panels/*_verify.png`
+
+## 经验教训(来自实测)
+
+| 问题 | 原因 | 解决方法 |
+|------|------|---------|
+| Scaffold N50 柱状图被截断 | 内部边框线被误判为 panel 分界 | 在粗坐标附近多检查几个间隙候选 |
+| fig4 被切成 11 块 | 数字表格的行间空白被当成 panel 间隙 | LLM 先确定"应该有几个分区" |
+| OCR 漏检 panel 标签 | Tesseract 对粗体单字母识别率仅 60-70% | 不依赖 OCR,以 LLM 视觉为主 |
+| 纯像素分析过度切分 | 无语义理解 | 必须 LLM 先定分组,再用像素精修 |