From mhtml-refine-to-md
将本地下载的极客时间专栏 `.mhtml` 网页文件自动提取、深度提炼,并转换为结构化、包含 Mermaid 图解与 HTML/CSS 信息卡片、完美适配 Obsidian 的 Markdown 学习笔记。仅适用于极客时间(time.geekbang.org)站点的文章。
How this skill is triggered — by the user, by Claude, or both
Slash command
/mhtml-refine-to-md:mhtml-refine-to-mdThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
当用户要求"总结"、"整理"、"提取"一个或多个**极客时间专栏** `.mhtml` 文件并输出为笔记/Markdown 时,自动触发。**非极客时间站点的 mhtml 不适用本 skill**(关键提取规则如 `data-slate-string`、`articleInfo` class、`resource/image` 路径均为极客时间专属)。
当用户要求"总结"、"整理"、"提取"一个或多个极客时间专栏 .mhtml 文件并输出为笔记/Markdown 时,自动触发。非极客时间站点的 mhtml 不适用本 skill(关键提取规则如 data-slate-string、articleInfo class、resource/image 路径均为极客时间专属)。
Source_File: 待处理的源 .mhtml 文件绝对路径。Target_Directory: (可选) 笔记输出的目标目录。如果不指定,默认输出到 /Users/fengyuhao/SecondBrain/200_Learning/ 下的相关课程目录。请严格按照以下步骤执行任务:
读取 Source_File 的原始内容,正确处理 mhtml 的 Base64 或 quoted-printable 编码分段。
极客时间专栏 HTML 特性:极客时间专栏使用 Slate.js 富文本渲染,正文所有文本存储在 <span data-slate-string="true"> 元素中,不在 <p> 标签内。提取正文必须使用此选择器,而非常规 <p> 标签。
从 HTML 结构中优先提取关键元数据:
<title> 或 <h1>)<title> 中提取,格式通常为"文章标题-课程名-极客时间")| 与半角 |(含两侧空格 |)均可能出现,按任一分隔符切分取前缀;无数字时保留文字前缀)articleInfo 的元素内的文本——注意该 class 可能挂在 <div> 上,作者名在其内层无 class 的 <span> 里(如茹炳晟《软件测试52讲》:<div class="...articleInfo..."> <span>茹炳晟</span></div>),不要机械只认"class 含 articleInfo 的 span"。authorName(如 ColumnInfo_authorName_*)的元素文本——最稳定的兜底来源,几乎所有极客时间专栏页都有。## [作者名]说)和署名使用。图片处理(必须逐张视觉核查,不得跳过):
1a — 提取图片并获取原文字符数:调用 skill 自带的提取脚本(已封装清空临时目录、封面识别、Slate 字符统计):
python3 __SKILL_DIR__/scripts/extract_images.py "<Source_File 的绝对路径>"
脚本输出:
<文件名> | <COVER|CONTENT> | <字节数> | <原始URL> [可选标注],存放在 /tmp/geektime_imgs/。CONTENT 待视觉核查,COVER 直接跳过。[RESIZED WxH→W'xH'] 标注,确保后续 Read 工具视觉分析时不触发 Claude API many-image 尺寸限制。[LIKELY_DECORATIVE] 装饰图标记:若 CONTENT 图同时满足"任意边 < 1500px"和"字节数 < 350KB",脚本会追加此标注。这类图多为卡通插画、概念隐喻装饰,文字信息密度极低。视觉分析时应:
[!INFO] 类比示意——这些图的文字内容已被正文覆盖,[!INFO] 块只是冗余复述SLATE_CHARS: N:原文正文纯字符数。必须记录此 N 值,Step 6 字数比例核查依赖它(笔记字符数应 ≥ N × 60%)。脚本源码见 scripts/extract_images.py,可独立测试。
1b — 逐张视觉分析(Read 工具查看每张图片):对 1a 提取的每张图,使用 Read 工具查看实际内容,记录:
⚠️ 批量读取与上下文控制(必须遵守,防止请求体超限):图片超过 4 张时,每读完 4 张立即将分析结果归纳为一张紧凑摘要表,再继续读下一批。原始图片 base64 数据会快速撑大对话上下文;及时归纳摘要可将图片数据从"活跃上下文"中清出,避免后续 API 请求体超限崩溃。
摘要表格式(每批读完后输出,后续步骤只依赖此表,不再重复引用原始图片内容):
| 文件名 | 图片类型 | 核心信息(一行内) | 处理决策 | 所在正文位置 |
|---|---|---|---|---|
| img001.png | 架构图 | Client→Gateway→Service→DB 四层 | Mermaid graph TD | 第2节"架构概览"后 |
| img002.png | 代码截图 | Spring @Async + CompletableFuture 并发模式 | 代码块(java) | 第4节示例后 |
1c — 按类型决定处理方式(基于视觉分析,而非文字推测):
[!INFO] 📷 图示:,内容不少于 3 句话,说明截图展示的具体界面、操作步骤和关键结论COVER)→ 直接跳过,无需 Read 查看)或基于外部 URL 的 <img> 标签;HTML 卡片中的 <div>/<span> 不受此限。作者旁白块识别(条件性):仅当文章中存在与正文视觉/结构显著区分的旁白块(独立 CSS class、特殊引用框、带头像发言框、明确以"提示:"/"小贴士:"等前缀引出的独立段落)时才识别。整篇都是第一人称叙述但无显著区分的文章(极客时间专栏多为此类),M=0,后续 ## [作者名]说 章节直接跳过。识别到的旁白块总数记为 M。
🔑 章节级标题判定(关键,不可跳过):极客时间专栏中,章节级标题既可能是 H1,也可能是 H2(不同作者/编辑团队习惯不同),不能机械假设为 H2。判定规则:
<title> 内容一致)= 文章主标题,不计入章节。## 结构完全由 skill 模板(关键概念 / 原理解析 / 最佳实践…)主动赋予,把上述逻辑块归类填入对应模板章节。章节级过滤规则:
## 总结 章节的基础素材(加以丰富,不照抄)。剥离所有干扰内容:导航栏、侧边栏、广告、评论区、footer。
精准保留:正文段落、代码块、列表、表格、作者旁白块(若有)、以及文末的"思考题"部分。
建立章节级覆盖清单:解析完成后,列出正文中所有有效章节级标题(按上面"章节级判定"识别,已过滤"全部留言"),格式如下(后续 Step 2 写作前逐一打勾核对):
原文章节级覆盖清单(章节级 = H1 或 H2,写实际级别):
- [ ] 章节标题一
- [ ] 章节标题二
- [ ] 章节标题三
...
⚠️ 写作前必须执行:内容完整性核查
## 或 ### 节覆盖。### 子节),保留该视角的核心论述。## [作者名]说,不得因"内容已在正文中体现"而删减;若 M = 0,跳过该章节。将提炼后的内容重新组织为以下笔记结构(根据文章内容选用适合的章节,不强制全部包含):
# [讲次编号]-[文章标题]
> [!ABSTRACT] 一句话摘要
> 本文解决什么问题?核心结论是什么?(1-2句话,用最精炼的语言)
---
## 关键概念
(定义类内容,术语解释,对比表格)
---
## 原理解析
(核心机制、架构、流程的深度拆解,长节必须拆 ### 子节)
---
## 代码示例
(保留文章中的关键代码,每段代码上方一句话说明演示目的)
---
## 最佳实践 & 避坑指南
(操作建议、常见误区)
---
## [作者名]说(仅当 M > 0 时包含)
(完整保留 Step 1 识别到的 M 个作者旁白块,每段独立一个 [!QUOTE] callout,末尾署名 `— [作者名]`;章节标题中的「作者名」替换为 Step 1 提取到的实际作者名,如「咖哥说」「张磊说」「Robert说」。若 M = 0(极客时间专栏常见情况),整节省略)
---
## 作者金句
(从正文中提炼 1-3 句最精炼、最具洞见的核心观点,用 [!QUOTE] callout 呈现,与上节"[作者名]说"的旁白不同,这里是对全文最重要论断的主动萃取)
---
## 思考题
(完整保留原文末尾的思考题,每题用 [!QUESTION] callout;每题下方紧跟一个 [!NOTE]- 折叠块,标题为「参考答案」,结合本文内容与相关技术背景给出完整、有深度的参考答案,不得仅用"见上文"敷衍)
---
## 总结
(必须包含:① 一个 mindmap 图表作为全文知识俯瞰 ② 3-5 条核心 bullet points)
重构原则:
## 章节之间使用 --- 分隔线,增强视觉层次感。## 总结 章节的基础素材,再补充 mindmap 与 3-5 条 bullet points。### 子节:## 原理解析 等内容丰富的章节,应拆分为多个 ### 子节(每个子节覆盖一个独立概念),避免整节变成无结构的长段落。``` 代码块(不带语言标注),而非强制转为 Mermaid;Mermaid 保留给需要展示"关系与动态"的内容。Step 1c 中已确定处理方式的图,在笔记对应位置按决策输出(HTML 卡片 / Mermaid / 代码块 / 表格 / [!INFO])。
主动新增图/卡:即便原文某处没有图片,若该段文字描述了关系/流程/对比/层级/时序,主动生成 Mermaid;若描述的是多分区陈列/总览/方法论矩阵,主动生成 HTML 卡片。不受原文图片数量限制。
可视化模式选型表(按内容形态选工具,而不是按"原文有没有图"):
| 内容形态 | 工具 | 典型场景 |
|---|---|---|
| 多分区陈列(≥3 并列面板,无方向连接) | HTML 卡片 | 课程七大部分、五层架构总览、方法论矩阵、知识体系 |
| 层级结构(有上下父子关系,节点 ≤6) | Mermaid graph TD + subgraph | 系统三层模型、调用栈 |
| 流程 / 链路 / 步骤 | Mermaid graph TD/LR | 请求处理流程、CI/CD 流水线 |
| 时序 / 交互 | Mermaid sequenceDiagram | OAuth 握手、RPC 调用 |
| 状态 / 生命周期 | Mermaid stateDiagram-v2 | 订单状态机、连接生命周期 |
| 决策树 / 选型 | Mermaid graph TD + 菱形 | "什么时候用 X / Y" |
| 思维导图 / 全文俯瞰 | Mermaid mindmap | 总结章节 |
| 属性对比 / 参数速查 | Markdown 表格 | 方案对比表、配置参数 |
| 目录 / 文件树 | plain code block | 工程目录结构 |
关键判断:"有方向的拓扑 → Mermaid";"无方向的陈列 → HTML 卡片";"精确查找 → 表格"。
典型误判案例:
ROOT --> M1 --> M1A 的 Mermaid 树(自动布局会乱、丑),✅ 用 HTML 卡片(见 Step 3·A)所有可视化块上方注明 > 📊 [内容描述],下方 1-2 句话提炼核心结论。
表格美化规范:
---:),状态/类型/是否等短值列居中(:---:)。| ⚙️ 配置项 | 📝 说明 | ✅ 是否必填 |)。代码块美化规范:
```java、```python、```yaml、```sql 等;纯文本结构用 ```(无标注)。**📄 文件名 / 场景描述** 加粗行作为标题(如 **📄 .claude/skills/api/SKILL.md**),让读者一眼知道这段代码的来源或用途。# ← 说明 或 // ← 说明,解释其作用(勿对每行都注释,只注释非显而易见的行)。# --- 分段标题 ---)在块内分段,方便定位。**❌ 不推荐** 和 **✅ 推荐**,而非在同一代码块内混合。核心原则:图是为了"看图就懂",不是装饰。读者扫一眼图就应该明白这个概念/流程/关系,不必回到正文找解释。可视化块不设上限,鼓励多图。
双轨制:本 skill 用两种工具做可视化,必须按 Step 1c / Step 2 的选型表判断用哪种:
图配文规范(两者共用):
> 📊 [描述] 引用块说明这张图想表达什么可视化数量下限:每篇笔记至少 2 个可视化块(Mermaid + HTML 任意组合),且总结章节必须包含 1 个 mindmap。
为什么用 HTML 而不是 Mermaid:Mermaid 是 dagre 自动布局,节点位置由算法决定,做不出"卡片矩阵+色面板+整齐对齐"的设计感。HTML+inline CSS 完全可控,且 Obsidian 原生渲染,无需任何插件。
🚨 铁律 1:HTML 块内禁止出现空行
Obsidian 的 markdown 处理器看到空行会把后面的 <div> 包进新的 <p>,直接破坏 CSS Grid / Flex 的父子关系,导致 grid 退化成单列、flex 间距错乱。所有 <div> 之间不能有任何空行,整个卡片块必须是连续的一整段 HTML。
铁律 2:只用 inline style= 属性
不要在笔记里引入 <style> 标签或外部 CSS——Obsidian 会过滤。所有样式必须写在 style= 内联属性中。
铁律 3:禁止外部资源 HTML 卡片不得引用任何外部 URL(图片、字体、JS)。
两种核心模板:
模板 A1 — 列表卡片(纵向堆叠,每张大卡片内含子标签)
适用于:课程章节总览、五层架构、分区式知识图
结构:外层 flex column 容器 → 每个分区一个色面板卡片 → 卡片内含 header 行(emoji + 加粗标题 + 副标题)和 chips 行(白底色边小标签)
<div style="display:flex; flex-direction:column; gap:10px; margin:1em 0;">
<div style="background:#FCE4EC; border:2px solid #C2185B; border-radius:10px; padding:14px;">
<div style="display:flex; align-items:baseline; gap:10px; margin-bottom:10px; flex-wrap:wrap;">
<span style="font-weight:bold; color:#880E4F; font-size:1.05em;">🧠 第一部分 · 认知篇</span>
<span style="color:#AD1457; font-size:0.85em;">你是架构师,AI 是团队</span>
</div>
<div style="display:flex; flex-wrap:wrap; gap:8px;">
<span style="background:#fff; border:1.5px solid #C2185B; color:#880E4F; padding:6px 12px; border-radius:6px; font-size:0.9em;">三层分工模型</span>
<span style="background:#fff; border:1.5px solid #C2185B; color:#880E4F; padding:6px 12px; border-radius:6px; font-size:0.9em;">规范驱动开发 SDD</span>
</div>
</div>
<!-- 下一个分区卡片紧接着写,不能空行 -->
</div>
模板 A2 — 网格卡片(2 列 / 3 列网格,每张同等大小)
适用于:方法论矩阵、能力九宫格、特性对比卡
结构:外层 display:grid; grid-template-columns:repeat(N, 1fr) → 每张卡片同结构(标题 + 描述)
<div style="display:grid; grid-template-columns:repeat(2, 1fr); gap:10px; margin:1em 0;">
<div style="background:#E8F5E9; border:1.5px solid #2E7D32; border-radius:8px; padding:12px;">
<div style="font-weight:bold; color:#1B5E20; margin-bottom:6px; font-size:1em;">🧩 三层分工</div>
<div style="color:#2E7D32; font-size:0.85em; line-height:1.5;">你负责思考<br/>AI / Agent 负责执行</div>
</div>
<!-- 下一张紧接着写,不能空行 -->
</div>
卡片配色板(7 色循环,对每个分区/卡片分配一种色组,保持视觉区分度):
| 色系 | 面板底色 | 边框色 | 文字色(标题/正文) |
|---|---|---|---|
| 粉 | #FCE4EC | #C2185B | #880E4F / #AD1457 |
| 橙 | #FFF3E0 | #E65100 | #BF360C / #E65100 |
| 黄绿 | #F1F8E9 | #558B2F | #33691E / #558B2F |
| 绿 | #E8F5E9 | #2E7D32 | #1B5E20 / #2E7D32 |
| 青 | #E0F2F1 | #00695C | #004D40 / #00695C |
| 蓝 | #E3F2FD | #1565C0 | #0D47A1 / #1565C0 |
| 紫 | #F3E5F5 | #6A1B9A | #4A148C / #6A1B9A |
| 琥珀(备用) | #FFF8E1 | #F57F17 | #E65100 / #F57F17 |
配色应用原则:
写卡片的硬性要求:
font-weight:bold;副标题用 0.85em + 中等饱和度色padding:6px 12px; border-radius:6px; font-size:0.9emborder-radius:8-10px; padding:12-14pxgap:10px(不要用 margin,用 flex/grid 的 gap)Mermaid 适用范围(再次强调):节点之间有明确方向连接的图——流程、时序、状态、决策、思维导图、类图、ER。 禁止用 Mermaid 画:多分区陈列总览图(用 HTML 卡片)、方法论矩阵(用 HTML 卡片)。
| 内容类型 | 推荐图表 | Mermaid 语法 |
|---|---|---|
| 流程 / 链路 / 步骤 | 流程图 | graph TD 或 graph LR |
| 时序 / 交互 / 调用链 | 时序图 | sequenceDiagram |
| 思维导图 / 全文俯瞰 | 思维导图 | mindmap |
| 类 / 接口 / 继承 | 类图 | classDiagram |
| 数据模型 / 表关系 | ER 图 | erDiagram |
| 状态机 / 生命周期 | 状态图 | stateDiagram-v2 |
🚫 禁止的 Mermaid 反模式:
ROOT --> P1T、ROOT --> P2T…)——这种图视觉噪音极大,且本质上是"分区陈列",应改用 HTML 卡片~~~ 强连:会让外层 TB 方向覆盖 subgraph 内的 direction LR,导致子项错乱。需要顺序时让 Mermaid 自然按声明顺序排~~~:除非确实需要强制水平线性,否则不要用——会产生意外的不可见连线A[X] B[Y] 这种是大忌;节点 ID 可以用字母,但 [...] 内的显示文字必须是有意义的中文/术语🚨 铁律:节点内换行必须用 <br/>,绝对禁止 \n
Mermaid 渲染器不识别 \n 转义符——写 A["第一行\n第二行"] 在 Obsidian 里只会原样显示反斜杠 n,完全不换行。正确写法是 A["第一行<br/>第二行"],且节点标签须用双引号包裹。
❌ 错误(常见踩坑):
S1["第一步\n项目骨架\nVue3+Vite"] --> S2["第二步\naxios层"]
✅ 正确:
S1["第一步<br/>项目骨架<br/>Vue3+Vite"] --> S2["第二步<br/>axios层"]
图表详细度要求:
A[模型层] → ✅ A["模型层<br/>提供 LLM 能力"]A --> B → ✅ A -->|调用| B,让人不用看正文也能读懂连线含义A{需要异步?} -->|是| B[消息队列] A -->|否| C[同步调用]统一色板(Mermaid 节点高亮):
| 节点语义 | 颜色 | 样式写法 |
|---|---|---|
| 起点 / 入口 | 绿色 | fill:#4CAF50,stroke:#388E3C,color:#fff |
| 核心概念 / 重点 | 蓝色 | fill:#2196F3,stroke:#1565C0,color:#fff |
| 结果 / 输出 | 橙色 | fill:#FF9800,stroke:#E65100,color:#fff |
| 风险 / 警告 | 红色 | fill:#f44336,stroke:#B71C1C,color:#fff |
| 普通节点(默认) | 浅灰 | 不加 style,使用 Mermaid 默认 |
各图表类型细化规范:
graph TD:流程首选,链路/对比选 graph LRsequenceDiagram:参与者名称使用中文;消息描述简短(≤ 15 字);用 Note over 标注关键说明mindmap:根节点为文章核心主题;第二层为各 ## 章节;第三层为关键要点;层级不超过 4 层。总结章节的 mindmap 应覆盖全文所有 ## 章节stateDiagram-v2:明确 [*] 起止;转移条件写在箭头上subgraph 分组名 归组,但不要在 subgraph 之间加连线——让 dagre 自然排列即可Frontmatter(文件最顶部的 YAML 元数据):
---
title: "[文章标题]"
course: "[课程名称]"
tags:
- geektime
- [技术领域标签,小写+连字符,如 golang / distributed-system / database]
date: [通过 Bash 执行 `date +%Y-%m-%d` 获取的当前日期,格式 YYYY-MM-DD]
source: "[原始mhtml文件名]"
---
可选字段 related / 正文 wikilink:仅当原文明确提到前置/相关讲次(如"详见第 08 讲 Hooks")且能从文件名推断对应 wikilink 时才添加 frontmatter related 字段或在正文使用 [[讲次号-主题]];不确定就整字段/链接省略,不要写空数组、占位符或硬编造,避免产生 Obsidian 断链。
Obsidian Callout 块完整规范:
| Callout 类型 | 使用场景 | 语法示例 |
|---|---|---|
[!ABSTRACT] | 文章开头一句话摘要 | > [!ABSTRACT] 一句话摘要 |
[!NOTE] | 重要知识点、概念定义 | > [!NOTE] 核心概念 |
[!INFO] | 关键数据、统计数字、版本信息 | > [!INFO] 关键数字 |
[!TIP] | 最佳实践、操作建议、技巧 | > [!TIP] 最佳实践 |
[!WARNING] | 常见误区、易错点、注意事项 | > [!WARNING] 避坑指南 |
[!QUESTION] | 文末思考题(每题单独一个) | > [!QUESTION] 思考题 1 |
[!QUOTE] | 作者金句、核心洞见原话 | > [!QUOTE] 作者原话 |
[!EXAMPLE] | 具体案例、对比举例 | > [!EXAMPLE] 示例 |
折叠 Callout(长参考内容使用,默认收起):
> [!NOTE]- 完整配置参数速查(点击展开)
> 此处放详细的配置表格或长列表...
在 [!TYPE] 后加 - 表示默认折叠,+ 表示默认展开。适用于 frontmatter 字段速查、权限控制详情等参考型内容。
其他正文格式规范:
#)到 H4(####),H1 仅用于文章主标题。**文本** 强调核心术语(每段不超过 2 处)。<img src="http..."、外部 <style> 标签或外部脚本;HTML 卡片的 inline <div>/<span> + inline style= 属性不受此限。文件命名规则:[讲次编号]-[简短主题].md
01、09、23);无数字编号时保留文字前缀(如 开篇词、热点速递)。01-你是架构师.md、开篇词-以Claude-Code造简版Dify.md目标目录推导(极客时间课程统一收纳在 260_Courses 下):若未指定 Target_Directory,按以下步骤推导目录:
/Users/fengyuhao/SecondBrain/200_Learning/260_Courses/260X_GeekTime_* 编号序列(已存在:2601 / 2602 / 2603 / 2604 / 2605 / …):
for d in /Users/fengyuhao/SecondBrain/200_Learning/260_Courses/260*_GeekTime_*/; do basename "$d"; done
⚠️ 不要用 ls … | grep '^260…':用户环境中 ls 被别名为带图标的工具(每个名字前注入一个图标字形),会破坏 ^260 行首锚点导致 grep 漏掉全部已有目录、错误地从 2601 重新开始。glob 直接由 shell 展开,不经过 ls 别名,最稳。取序列中最大编号 + 1 作为新课程编号。2604_GeekTime_AI_Reshaping_Test_Engineering);找到则复用,否则取下一个未占用的编号(如 2604)并创建新目录。260<N>_GeekTime_<EnglishCamelOrSnake>。<EnglishCamelOrSnake> 是课程名的英文意译(不是音译,不是拼音),用下划线连接单词,首字母大写。例:
2603_GeekTime_Claude_Code_Enterprise_Level_End_to_End2604_GeekTime_AI_Reshaping_Test_Engineering2602_GeekTime_Multi_Agent_Design_Engineeringmkdir -p 创建。❌ 禁止:直接在 200_Learning/ 根目录创建中文目录(如 Claude-Code-企业级全链路开发实战)——这会破坏现有 260_Courses/ 收纳体系。
将最终 Markdown 内容写入目标目录。
写入完成后不要在对话框中打印笔记正文(避免冗余刷屏),但必须继续执行 Step 6 的回读核查并输出 checklist 与完成报告。
通过 Bash 命令核查,禁止用 Read 工具整体回读大文件(文件全文重新进入上下文,叠加前面的 mhtml 原文 + 图片 base64 数据,会直接触发请求体超限错误)。将 <FILE> 替换为笔记绝对路径,逐条执行下方命令,根据输出填写 checklist:
# 核查 1 & 8:字符数(同时验证 > 1000 且 ≥ SLATE_CHARS × 60%)
wc -m <FILE>
# 核查 2:Frontmatter 字段(期望输出 5 行,覆盖 title/course/tags/date/source)
grep -E "^(title|course|tags|date|source):" <FILE>
# 核查 2b:tags 列表非空(期望输出 ≥ 2,至少 geektime + 1 个领域标签)
awk '/^tags:/{flag=1; next} flag && /^[^[:space:]-]/ {flag=0} flag && /^[[:space:]]*-/{count++} END{print count+0}' <FILE>
# 核查 3:章节级标题列表(对照 Step 1 覆盖清单逐一确认)
grep "^## " <FILE>
# 核查 4:可视化块计数
grep -c '```mermaid' <FILE>
grep -c 'mindmap' <FILE>
grep -cE 'display:(grid|flex)' <FILE>
# 核查 6:思考题(原文有思考题时期望 ≥ 1)
grep -c '\[!QUESTION\]' <FILE>
# 核查 7:外部资源污染(三项期望均为 0)
grep -c '!\[\](https://' <FILE>
grep -cF '<img src="http' <FILE>
grep -c '<style>' <FILE>
# 核查 9:Mermaid 节点内 \n 换行(期望为 0;Mermaid 不渲染 \n,必须用 <br/>)
python3 -c "
import re
content = open('<FILE>').read()
blocks = re.findall(r'\`\`\`mermaid\n(.*?)\`\`\`', content, re.DOTALL)
count = sum(1 for b in blocks for line in b.split('\n') if r'\n' in line)
print(count)
"
基于以上命令输出结果执行以下 7 项核查,以 checklist 形式输出结果:
| # | 核查项 | 通过标准 |
|---|---|---|
| 1 | 文件存在且有实质内容 | 文件可读,字符数 > 1000 |
| 2 | Frontmatter 完整 | 包含 title / course / tags / date / source 五个字段;tags 列表至少含 2 项(geektime + 领域标签) |
| 3 | 章节级覆盖无遗漏 | 对照 Step 1 的章节级覆盖清单,原文每个有效章节级标题在笔记中有对应章节;扁平叙述型文章则核对 Step 1 识别的每个「逻辑主题块」是否已归入笔记某个 ##/### 节(而非核对字面标题) |
| 4 | 可视化块充分 | ① 至少 2 个可视化块(Mermaid + HTML 卡片任意组合),且总结章节必须含 1 个 mindmap;② Mermaid 不出现单字母显示标签(节点 ID 可字母但 [...] 内显示文字必须有意义);③ 有方向的 graph 箭头中至少 80% 带 |文字| 关系说明;④ HTML 卡片必须用 Step 3·A 的两种模板之一,包含 emoji 图标 + 配色板 |
| 5 | HTML 卡片块内无空行 | 用 grep -Pzo '<div[^>]*style="[^"]*display:(grid|flex)[^"]*"[^>]*>[\s\S]*?</div>' 抽取每个卡片容器后,容器内不得有 \n\s*\n(连续两个换行)。这是 HTML 卡片能否正确渲染的硬条件——失败则 Obsidian 会把子 <div> 包进 <p>,破坏 grid/flex |
| 6 | 思考题处理正确 | 原文有思考题 → 笔记中有 [!QUESTION] 和折叠参考答案;原文无 → 该项自动通过 |
| 7 | 无外部资源污染 | 笔记中不含 ≥ Step 1a 记录的 SLATE_CHARS 值 × 60% |
| 9 | Mermaid 节点无 \n 换行 | 所有 mermaid 代码块内的节点标签不含 \n 转义符(期望核查命令输出 0);换行必须用 <br/> |
HTML 卡片空行检查的简化命令(可直接复制执行):
# 替换 <FILE> 为笔记绝对路径;若有任何输出,说明卡片内存在空行,需修复
python3 -c "
import re, sys
content = open('<FILE>').read()
# 匹配所有 display:grid 或 display:flex 的顶层 div 块
pattern = re.compile(r'<div[^>]*style=\"[^\"]*display:(grid|flex)[^\"]*\"[^>]*>(.*?)</div>\s*(?=<div|\n##|\n---|\Z)', re.DOTALL)
violations = []
for m in pattern.finditer(content):
block = m.group(0)
if re.search(r'\n\s*\n', block):
violations.append(content[:m.start()].count(chr(10))+1)
print('VIOLATIONS at lines:', violations) if violations else print('OK')
"
输出格式(写在完成报告之前):
## 质量核查结果
- [x] 文件存在且有实质内容(xxxx 字符)
- [x] Frontmatter 完整(tags 列表 N 项)
- [x] 章节级覆盖无遗漏(原文章节级 = H1/H2,共 N 个,笔记已覆盖 N 个)
- [x] 可视化块充分(Mermaid N 个 + HTML 卡片 M 个,含 mindmap)
- [x] HTML 卡片块内无空行
- [x] 思考题处理正确
- [x] 无外部资源污染
- [ ] 内容未过度压缩 ⚠️ 笔记 xxxx 字,原文 xxxx 字,未达 60%
- [x] Mermaid 节点无 \n 换行
核查失败处理:
完成报告:核查 checklist 输出后,紧跟一段简短报告,包含:文章标题 & 课程名、文件完整路径、Mermaid 图表数量及类型、思考题状态。
Creates, edits, and optimizes skills for Claude Code, including drafting, evaluating with test prompts, iterating on performance, and improving skill descriptions for better triggering accuracy.
npx claudepluginhub skytechfyh/ai-marketplace --plugin mhtml-refine-to-md