agentleFS
Trouvaille-xxTrouvaille-xx/mece-decompose-skill/SKILL.md

mece-decompose

Skill1 starsChanged 50 days agoMITView on GitHub

对一个主题做逐级、带依据的 MECE 树状拆解,并产出可交互的思维导图 HTML。当用户要求「拆解」「MECE 拆解」「结构化拆解」「逐级拆解」「思维导图拆解」「把这个主题拆成树」,或给出一个主题(如 PPT 设计、用户增长、供应链履约)并要求分析维度、分层展开时使用。每次只拆解最近一层,每个父节点标注本层使用的拆解依据,支持包含性/流程性/类型学等依据切换。产出物含 10 套视觉风格预设、右侧可折叠的样式调整胶囊(主题/配色/方框/连线/字体/密度)与 JSON 导出。

1 other project uses a version of this.

Adopted by

Most recently taken up by

  • Other versions1 project uses a version of this.

    Word for word · 0

    None found.

    Edited · 0

    None found.

    Wrote their own version · 1

    1. Lukehle/claude-quorum★ 0same name

    How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.

The file

Files
SKILL.md

MECE 逐级拆解

把一个主题拆成一棵带依据的树,而不是一坨并列的要点。

核心区别在于两点:一是逐级——每轮只拆当前最末端那一层,不跳级、不预设;二是带依据——每一次切开都必须说清楚「凭什么这么切」,依据挂在父节点上,可被审查、可被替换。

何时使用

  • 用户给出一个主题,要求拆解维度、做结构化分析、画思维导图。
  • 用户要求「MECE 拆解」「逐级拆解」「拆到可执行」。
  • 用户已有拆解结果,要求补齐依据、检查 MECE、或重新可视化。

不适用:单纯画流程图的(用 flowchart)、单纯做架构图的(用 archify / diagram-design)、单纯列要点的(直接回答即可)。

三条铁律

违反任意一条,拆解结果就是无效的。

1. 每轮只拆最近一层。 第 N 轮只处理第 N-1 轮留下的叶子节点。不允许跳过中间层直接给第三层内容,也不允许把尚未拆开的节点当作已拆开继续往下。这条决定了整棵树是否可信——跳级意味着中间那层的依据从未被检验。

2. 每次拆解必须给出依据,依据挂在父节点上。 父节点记录的是「我这一刀是按什么逻辑切的」。子节点继承这条依据的颜色与标签。同一次拆解动作产出的所有子节点,共享同一条依据——不允许在一层里混用两种逻辑。

判断依据是否合格:把子节点全列出来,问「它们合起来是否等于父节点」。若答案是否,说明依据选错了,或漏了维度。

3. 每轮拆完必须停下来确认。 产出该轮结果与依据说明,交用户审阅,由用户指定下一轮拆哪个分支、是否更换依据。不要闷头拆到底。

唯一例外:用户明确说「直接出图」「一次拆满三层」时,可连续拆解,但每轮的依据说明仍需逐轮记录,并在最终 HTML 的详情面板中完整呈现。

拆解依据的类型库

依据不是随便编的。优先从下表挑选,并明确标注类型。这张表本身不追求穷尽,遇到表中没有的合理依据时,自定义并在标签中说明。

类型逻辑典型问法示例
包含性整体由哪些部分组成它由什么构成?演示文稿 = 内容 + 视觉 + 表达 + 交付
流程性按时间/工序先后推进它经历哪几个阶段?设计 = 需求理解 → 草稿 → 定稿 → 交付
类型学按种类穷举它有哪些种类?过渡动效 = 淡入淡出型 / 方向推进型 / 形状分割型…
属性维度按可独立变化的性质切分它有哪几个可调的性质?字体系统 = 字族 + 字号 + 字重 + 行距 + 字距
因果链按目的—手段递进为了达成 X,需要先解决什么?可读性 → 字号 / 行距 / 对比度
要素角色按在系统里扮演的角色切分谁在做事、谁在承接?产品 = 用户 + 场景 + 价值 + 约束
生命周期按对象从生到灭的阶段切分它从哪来、到哪去?需求 = 提出 → 评审 → 开发 → 验收 → 归档
粒度层级按抽象程度分层从战略到执行分别是?目标 = 北极星 → 季度目标 → 周任务

依据可以换,而且这是常态。 同一个节点,换一个依据就会得到完全不同的子树。例如「视觉」按包含性切是「版式/色彩/字体/图像/图表/动效/一致性」,按流程性切则是「设计前调研 → 风格设定 → 逐页实现 → 一致性校验」。两种都合理,取决于要回答什么问题。

依据的三种关系

换依据不只有「作废重来」一种。动笔前先判断属于哪种,选错了树就是歪的。

关系什么情况用怎么落地
替换旧依据下的子树答不了当前问题,应当作废重拆该层,原依据写入 basis.superseded 留痕,详情面板显示删除线
并存两条依据各自成立、都要保留,回答的是不同问题两棵子树都要存,需要模板支持平行视图(见下)
分层一条是决策入口(该选哪个),一条是执行细节(选定后怎么调)给父节点加一层「包含性」包装,把两种依据拆到两个子节点上,各自用自己的依据

分层的具体做法(最常用,优先考虑):

不要把一个节点的子节点混用两种依据——那违反「同层同依据」。正确做法是先按包含性把它拆成「选型」和「调参」两块,再让每块用自己的依据往下拆:

版式布局                          ← 依据:包含性(选型 + 调参两件事)
├─ 版式选型                       ← 依据:类型学(该用哪个)
│   叙事类 / 结构类 / 对比类 / 数据类 / 内容类 / 特殊类
└─ 版式调参                       ← 依据:属性维度(选定后怎么调)
    网格系统 / 对齐关系 / 留白密度 / 层级对比 / 阅读动线

这样既守住了「同层同依据」,又把「决策入口 → 执行细节」的先后讲清楚了。命名时让子节点名字自带职责(「选型」/「调参」),读者一眼知道谁管什么。

判断用哪种关系:

  • 两条依据回答的是同一个问题的不同角度 → 替换(留一条即可)。
  • 两条依据回答的是不同问题,且用户两个都要 → 并存或分层。
  • 两条依据之间有先后依赖(A 的产出是 B 的输入)→ 分层。

换依据的时机

  • 当前依据下的子树无法支撑用户的实际目的(比如用户要落地执行,包含性切出来的是静态分类,帮不上)。
  • 当前依据切出的子节点颗粒度严重不均。
  • 用户主动要求换。

换依据时必须在对话里说明为什么换,并在 HTML 详情面板中保留痕迹(见下)。

执行流程

第 0 步:先问目的,别急着拆

拆解依据的选择完全取决于目的。同一个「PPT 设计」,为「培训新人」拆和为「做质检清单」拆,树长得完全不一样。

开始前确认三件事(用户已说明的跳过):

  1. 拆给谁看、用来干什么 —— 决定拆到多深、依据选哪个。
  2. 拆到哪一层算完 —— 是拆到「能直接动手」还是「讲清楚概念即可」。
  3. 有没有已知的骨架 —— 用户心里已有分类时,优先顺着他的分类拆,只补依据和补齐 MECE。

用户回答含糊时,给两个候选依据让他选,而不是继续追问。

第 1 步:拆解并输出本轮结果

每轮输出四块内容:

  1. 本轮拆的是哪个节点(给出路径,如 PPT设计 › 视觉层面)
  2. 依据:类型 + 一句话逻辑 + 为什么这条依据适配当前目的
  3. 子节点清单:每个子节点给出名称 + 一句简介 + 它包含的具体要素(用顿号列举,像示例中「网格、对齐、留白、层级、阅读动线,决定元素放在哪」的写法)
  4. MECE 自检:说明是否有重叠、是否有遗漏、颗粒度是否均衡

写子节点简介时,句式统一为「要素列举,决定它管什么」。这样每个节点都自带「包含什么」和「为什么重要」两层信息。

第 2 步:停下确认,再进下一轮

给出本轮结果后,明确询问:下一轮拆哪几个分支?依据保持还是更换?

用户指定后,回到第 1 步。每一轮的拆解记录都要留存——后续生成 HTML 时需要完整数据。

第 3 步:生成交互式思维导图

拆解完成后,按下述规范生成单文件 HTML。

HTML 产出规范

数据准备

每个节点都必须有唯一的 id,这是硬要求。 模板靠 id 做节点查找(allNodes.find(r => r.data.id === ...)),缺了不会报错,但 find 会一律命中第一个节点(根节点),症状是:

  • 点开任意深层节点,详情面板的路径只剩根节点(如 供应链管理 而不是 供应链管理 › 采购 › 对账结算 › 三单匹配);
  • 连线数量看起来正常,但坐标全错,线都堆在根节点附近。

生成数据时用父路径编号批量补 id(n1、n1-2、n1-2-3),别手写。校验时必须断言详情面板的路径含 ›——只看连线数量会漏掉这个问题。

先把整棵树写成 JSON,放进 HTML 的 <script> 中。结构如下:

{
  "root": {
    "id": "n0",
    "label": "PPT设计",
    "intro": "把信息转化为可被观众理解的视觉呈现",
    "basis": null,
    "basisType": "root",
    "children": [
      {
        "id": "n1",
        "label": "视觉层面",
        "intro": "版式、色彩、字体、图像、图表、动效、一致性,决定它看起来怎么样",
        "basis": {
          "type": "包含性",
          "logic": "演示文稿的视觉由若干个可独立调节的子系统共同构成,拆开后可分别优化",
          "why": "用户目标是落地执行,包含性切出的静态分类可直接对应到具体工作项",
          "superseded": null
        },
        "children": [ ... ]
      }
    ]
  }
}

字段说明:

字段含义
label节点名称,2–6 字
intro一句话简介,句式「要素列举,决定什么」
basis本节点作为父节点时,切开它所依据的逻辑。叶子节点为 null
basis.type依据类型,取自依据类型库
basis.logic这条依据的核心逻辑,一句话
basis.why为什么这里选它(适配什么目的)
basis.superseded若本层换过依据,记录原依据及更换原因;否则 null
layer仅依据分层时使用。取值 "decide"(决策层)或 "exec"(执行层),卡片上会显示对应徽标
layerNote仅父节点使用。说明「这一层为什么用两条依据」,详情面板单独展示

视觉与交互要求

不要从零写 HTML。 assets/template.html 是一个已经跑通的完整实现(含主题引擎、样式胶囊、JSON 导出),把它复制一份,只替换 const DATA 和 let cfg 两处即可。下面这些是模板已满足的标准,改模板时用来对照,不是让你重新实现一遍。

布局

  • 横向可折叠树,从左到右逐层展开。根节点在左,每层向右推进。
  • 节点为卡片,带柔和阴影,浅灰底配白卡(深色主题反之)。
  • 层级列用 flex 排布,节点之间用 SVG 连接线绘制。不要用 CSS 伪元素——伪元素画不了贝塞尔曲线和锥形收束。
  • SVG 层放在树容器内、节点层之下(z-index 分层),坐标按 getBoundingClientRect() 除以当前缩放比换算,窗口尺寸变化与折叠动作后重绘。
  • 折叠/展开用平滑过渡,不要生硬跳变。

配色

  • 卡片色表达分组,标签色表达依据,两者分工不能混。
  • 分组色:同一个父节点下的子节点共用一个颜色,相邻两组必然换色。这是读者判断「谁和谁是一伙的」的主要线索。
  • 依据色:卡片右上角的依据标签固定用该依据的专属色(包含性紫、类型学橙、流程性蓝…),不跟随配色模式变化,否则依据信息会丢。
  • 卡片左侧一条 3–4px 的竖条作为视觉锚点,颜色跟卡片主色。
  • 另有统一配色 / 彩虹配色两种模式,见下「样式调整:右侧居中的可折叠胶囊」,模板已内置,不要另起炉灶。

兄弟间距必须分层

同一列上的卡片如果等距排布,读者分不出「哪几张属于同一个父节点」。实测过一次:19 个分支贴着排,相邻间距全是 0,看图完全不知道前 11 个属于学科分支、后 8 个属于理论流派。

模板用四级间距变量做这件事(--sp0 到 --sp3,标准模式 56/22/9/6,紧凑模式 46/18/7/5),按深度递减:

可见列组内间距跨组间距比例
第 1 层--sp1 = 22--sp0 = 562.5×
第 2 层--sp2 = 9--sp1 = 222.4×
第 3 层--sp3 = 6--sp2 = 91.5×

规律是:任何一层的「跨组间距」都等于上一层的「组内间距」,这样每层都保证「跨组 > 组内」。改间距只需调这四个变量,不要给某个层级单独写死数值。

判断做对了没有,就量一件事:展开到任意一层,跨组间距是否明显大于组内间距。 相等就是没做。

详情面板

  • 点击节点,右侧滑出详情面板,展示:完整路径、简介、本层依据(类型/逻辑/为什么)、原依据(若有)、子节点列表。
  • 面板用 transform: translateX() 滑动进出,配半透明遮罩。

三个必备控件(顶部工具栏)

  1. 搜索框 —— 输入关键词,实时过滤并高亮命中节点,命中的节点自动展开其祖先链。
  2. 层级控制 —— 「展开全部 / 收起全部 / 只看第 N 层」按钮组,其中层级按钮按树的最大深度动态生成。
  3. 依据图例 —— 列出所有出现过的依据类型及其配色,点击可筛选只看某依据的节点。

其他

  • 单文件 HTML,不依赖任何外部 CDN 或字体。字体用系统栈:-apple-system, "Segoe UI", "Microsoft YaHei", sans-serif。
  • 适配窗口宽度,窄屏时横向滚动而非压扁节点。
  • 页面顶部加标题与一句副标题(说明拆解目的),右下角可加一句「共 N 个节点,M 层」的统计。

样式调整:右侧居中的可折叠胶囊

不要做成右上角的按钮 + 抽屉。 样式调整是「边看树边调」的常驻动作,抽屉一旦遮住画布就没法判断改动效果。模板做成了右侧垂直居中的一颗胶囊(#styleDock):

状态表现
收起只有一列 48px 宽的图标,圆角 24px(恰好等于半宽,两端成半圆),贴在右侧 20px、垂直居中,整体 50×308
展开向左长出 338px 的内容面板,图标列保持可见,可连续跳组;圆角收窄到 16px
遮罩没有遮罩,这是刻意省掉的——常驻工具挡住画布就失去意义

图标列共 7 个,每个都用 SVG 画出当前取值,收起状态下它就是唯一的风格回显:

图标画的是什么点击行为
主题配色主题主色实心圆 + 淡色外环展开并跳到该组
配色模式三个小圆点:分组=三色 / 统一=同色 / 彩虹=七彩同上
方框样式圆角矩形 / 直角矩形 / 带左竖条的矩形 / 上下双线同上
连接线样式曲线 / 直角折线 / 实心楔形同上
字体样式用当前字体族、当前档位字号渲染的「Aa」同上
画布密度标准=3 行、紧凑=4 行同上
其他滑块同上

行为细节:再点一次同一图标即收起;✕ 和 Esc 也能收起;点开节点详情面板时胶囊自动收起让位(两者都在右侧,会打架)。

定位反馈:面板在常见视口下能撑到视口高,六组基本全露着,这时点击图标不会发生滚动。所以点击后除了「尽量把目标组滚进视野」,还会给目标组左侧闪一道强调色竖条(.sec.pulse)——它才是「我点了之后落到哪一组」的真正反馈。矮视口下滚动才真正起作用。

内容面板六组:

组取值说明
主题配色10 套预设极简白 / 墨玉黑 / 深空暗 / 商务蓝 / 薄荷绿 / 薰衣草 / 暖阳橙 / 樱花粉 / 森林绿 / 石墨灰
配色模式分组分色 / 统一配色 / 彩虹配色见下
方框样式圆角卡片 / 直角方框 / 左色条 / 下划线卡片外观
连接线样式平滑曲线 / 直角折线 / 锥形收束参考 XMind 的曲线与锥形做法
字体样式字体族:无衬线 / 衬线 / 等宽;字号档位:小 / 中 / 大见下
画布密度标准 / 紧凑节点数 > 40 时自动切紧凑,见下「密度控制」
其他恢复默认 / 复制配置—

字体样式怎么设的:

  • 字体族只给三档,都用系统字体栈,不引任何网络字体(离线单文件是硬要求)。衬线的中文会回退到宋体系,等宽没有真正的中文等宽字体、中文会回退成默认无衬线——只有英文数字呈等宽。这是取舍,不为一个视觉选项塞进几 MB 字体。
  • 字号档位(小/中/大)与画布密度是正交的:密度只管「基准字号」(紧凑模式基准更小),档位只管「倍率」,两者相乘得到最终字号。所以四个组合都成立,改一个不会覆盖另一个。实现上乘法必须写在使用处(.card-label、.card-intro),不能在中转变量里先算好——细节见「改模板时的坑」第 6 条。
  • 三档的倍率差是 0.88 / 1 / 1.16。差得不够大用户看不出来,尤其在大树上人靠「整体观感」判断变化。

三种配色模式的区别:

  • 分组分色(默认)——同一个父节点下的子节点共用一色,相邻组必换色。回答的是「谁和谁是一伙的」,这是读者翻开一张拆解图的第一诉求。
  • 统一配色——全文一个主色。适合正式汇报,或配色需要服从甲方品牌规范时。
  • 彩虹配色——每个一级分支一个色相,往下沿用。适合一级分支少(3–6 个)且需要强区分的场景。

曾经还有一个「依据分色」模式(卡片主色按拆解依据着色),已移除。它在节点少时看着不错,节点一多反而更糊:一次拆解十几个分支,其中十一个都是同一个依据切出来的,于是十一个分支连成一片同色,分组信息彻底丢失。依据色现在只用在标签上,不占卡片主色——别加回来。

选哪套风格:先看用途(投屏宣讲用深空暗或暖阳橙,正式汇报用商务蓝,长文阅读用森林绿或墨玉黑,打印用石墨灰),再看配色模式(默认分组分色;一级分支很少且要强区分时才考虑彩虹配色)。

这套选项是刻意收窄的。 曾经提供过「胶囊形 / 实心填充」两种方框、「直线」一种连线、「单色系」一种配色,用户明确要求删掉;后来又删掉了「依据分色」这个配色模式(理由见上)。保留的都是通用性更强、互相之间差异更清楚的选项。不要自作主张加回去。 新增选项前先确认:它是否与现有某项视觉上足够区分,以及是否真的有使用场景。(字体样式这一组是用户明确要求新增的,不属于「自作主张」的范围。)

样式目录:styles/ 下是 10 个已渲染好的预设文件(01-minimal.html … 10-graphite.html),打开 styles/index.html 可以横向对比全部 10 种。styles/manifest.json 记录了每个预设的组合参数。需要给用户看效果时,直接把对应文件给他。

JSON 导出

模板右上角「导出 JSON」按钮,导出结构如下:

{
  "title": "PPT 设计 · MECE 逐级拆解",
  "exportedAt": "2026-09-23T07:12:33.000Z",
  "style": { "theme": "minimal", "colorMode": "group", "box": null, "link": null, "font": "sans", "fontSize": "m", "density": "normal" },
  "stats": { "nodes": 145, "depth": 5 },
  "tree": { "label": "...", "intro": "...", "basis": { "type": "...", "logic": "...", "why": "..." }, "children": [] }
}

几个约定:

  • 导出时剥掉 id 字段——树状结构靠嵌套表达,节点 ID 在文件外没有意义,反而会让 JSON 变噪。
  • basis 只保留 type / logic / why(以及有则保留的 superseded),父节点承载依据、子节点继承,与源数据结构一致。
  • 带上 style,是为了让这份 JSON 能反过来固定住视觉风格;把 style 里的值填回模板的 cfg 即可复现。
  • 导入 JSON 继续编辑:把导出的 tree 贴回模板 const DATA,节点 ID 缺失时补上 id 即可。

密度控制(节点数 > 40 时必做)

横向树在节点数少时很好看,但节点一多就会纵向拉成细长条——137 个节点全展开时树高接近 9000px,用户要滚很久才能看到深层,根节点还会滚出视野。

对策是紧凑模式:

  • 卡片宽度收到 170px 左右,内边距压缩,简介限制 2 行。
  • 叶子节点只留标签,隐藏简介——简介改由详情面板承载。这条最关键,能让树高直接砍掉约 40%。
  • 连接线间距同步收窄。
  • 节点总数超过 40 时自动启用,并提供一个切换按钮让用户随时切回标准模式。

取舍逻辑:大树上用户是靠搜索和层级导航找节点的,不是靠通读。所以叶子的简介可以牺牲在画布上,只要点开面板能看到就行。

主题适配

先看当前 IDE 主题再定配色。浅色主题用白卡 + 浅灰底 + 深色文字;深色主题用深灰卡 + 更深底 + 浅色文字。不要在浅色主题下用深色面板,反之亦然。

质量自检

生成 HTML 前,逐项确认:

  • 树是否逐级展开,没有跳级?
  • 每个非叶子节点是否都有 basis,且同一父节点下所有子节点共享同一条依据?
  • 若某个节点有两条都成立的依据,是否判断了属于替换/并存/分层中的哪种,并按对应做法处理?
  • 依据分层时,父节点是否有 layerNote 说明为什么用两条,子节点是否标了 layer?
  • 依据类型是否在标签中明确标注?
  • 是否有节点违反了 MECE(子节点之和 ≠ 父节点)?
  • 每个叶子节点是否达到终止条件(可执行动作 / 可测量指标 / 公认原子概念)?
  • HTML 是否单文件、无外部依赖、能离线打开?
  • 同一列上的卡片,跨组间距是否明显大于组内间距?等距排列会让读者看不出「哪几张属于同一个父节点」。
  • 同一个父节点下的子节点是否同色、相邻组是否换色?改过分组色逻辑的话,要真去读一遍各组的实际色值。
  • 所有节点 ID 是否唯一(len(set(ids)) == len(ids))?重复会让详情面板路径静默残缺。
  • 是否基于 assets/template.html 改的(只换 DATA 与 cfg),而不是从零重写?
  • 三个必备控件是否都能用?
  • 右侧样式胶囊收起时是否是一颗 50×308 的小胶囊(不是顶天立地的白条)?收起状态点开一个图标,看面板是否正常展开、图标是否反映当前取值。
  • 「样式」胶囊与「导出 JSON」是否都能用?
  • 节点数超过 40 时,是否启用了紧凑模式,避免树被拉成细长条?

终止条件

拆到以下三种状态之一即停止,不再往下拆:

  1. 可执行动作 —— 拆成了能直接指派给某人的具体工作(如「设置母版页脚」)。
  2. 可测量指标 —— 拆成了能定量的量(如「行距 1.5 倍」)。
  3. 业界公认原子概念 —— 再拆下去会变成对同一个词的重复解释(如「淡入淡出」)。

反面信号:某个节点再往下拆,只是把它的简介换个说法重写一遍——那就是到了原子层,停。

参考

  • 依据类型库的详细说明与切换案例 → references/basis-library.md
  • 完整拆解范例(PPT 设计,三层)→ references/example-ppt.md
  • HTML 模板(改这个,别从零写) → assets/template.html
  • 10 种风格预设 + 总览页 → styles/index.html、styles/01-minimal.html … styles/10-graphite.html
  • 风格参数清单 → styles/manifest.json
  • 改过模板后重新生成预设 → scripts/gen-styles.py(读 assets/template.html,产出 styles/)

改模板的操作步骤

  1. 复制 assets/template.html 到工作目录,重命名为「主题名-MECE逐级拆解.html」。
  2. 替换 const DATA = { … }; 整块,填本轮拆解结果(JSON 字面量,注意 basis 挂父节点)。
  3. 按用途改 let cfg = {theme:…, color:…, box:…, link:…, density:…, font:…, fsize:…};。box / link 填 null 表示跟随主题预设;font 取 sans|serif|mono、fsize 取 s|m|l。
  4. 改 <h1 id="pageTitle"> 与 <p id="pageSub">。
  5. 校验:在浏览器里跑一遍,确认节点数、层数、折叠、搜索、层级切换、样式胶囊(含字体与字号)、导出 JSON 都正常。不要只做静态检查——树渲染失败时通常没有控制台报错,必须真打开看。

校验脚本实操(用 Playwright 无头驱动时)

  • 本机没有 Playwright 自带浏览器,且 channel: 'chrome' 会直接崩。 %LOCALAPPDATA%\ms-playwright 里只有 *_headless_shell,chromium.launch() 报 Executable doesn't exist;退而用 channel: 'chrome' 实测启动即失败。可用写法是显式指本机 Chrome 的路径:chromium.launch({ executablePath: 'C:/Program Files/Google/Chrome/Application/chrome.exe' })。
  • 只有「详情面板」会带遮罩,样式胶囊不带。 点开节点后 #mask 与 .drawer 会拦截指针事件,导致下一个 click() 超时并报 intercepts pointer events——这是脚本问题,不是页面坏了,别去改页面。在连续点击之间插一次 page.keyboard.press('Escape') 关掉即可。样式胶囊是常驻无遮罩的,切换分组不受影响。
  • 点图标「是否滚动」不能当断言。 面板在常规视口下六组基本全露着,可滚范围可能只剩 20–30px,点任何图标都不会滚。要断言的是「目标组完整可见 + 定位提示(.sec.pulse)落在正确的 sec-* 上」,不是「scrollTop > 0」。想测滚动必须先把视口压矮(如 1600×680)让内容超出。
  • 断言写完先验它「会不会假通过」。 两个教训:一是选择器写错时 querySelectorAll 返回空数组,every() 对空数组恒真,于是断言一路绿;二是断言对象搞反逻辑(把「同组同色」写成「同组异色」)。做归属/分组类断言时,先打印出「识别到了几组、几对」,数量为 0 就是这个断言根本没在工作。
  • 值得断言的最小集合:#tree .card 数 === 节点总数;展开全部后 #links path 数 === 节点总数 − 1;点一个深层节点确认详情面板的路径含 › 且完整(只数连线会漏掉 id 缺失的坑);搜索一个高频词看命中数;#links 的 height 应等于 #tree 的 scrollHeight(不等说明踩了「SVG 自引用」那个坑);样式胶囊收起宽度 === 50、展开 === 338 + 图标数 === 7;切一次字体族和字号档位后读 getComputedStyle(...).fontFamily / fontSize 确认真的变了;最后确认零 console 报错。

改模板时的六个坑

这六个都是实际踩过的,且都不报错——页面看起来正常,只有真去点、真去量才暴露。

1. 建 DOM 的函数,返回值必须显式挂上去。 build() 返回的是记录对象,不是已插入的节点。忘了 treeEl.appendChild(rootRec.el) 的话,页面全空但控制台干净。改完先确认卡片数等于节点数。

2. SVG 层量尺寸会自引用。 连接线的画布尺寸若用 treeEl.scrollWidth/scrollHeight 来量,而 SVG 又是 #tree 的子元素——上一轮设进去的宽高会撑大 #tree 的 scroll 尺寸,于是永远量到上一轮的值。症状是改了密度/折叠后连线不跟随,且底部多出一大片空白(实测虚高到 9929px,真实只有 1642px)。修法是先把 SVG 的 width/height 归零再量。

3. 节点 ID 必须唯一,否则详情面板路径会静默残缺。 用 Python 脚本批量生成 DATA 时,递归分配 ID 的函数不能写成 def assign_ids(n, ctr=[0])。这是可变默认参数陷阱:递归调用若漏传 ctr,子节点会用另一份默认计数器从 0 重新开始,于是根节点和第一个子节点都叫 n0。

症状极隐蔽——卡片、连线、配色、折叠全都正常,只有点节点看详情面板时,路径会从重复处截断(点「用户研究」只显示「UX设计」,而点它下面的「研究规划」路径却是完整的)。因为面板是靠 ID 反查父链的,ID 一重复,链就断在错的地方。

修法:def assign_ids(n, ctr) 显式传参,递归时写 assign_ids(ch, ctr)。生成后断言 len(set(ids)) == len(ids)。

顺带一个更阴的:如果脚本里恰好有一次「预热调用」先把默认计数器推上去,真正的调用就会碰巧正确——bug 被掩盖,直到某天有人删掉预热调用才爆发。所以不能靠「跑出来是对的」来判断,要靠断言。

4. 删选项时要连带清理三处。 选项不是只写在 BOX_MODES 这类数组里就完了。删一个取值需要同步清理四处,漏一处就会出现「面板里没了但主题还在用」的鬼状态:

位置说明
选项数组COLOR_MODES / BOX_MODES / LINK_MODES
CSS 规则body.box-xxx 那一整组;连线样式在 redrawLinks() 的 if/else 分支里
主题预设THEMES 里可能仍有 box:"pill" 之类的引用,要改派到保留的选项
生成脚本scripts/gen-styles.py 里的名称映射表与 THEMES 列表,否则重跑一次预设就把旧选项带回来

删完跑一次全量:卡片数、连线数、色块数、面板可开、零报错。主题引用了已删的选项不会报错,只会静默回退成默认外观,所以必须真打开看。

验证脚本本身也会骗人。 归属类断言(如「某层是否沿用某祖先的属性」)不能用「第 N 层的序号」做标识——跨一级分支时序号会重号(每个分支的第 5 个子节点都叫 4),事后反查必然匹配到别人家的祖先,报出一堆假失败。归属标识要用节点自己的标签,不能用手工编号。

反方向也有:选择器写错时 querySelectorAll 返回空数组,every() 对空数组恒真,断言一片绿其实没在工作。做分组/归属类断言时,先打印「识别到了几组、几对」,数量为 0 就说明这个断言是空转的。

5. 分组色不能用「构建序号」取模。

给节点算分组色时,直觉写法是「拿父节点的构建序号对调色板长度取模」。这个写法在树上必错:构建是深度优先的,同一层的两个父节点,序号可能差着整棵子树的规模。一旦差值正好是调色板长度的整数倍就撞色。

实测:学科分支序号是 1、理论流派序号是 57,57 % 8 === 1 % 8——于是两个一级分支下的 19 个模块全成了同一个橙色,分组信息归零,比改之前还糊。

修法:用一个只发给「有孩子的节点」的独立计数器(groupCounter),它的孩子共用这个号。相邻父节点拿到的号必然连号,所以相邻组一定换色。

这类 bug 不报错、页面照常渲染,只是颜色全错。所以改完颜色逻辑必须真去读一遍各组的实际色值,别只看「卡片上有颜色」就以为对了——「有颜色」和「颜色是对的」是两件事。

6. 自定义属性里的 calc() 不能用来「中转」。

字号要同时受「画布密度」(定基准)和「字号档位」(定倍率)控制。直觉写法是在 :root 里定义两个中转变量:

:root{ --fs-label: calc(var(--fs-base-label) * var(--fs-scale)); }  /* ✗ 错的 */
.card-label{ font-size: var(--fs-label); }

这样密度和字号档位会双双失效。 原因:普通自定义属性的值在声明它的那条规则上就求值完毕,之后作为一个整体往下继承。:root 上的 calc() 在根元素处就算死了,body.compact 覆写 --fs-base-label、body.fs-l 覆写 --fs-scale 都影响不到早已算完的结果。

症状:无论怎么切密度和档位,字号恒定不变(实测永远是 13.5px,因为根上的 1 被算进去了)。改法是把乘法搬到使用处:

:root{ --fs-base-label:13.5px; --fs-scale:1; }
body.compact{ --fs-base-label:12.5px; }
body.fs-l{ --fs-scale:1.16; }
.card-label{ font-size: calc(var(--fs-base-label) * var(--fs-scale)); }  /* ✓ */

同一个坑还有个变体:width:0 的元素上留 border-left。 折叠面板收起时宽度设为 0,但 1px 的左边框仍会占位,整颗胶囊就从 50px 变成 51px,两端的半圆半径对不上。改用 inset 阴影画分隔线(不参与布局)。

这类问题的共同点是**「值看着有、其实没生效」或「宽度差 1px」**,靠肉眼看截图很难发现,必须用探针读出 getComputedStyle 的实测值来断言。

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.