@topmindspace/tms-skills 2.0.0 → 2.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +139 -123
- package/README.md +126 -103
- package/bin/tms-skills.js +160 -160
- package/package.json +48 -48
- package/top-ppt-html/README.md +254 -253
- package/top-ppt-html/SKILL.md +111 -111
- package/top-ppt-html/assets/examples/2026-09-09-architecture-graphite-dark.html +3873 -3926
- package/top-ppt-html/assets/examples/2026-09-09-architecture-graphite-dark.model.json +167 -167
- package/top-ppt-html/assets/examples/2026-09-09-architecture-spectrum.html +3873 -3926
- package/top-ppt-html/assets/examples/2026-09-09-architecture-spectrum.model.json +167 -167
- package/top-ppt-html/assets/examples/2026-09-09-presentation-apple-mono.html +4272 -4325
- package/top-ppt-html/assets/examples/2026-09-09-presentation-apple-mono.model.json +320 -320
- package/top-ppt-html/assets/examples/2026-09-09-presentation-brand-red.html +4272 -4325
- package/top-ppt-html/assets/examples/2026-09-09-presentation-brand-red.model.json +320 -320
- package/top-ppt-html/assets/examples/2026-09-09-presentation-business-blue.html +4272 -4325
- package/top-ppt-html/assets/examples/2026-09-09-presentation-business-blue.model.json +320 -320
- package/top-ppt-html/assets/examples/2026-09-09-research-deep-teal.html +5474 -5527
- package/top-ppt-html/assets/examples/2026-09-09-research-deep-teal.model.json +913 -913
- package/top-ppt-html/assets/examples/2026-09-09-research-indigo-violet.html +5474 -5527
- package/top-ppt-html/assets/examples/2026-09-09-research-indigo-violet.model.json +913 -913
- package/top-ppt-html/assets/examples/2026-09-09-research-mckinsey.html +5474 -5527
- package/top-ppt-html/assets/examples/2026-09-09-research-mckinsey.model.json +913 -913
- package/top-ppt-html/assets/examples/2026-09-09-research-warm-sand.html +5474 -5527
- package/top-ppt-html/assets/examples/2026-09-09-research-warm-sand.model.json +913 -913
- package/top-ppt-html/assets/pptx-export.js +1944 -1944
- package/top-ppt-html/assets/style-gallery.html +559 -589
- package/top-ppt-html/assets/templates/architecture.html +3675 -3728
- package/top-ppt-html/assets/templates/engine.css +787 -840
- package/top-ppt-html/assets/templates/presentation.html +3685 -3738
- package/top-ppt-html/assets/templates/research.html +3964 -4017
- package/top-ppt-html/assets/templates/ui.js +520 -520
- package/top-ppt-html/assets/theme-overview-architecture.png +0 -0
- package/top-ppt-html/assets/theme-overview-presentation.png +0 -0
- package/top-ppt-html/assets/theme-overview-research.png +0 -0
- package/top-ppt-html/assets/theme-overview.png +0 -0
- package/top-ppt-html/evals/prompts.csv +15 -15
- package/top-ppt-html/evals/rubric.schema.json +25 -25
- package/top-ppt-html/evals/run_evals.py +220 -220
- package/top-ppt-html/evals/trace.example.json +16 -16
- package/top-ppt-html/package-lock.json +175 -0
- package/top-ppt-html/package.json +30 -35
- package/top-ppt-html/references/charts-basic.md +624 -624
- package/top-ppt-html/references/charts-discipline.md +108 -108
- package/top-ppt-html/references/charts-extended.md +482 -482
- package/top-ppt-html/references/charts.md +28 -28
- package/top-ppt-html/references/components-atoms.md +624 -624
- package/top-ppt-html/references/components.md +30 -30
- package/top-ppt-html/references/content-rules.md +490 -490
- package/top-ppt-html/references/design-system-engine.md +235 -235
- package/top-ppt-html/references/design-system.md +471 -478
- package/top-ppt-html/references/failure-modes.md +214 -214
- package/top-ppt-html/references/high-fidelity.md +127 -127
- package/top-ppt-html/references/icons.md +397 -397
- package/top-ppt-html/references/industry-benchmark.md +105 -105
- package/top-ppt-html/references/infographics-stats.md +308 -308
- package/top-ppt-html/references/infographics-structure.md +226 -226
- package/top-ppt-html/references/infographics.md +43 -43
- package/top-ppt-html/references/layout-grammar.md +315 -315
- package/top-ppt-html/references/layouts-architecture.md +108 -108
- package/top-ppt-html/references/layouts-combo.md +600 -600
- package/top-ppt-html/references/layouts-research.md +160 -160
- package/top-ppt-html/references/modes.md +254 -254
- package/top-ppt-html/references/outline-design.md +275 -275
- package/top-ppt-html/references/playbook.md +266 -266
- package/top-ppt-html/references/pptx-export.md +209 -209
- package/top-ppt-html/references/reform-plan.md +252 -252
- package/top-ppt-html/references/styles.md +336 -370
- package/top-ppt-html/references/tech-design.md +138 -138
- package/top-ppt-html/scripts/audit_css.py +109 -109
- package/top-ppt-html/scripts/audit_docs.py +176 -176
- package/top-ppt-html/scripts/audit_skill.py +220 -220
- package/top-ppt-html/scripts/audit_styles.py +293 -351
- package/top-ppt-html/scripts/build_examples.py +2276 -2276
- package/top-ppt-html/scripts/build_pptx.js +2380 -2380
- package/top-ppt-html/scripts/capture_theme_overview.js +78 -78
- package/top-ppt-html/scripts/checks_html.py +127 -127
- package/top-ppt-html/scripts/cross_verify.py +294 -294
- package/top-ppt-html/scripts/env_probe.py +158 -158
- package/top-ppt-html/scripts/extract_model.py +210 -210
- package/top-ppt-html/scripts/extract_snippet.py +374 -374
- package/top-ppt-html/scripts/gen_channel_a.js +214 -214
- package/top-ppt-html/scripts/layout-constants.json +3309 -3377
- package/top-ppt-html/scripts/layout_slots.json +830 -830
- package/top-ppt-html/scripts/lib_layout_regions.js +412 -412
- package/top-ppt-html/scripts/measure_height.py +178 -178
- package/top-ppt-html/scripts/model-schema.json +547 -547
- package/top-ppt-html/scripts/negative_tests.py +307 -307
- package/top-ppt-html/scripts/package_skill.py +291 -291
- package/top-ppt-html/scripts/prepare_images.py +341 -341
- package/top-ppt-html/scripts/probe_image_export.py +188 -188
- package/top-ppt-html/scripts/quality_gate.py +301 -301
- package/top-ppt-html/scripts/regression.py +307 -308
- package/top-ppt-html/scripts/render_compare.py +275 -275
- package/top-ppt-html/scripts/render_from_model.py +698 -698
- package/top-ppt-html/scripts/scaffold_report.py +1054 -1054
- package/top-ppt-html/scripts/section-file-map.json +104 -104
- package/top-ppt-html/scripts/sync_runtime.py +662 -662
- package/top-ppt-html/scripts/validate_pptx.py +1510 -1510
- package/top-ppt-html/scripts/validate_report.py +1456 -1456
|
@@ -1,138 +1,138 @@
|
|
|
1
|
-
# TopPPT HTML 总体技术方案
|
|
2
|
-
|
|
3
|
-
> 本文是**总体技术方案**(架构 / 双单源 / 双通道 / 门禁 / 深度模式 / 工作流 / 演进)。
|
|
4
|
-
> 生成报告前不必读本文;**改动架构、导出机制、常量或校验体系前必读**。
|
|
5
|
-
|
|
6
|
-
## 一、一句话架构
|
|
7
|
-
|
|
8
|
-
**一套内容模型,两种交付形态,双单源,双通道,按需深度模式。**
|
|
9
|
-
|
|
10
|
-
```
|
|
11
|
-
用户意图 ──六项问询──> 模式 × 风格 × 亮暗主题 × 篇幅 × 交付格式
|
|
12
|
-
│
|
|
13
|
-
▼
|
|
14
|
-
内容架构(outline-design.md)
|
|
15
|
-
│
|
|
16
|
-
▼
|
|
17
|
-
scaffold_report.py 出骨架(data-skel / layoutPreset)
|
|
18
|
-
→ 只填 window.REPORT_MODEL(纯文本,[n] 引用)
|
|
19
|
-
→ render_from_model.py --inplace 回填 HTML(v9 单写路径)
|
|
20
|
-
│
|
|
21
|
-
┌─────────────────┴──────────────────┐
|
|
22
|
-
HTML(阅读/演示) PPTX(精导)
|
|
23
|
-
validate_report --strict extract_model → build_pptx
|
|
24
|
-
LAYOUT_* / qualityGates → validate_pptx --strict
|
|
25
|
-
```
|
|
26
|
-
|
|
27
|
-
## 二、双单源(唯一事实源)
|
|
28
|
-
|
|
29
|
-
| 单源 | 内容 | 消费方 |
|
|
30
|
-
|------|------|--------|
|
|
31
|
-
| `scripts/layout-constants.json` | 页面几何 `page` / **12 列网格 `grid`** / **语义字阶 `typography` C0–T14** / 三模式比例尺 `typeScale`+`modeTypeScale` / 页型几何 `pageTypes`+`pageTypeGeometry` / **9 风格 token `styles`+`stylesDark`+`styleAccents`** / **编码色板 `styleDataColors`+`styleDataColorsDark`(9 套 × 亮暗 × c1–c5)** / **图表登记四元组 `charts.registry`** / 图表最小尺寸 `charts.minSize` / **图表多样性预算 `charts.variety`** / **容器内边距 `containers`** / **锚点容差 `anchorTolerance`** / **图片准入门 `imageAdmission`** / **图片规格 `imageSpec`**(版式 `layouts` / 比例 `ratioDefault`+`ratioCssClass` / 建议尺寸 / 占位标签 / 体积上限) / **深度模式 `deepMode`** / 校验预算 `checkBudgets` / 强调带 `emphasis` / 待核实 `annotations` / 去AI味词 `aiFlavor` / **内容级质量 `contentQuality`**(版式节奏 / so-what 实质 / research 标题判断) | `build_pptx.js`(require)、`pptx-export.js`(注入常量块)、三模板、`style-gallery.html`、`validate_report.py`、`validate_pptx.py`、`audit_styles.py`、`audit_skill.py`、`prepare_images.py`、`evals/run_evals.py` |
|
|
32
|
-
| `scripts/model-schema.json` | 29 种页型的字段与必填约束 / **图表类型白名单 `chartTypes`(30 类:16 原生 + 14 形状)** / **数据表策略 `chartDataTable`** / 通用可选字段 `commonSectionFields` | `extract_model.py`(本地校验)、`pptx-export.js`(浏览器端 `validateModel`) |
|
|
33
|
-
| `scripts/layout_slots.json` | **页型布局 IR(29 页型全量)**:语义槽位(head/primary/secondary/annotation + required)——双通道按同一槽位语义落位 | `sync_runtime.py`(完整性校验);`lib_layout_regions.js`(槽位→英寸矩形);`extract_snippet.py --page-type` |
|
|
34
|
-
| 交付一键 | `quality_gate.py` | `validate_report --strict` + 可选 `validate_pptx` + `evals --score` + **rubric 启发式五维**(content/layout/chart/infographic/tone) |
|
|
35
|
-
|
|
36
|
-
**纪律**:改常量只改 JSON,改 schema 只改 schema JSON,然后跑 `python scripts/sync_runtime.py`(注入 + 完整性校验 + 哈希摘要)。**禁止手改** `pptx-export.js` / 三模板 / `style-gallery.html` 的标记块。
|
|
37
|
-
|
|
38
|
-
**`sync_runtime.py` 的完整性校验**(任一不过即 PASS→WARN):
|
|
39
|
-
1. 双端引用(`build_pptx.js` require 常量;`extract_model.py` 读 schema)
|
|
40
|
-
2. **页型四件套**:每个 schema 页型都有 `pageTypeGeometry` 几何映射
|
|
41
|
-
3. **图表登记四元组**:`charts.types` ↔ `charts.registry` 双向一致;`pptx` 通道合法;非原生图表 `dataTable` 不得为 off(登记表自身声明 off 的装饰微图豁免);`schema.chartTypes` 是 registry 子集
|
|
42
|
-
4. **图表类型可达性不变量**:`registry.types == chartTypes ∪ 信息图专属页型`——每个登记类型都必须能由模型表达(作 `chart.type` 或作 `sections[].type`),否则双引擎实现沦为死代码(该不变量正是为拦住「登记 36 类但模型只能表达 16 类」这类漂移而加);信息图页型清单亦单源于 `charts.scaffold.infoTypes`
|
|
43
|
-
5. **页型实现可达性(冒烟)**:schema 每个页型都应出现在 `build_pptx.js` 与 `pptx-export.js` 源码中(子串级,拦「schema 加了页型、引擎没实现」)
|
|
44
|
-
6. **骨架尺寸单源不变量**:`charts.scaffold`(scaffold_report.py 骨架尺寸唯一事实源)键 ⊆ registry,且默认值 ≥ `charts.minSize` 同口径阈值——拦「脚本硬编码第二源漂移」
|
|
45
|
-
7. **语义字阶**:每个 level 的 `role` 在 `modeTypeScale` 中存在
|
|
46
|
-
8. **页型布局 IR**:`layout_slots.json` 每个页型在 schema 中存在,且含 head + primary 槽位
|
|
47
|
-
|
|
48
|
-
> **图表计数的两种口径**(同一事实的两种切法,勿混用):`registry` 视角 **36 = 16 原生 + 20 形状**(SKILL.md 铁律 9 用此口径);`chartTypes` 视角 **30 = 16 原生 + 14 形状**(可作 `chart.type` 的形状类,`pptx-export.md` 页型表用此口径)——差额 6 类是信息图专属页型(sankey/treemap/boxplot/network/marimekko/streamgraph),它们只作 `sections[].type`,不作 `chart.type`。
|
|
49
|
-
|
|
50
|
-
## 三、双通道(A 预览 / B 交付)
|
|
51
|
-
|
|
52
|
-
| | A 通道(`assets/pptx-export.js`) | B 通道(`scripts/build_pptx.js`) |
|
|
53
|
-
|---|---|---|
|
|
54
|
-
| 角色 | 页面「预览 PPTX」WYSIWYG + 回归双裁判 | **唯一交付通道** |
|
|
55
|
-
| 输出 | 手写 OOXML 形状/文本框/表格(零依赖,约 120KB) | pptxgenjs → 原生形状 + **原生数据图表**(chart part + 内嵌 Excel) |
|
|
56
|
-
| 图表 | 全部类型做**形状近似**(类别标签与数值都落为文本) | 原生通道走 `addChart`;形状通道走高保真形状还原 + 数据表;**已登记类型禁止静默回落**(`SHAPE_RENDERER_MISSING` / 原生调用失败即非 0) |
|
|
57
|
-
| 一致性 | 与 B 通道**逐页文本集合一致**(`cross_verify.py` 用 python-pptx 第三方裁判核对) | 同左 |
|
|
58
|
-
|
|
59
|
-
**为什么手写序列化而不内嵌 pptxgenjs**:pptxgenjs 浏览器版约 700KB,内嵌进每份单文件报告不可接受;手写运行时约 120KB 且与 B 通道消费同一份常量单源。**复用的是引擎与规范,自研的只是轻量序列化轮毂**。
|
|
60
|
-
|
|
61
|
-
**`cross_verify.py` 归一化三件事**(新增图表类型时必须同步扩展,否则误报):
|
|
62
|
-
1. B 端 chart part 的 `plots[0].categories` 并入文本集
|
|
63
|
-
2. `NUMERIC_TOKEN` 纯数值 token(含枚举单位)两端对称过滤
|
|
64
|
-
3. `sorted(set(...))` 集合语义去重
|
|
65
|
-
|
|
66
|
-
## 四、门禁体系(分层)
|
|
67
|
-
|
|
68
|
-
| 层 | 工具 | 通过标准 |
|
|
69
|
-
|----|------|---------|
|
|
70
|
-
| HTML | `validate_report.py` | 全 PASS(模式预算 + 页高溢出估算 + 模型一致性 + 图表数据表策略 + **图表多样性(类型数下限 / 不连续同型)** + **组合版式比例(research)** + **内容级质量(版式节奏连用 / so-what 实质 / research 标题含判断)** + 页型↔版式对应 + 锚点闭环 + 图表登记与最小尺寸 + Exhibit 连续性 + 强调带/待核实/**素材图片与配图占位**(零外链·alt·版式与比例锁定类对应·占位可见标签·内联体积)+ 预览配套断言) |
|
|
71
|
-
| PPTX(交付基线) | `validate_pptx.py --strict --model=` | 0 errors / 0 warnings:模型往返保真 + 按类型原生图表断言 + 数据表落位 + 容器级溢出 + 表格语义字号与密度 + 越界/文本溢出/字号下限/未声明图片 + **图片版式·裁切·多图数量·相对路径文件存在性** |
|
|
72
|
-
| PPTX(深度模式) | 加 `--deep` | 再把**连续文本流**与**空间锚点**纳入 strict |
|
|
73
|
-
| 双裁判 | `cross_verify.py`(python-pptx) | A/B 产物均可严格解析 + 逐页文本一致 + 溢出启发式 |
|
|
74
|
-
| 素材图片 | `probe_image_export.py` | 真实位图 build→strict 0/0 且 `pictures == 声明数`(示例矩阵刻意零图片,真实素材路径/版式/门禁靠它覆盖) |
|
|
75
|
-
| 风格与单源 | `audit_styles.py` | 9 风格 × light/dark × 8 组 WCAG 配对 + engine.css ↔ JSON 双源逐字段一致 + **编码色板 9×2×5(双源 + 对 bg 对比 ≥3.0 + 同套两两可区分)** + ui.js 色板一致 + 单源完整性 |
|
|
76
|
-
| 技能工程 | `audit_skill.py` | 体积预算(SKILL.md ≤13KB / L1 playbook / 单份 reference / 模板)+ description 软硬上限 + **三档披露(L0/L1/L2)** + **Gate 0 位于六项问询之前** + 交互轮次与必读文件预算声明 + 引用完整性 + **内容重复(SKILL.md↔references 归一化滑窗 ≥20 字含 CJK)** |
|
|
77
|
-
| 文档一致性 | `audit_docs.py` | § 引用可解析(含大写后缀)+ 文件前缀规范 + §46 覆盖 36 图表/29 页型 + 图表代码节齐备 + 元数据与必需文件 + **任务路由可解析(TASK_ROUTES 每条实际抽取)** |
|
|
78
|
-
| CSS 覆盖率 | `audit_css.py` | engine.css 选择器类 ↔ 消费方语料(剔除注入镜像);**报告制**(列出疑似死类,不设门禁) |
|
|
79
|
-
| 单源注入 | `sync_runtime.py` | 注入成功 + 完整性校验(①–⑪:双端引用 / 页型四件套 / 图表登记四元组 / 可达性 / 实现冒烟 / **骨架尺寸单源 7d** / 语义字阶 / 布局 IR / **版本一致性**)PASS + 哈希摘要一致 |
|
|
80
|
-
| 反向验证 | `negative_tests.py` | 14 例故障注入(编号/引用/备注/数值/标题/字号/登记/多样性/外链/tbd 图例/主题与模式矛盾/锚点)→ 校验器必须报错 |
|
|
81
|
-
| Eval(四类目标) | `evals/run_evals.py` | 结果(零外链 / strict 0/0 / 模型一致)+ 风格(图表多样性 / 组合版式 / 结构图形)+ 过程(Gate 0)+ 效率(轮次 ≤3 / 工具调用 ≤25 / 读取 ≤40KB);定性部分用 `evals/rubric.schema.json` |
|
|
82
|
-
| 全链路 | `regression.py` | 以上全部 + 9 示例端到端 + **任务路由全量遍历**(与 audit_docs ⑥ 共用 `verify_routes`) |
|
|
83
|
-
|
|
84
|
-
## 五、按需深度模式
|
|
85
|
-
|
|
86
|
-
**触发**:用户明示「高保真 / 1:1 / 精确还原 / 正式交付…」(`deepMode.triggers`)或页面含复杂信息图(`deepMode.complexCharts`)。
|
|
87
|
-
|
|
88
|
-
**多做的三件事**:
|
|
89
|
-
1. `validate_pptx.py --strict --deep` → 连续文本流 + 空间锚点(页头标题对齐版心左边界,容差 `anchorTolerance.keyPx`)纳入 strict
|
|
90
|
-
2. `--emit-manifest=<path>` → 机器可验证的 manifest(锚点注册 / 容器清单 / 数据表清单 / 图表通道清单 / 图片资产登记 / 门禁计数)
|
|
91
|
-
3. `render_compare.py` → 渲染对照材料(PPTX→PDF→位图 + HTML/PPTX 并排对照页 + 自动偏差登记),**缺依赖自动跳过**
|
|
92
|
-
|
|
93
|
-
**明确不引入**:SHA-256 冻结签名、逐页人工验收确认、ImageGen 逐页蓝图、PowerPoint COM 硬门禁(详见 `high-fidelity.md` §八)。
|
|
94
|
-
|
|
95
|
-
## 六、工作流
|
|
96
|
-
|
|
97
|
-
```
|
|
98
|
-
听意图 → Gate 0 先给参考图(theme-overview*.png)
|
|
99
|
-
→ 六项问询一次收集(形式参数:模式/篇幅/风格/主题/交付格式/参考图)
|
|
100
|
-
→ 按路径分流:轻量路径(1 张规划卡)/ 完整路径(内容架构七步法 + 按需大纲确认)
|
|
101
|
-
→ scaffold_report.py 起骨架(锁模式/风格/主题 + 预生成 REPORT_MODEL,不读模板全文)
|
|
102
|
-
→ 照规划卡填内容(每页 = 主件 + 从件 + 注释层)→ 自校验自修复 → 交付
|
|
103
|
-
→ [深度模式] --deep --emit-manifest + render_compare
|
|
104
|
-
```
|
|
105
|
-
|
|
106
|
-
**效率预算(硬)**:交互轮次 ≤3;必读 = `SKILL.md`(L0)+ `references/playbook.md`(L1)共 2 份;L2 深度文件按需读、读完即停。
|
|
107
|
-
**断点可恢复**:`REPORT_MODEL` 即恢复锚点——上下文中断后重跑 `extract_model.py` → `build_pptx.js` 即恢复,无需重新生成内容。
|
|
108
|
-
|
|
109
|
-
## 七、发布态与兼容
|
|
110
|
-
|
|
111
|
-
- **发布版**:**v9.
|
|
112
|
-
- **兼容回落**:排版比例尺只有 `modeTypeScale` 一套;未知模式回落 `presentation` 档。环境变量 `TOP_PPT_NODE_EXE` / `TOP_PPT_NODE_PATH`(旧名已移除)。
|
|
113
|
-
- **文档分工**:本文件管架构与机制;日常规范见 `references/*`,业界对标依据见 `industry-benchmark.md`。
|
|
114
|
-
|
|
115
|
-
## 八、目录职责
|
|
116
|
-
|
|
117
|
-
| 路径 | 职责 |
|
|
118
|
-
|------|------|
|
|
119
|
-
| `SKILL.md` | **L0** 路由与门禁:触发边界、**Gate 0 参考图先行**、三模式速选、六项问询、两条路径、**12 条铁律**、阶段路由(L0/L1/L2)、效率预算 |
|
|
120
|
-
| `references/playbook.md` | **L1 唯一常读入口(决策层)**:三模式契约、两条路径、页型选型、**组合版式矩阵**、**图表选型决策树**、内容规则 Top-12、信息图页型族、配色与主题、校验命令 |
|
|
121
|
-
| `references/modes.md` | 三模式密度契约与锁定版式(L2:定模式细节时读) |
|
|
122
|
-
| `references/outline-design.md` | 内容架构七步法 + 跨页叙事节奏 + 细节保全 |
|
|
123
|
-
| `references/design-system.md` | MD3 对齐、设计原理(CRAP/7:2:1/字体矩阵)、页高模型、12 列网格、语义字阶、SVG 语义类(生成时主读) |
|
|
124
|
-
| `references/design-system-engine.md` | 顶栏/卡片/列表/表格/页脚/动效等 CSS 类实现目录(维护者;生成时勿读) |
|
|
125
|
-
| `references/styles.md` | 9 套风格定义与选型 |
|
|
126
|
-
| `references/content-rules.md` | 页内写作规则、密度三档、表格语义字号、容器内边距、连续文本流 |
|
|
127
|
-
| `references/components.md` | 结构组件与锁定版式(`components.md` §1–§15c、§32–§50)+ 版式选型表 + **组合版式矩阵(§46c)** |
|
|
128
|
-
| `references/charts.md` | 图表全库(`charts.md` §16–§31、§29b、§35、§52–§70)+ **误用反例与多样性纪律(§66)** |
|
|
129
|
-
| `references/infographics.md` | 信息图铁律与边界(逻辑入口);代码在 `infographics-stats.md` / `infographics-structure.md` |
|
|
130
|
-
| `references/icons.md` | 内联 SVG 图标库与使用准则 |
|
|
131
|
-
| `references/pptx-export.md` | PPTX 导出通道、模型字段、双单源、29 页型、图表双通道 |
|
|
132
|
-
| `references/high-fidelity.md` | 按需深度模式规范(触发、容差、manifest、渲染对照) |
|
|
133
|
-
| `references/failure-modes.md` | 十四类失败模式 + 修复顺序铁律 + 错误解释纠正表 |
|
|
134
|
-
| `references/industry-benchmark.md` | 业界对标与采纳/不采纳决策依据 |
|
|
135
|
-
| `scripts/*` | 校验器、**骨架生成器 `scaffold_report.py`**、注入器、回归、**三项审计(styles / docs / skill)**、打包(全部标准库;PPTX 精导需 Node + pptxgenjs) |
|
|
136
|
-
| `evals/*` | Eval 框架:`prompts.csv`(14 条,含负对照)+ `rubric.schema.json`(风格目标评分契约)+ `run_evals.py`(结果/过程/风格/效率四类目标) |
|
|
137
|
-
| `assets/templates/*` | 三模式模板 + 公共引擎/UI(标记块由 `sync_runtime.py` 注入) |
|
|
138
|
-
| `assets/examples/*` | 示例矩阵(含信息图页型与原生图表技巧) |
|
|
1
|
+
# TopPPT HTML 总体技术方案
|
|
2
|
+
|
|
3
|
+
> 本文是**总体技术方案**(架构 / 双单源 / 双通道 / 门禁 / 深度模式 / 工作流 / 演进)。
|
|
4
|
+
> 生成报告前不必读本文;**改动架构、导出机制、常量或校验体系前必读**。
|
|
5
|
+
|
|
6
|
+
## 一、一句话架构
|
|
7
|
+
|
|
8
|
+
**一套内容模型,两种交付形态,双单源,双通道,按需深度模式。**
|
|
9
|
+
|
|
10
|
+
```
|
|
11
|
+
用户意图 ──六项问询──> 模式 × 风格 × 亮暗主题 × 篇幅 × 交付格式
|
|
12
|
+
│
|
|
13
|
+
▼
|
|
14
|
+
内容架构(outline-design.md)
|
|
15
|
+
│
|
|
16
|
+
▼
|
|
17
|
+
scaffold_report.py 出骨架(data-skel / layoutPreset)
|
|
18
|
+
→ 只填 window.REPORT_MODEL(纯文本,[n] 引用)
|
|
19
|
+
→ render_from_model.py --inplace 回填 HTML(v9 单写路径)
|
|
20
|
+
│
|
|
21
|
+
┌─────────────────┴──────────────────┐
|
|
22
|
+
HTML(阅读/演示) PPTX(精导)
|
|
23
|
+
validate_report --strict extract_model → build_pptx
|
|
24
|
+
LAYOUT_* / qualityGates → validate_pptx --strict
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## 二、双单源(唯一事实源)
|
|
28
|
+
|
|
29
|
+
| 单源 | 内容 | 消费方 |
|
|
30
|
+
|------|------|--------|
|
|
31
|
+
| `scripts/layout-constants.json` | 页面几何 `page` / **12 列网格 `grid`** / **语义字阶 `typography` C0–T14** / 三模式比例尺 `typeScale`+`modeTypeScale` / 页型几何 `pageTypes`+`pageTypeGeometry` / **9 风格 token `styles`+`stylesDark`+`styleAccents`** / **编码色板 `styleDataColors`+`styleDataColorsDark`(9 套 × 亮暗 × c1–c5)** / **图表登记四元组 `charts.registry`** / 图表最小尺寸 `charts.minSize` / **图表多样性预算 `charts.variety`** / **容器内边距 `containers`** / **锚点容差 `anchorTolerance`** / **图片准入门 `imageAdmission`** / **图片规格 `imageSpec`**(版式 `layouts` / 比例 `ratioDefault`+`ratioCssClass` / 建议尺寸 / 占位标签 / 体积上限) / **深度模式 `deepMode`** / 校验预算 `checkBudgets` / 强调带 `emphasis` / 待核实 `annotations` / 去AI味词 `aiFlavor` / **内容级质量 `contentQuality`**(版式节奏 / so-what 实质 / research 标题判断) | `build_pptx.js`(require)、`pptx-export.js`(注入常量块)、三模板、`style-gallery.html`、`validate_report.py`、`validate_pptx.py`、`audit_styles.py`、`audit_skill.py`、`prepare_images.py`、`evals/run_evals.py` |
|
|
32
|
+
| `scripts/model-schema.json` | 29 种页型的字段与必填约束 / **图表类型白名单 `chartTypes`(30 类:16 原生 + 14 形状)** / **数据表策略 `chartDataTable`** / 通用可选字段 `commonSectionFields` | `extract_model.py`(本地校验)、`pptx-export.js`(浏览器端 `validateModel`) |
|
|
33
|
+
| `scripts/layout_slots.json` | **页型布局 IR(29 页型全量)**:语义槽位(head/primary/secondary/annotation + required)——双通道按同一槽位语义落位 | `sync_runtime.py`(完整性校验);`lib_layout_regions.js`(槽位→英寸矩形);`extract_snippet.py --page-type` |
|
|
34
|
+
| 交付一键 | `quality_gate.py` | `validate_report --strict` + 可选 `validate_pptx` + `evals --score` + **rubric 启发式五维**(content/layout/chart/infographic/tone) |
|
|
35
|
+
|
|
36
|
+
**纪律**:改常量只改 JSON,改 schema 只改 schema JSON,然后跑 `python scripts/sync_runtime.py`(注入 + 完整性校验 + 哈希摘要)。**禁止手改** `pptx-export.js` / 三模板 / `style-gallery.html` 的标记块。
|
|
37
|
+
|
|
38
|
+
**`sync_runtime.py` 的完整性校验**(任一不过即 PASS→WARN):
|
|
39
|
+
1. 双端引用(`build_pptx.js` require 常量;`extract_model.py` 读 schema)
|
|
40
|
+
2. **页型四件套**:每个 schema 页型都有 `pageTypeGeometry` 几何映射
|
|
41
|
+
3. **图表登记四元组**:`charts.types` ↔ `charts.registry` 双向一致;`pptx` 通道合法;非原生图表 `dataTable` 不得为 off(登记表自身声明 off 的装饰微图豁免);`schema.chartTypes` 是 registry 子集
|
|
42
|
+
4. **图表类型可达性不变量**:`registry.types == chartTypes ∪ 信息图专属页型`——每个登记类型都必须能由模型表达(作 `chart.type` 或作 `sections[].type`),否则双引擎实现沦为死代码(该不变量正是为拦住「登记 36 类但模型只能表达 16 类」这类漂移而加);信息图页型清单亦单源于 `charts.scaffold.infoTypes`
|
|
43
|
+
5. **页型实现可达性(冒烟)**:schema 每个页型都应出现在 `build_pptx.js` 与 `pptx-export.js` 源码中(子串级,拦「schema 加了页型、引擎没实现」)
|
|
44
|
+
6. **骨架尺寸单源不变量**:`charts.scaffold`(scaffold_report.py 骨架尺寸唯一事实源)键 ⊆ registry,且默认值 ≥ `charts.minSize` 同口径阈值——拦「脚本硬编码第二源漂移」
|
|
45
|
+
7. **语义字阶**:每个 level 的 `role` 在 `modeTypeScale` 中存在
|
|
46
|
+
8. **页型布局 IR**:`layout_slots.json` 每个页型在 schema 中存在,且含 head + primary 槽位
|
|
47
|
+
|
|
48
|
+
> **图表计数的两种口径**(同一事实的两种切法,勿混用):`registry` 视角 **36 = 16 原生 + 20 形状**(SKILL.md 铁律 9 用此口径);`chartTypes` 视角 **30 = 16 原生 + 14 形状**(可作 `chart.type` 的形状类,`pptx-export.md` 页型表用此口径)——差额 6 类是信息图专属页型(sankey/treemap/boxplot/network/marimekko/streamgraph),它们只作 `sections[].type`,不作 `chart.type`。
|
|
49
|
+
|
|
50
|
+
## 三、双通道(A 预览 / B 交付)
|
|
51
|
+
|
|
52
|
+
| | A 通道(`assets/pptx-export.js`) | B 通道(`scripts/build_pptx.js`) |
|
|
53
|
+
|---|---|---|
|
|
54
|
+
| 角色 | 页面「预览 PPTX」WYSIWYG + 回归双裁判 | **唯一交付通道** |
|
|
55
|
+
| 输出 | 手写 OOXML 形状/文本框/表格(零依赖,约 120KB) | pptxgenjs → 原生形状 + **原生数据图表**(chart part + 内嵌 Excel) |
|
|
56
|
+
| 图表 | 全部类型做**形状近似**(类别标签与数值都落为文本) | 原生通道走 `addChart`;形状通道走高保真形状还原 + 数据表;**已登记类型禁止静默回落**(`SHAPE_RENDERER_MISSING` / 原生调用失败即非 0) |
|
|
57
|
+
| 一致性 | 与 B 通道**逐页文本集合一致**(`cross_verify.py` 用 python-pptx 第三方裁判核对) | 同左 |
|
|
58
|
+
|
|
59
|
+
**为什么手写序列化而不内嵌 pptxgenjs**:pptxgenjs 浏览器版约 700KB,内嵌进每份单文件报告不可接受;手写运行时约 120KB 且与 B 通道消费同一份常量单源。**复用的是引擎与规范,自研的只是轻量序列化轮毂**。
|
|
60
|
+
|
|
61
|
+
**`cross_verify.py` 归一化三件事**(新增图表类型时必须同步扩展,否则误报):
|
|
62
|
+
1. B 端 chart part 的 `plots[0].categories` 并入文本集
|
|
63
|
+
2. `NUMERIC_TOKEN` 纯数值 token(含枚举单位)两端对称过滤
|
|
64
|
+
3. `sorted(set(...))` 集合语义去重
|
|
65
|
+
|
|
66
|
+
## 四、门禁体系(分层)
|
|
67
|
+
|
|
68
|
+
| 层 | 工具 | 通过标准 |
|
|
69
|
+
|----|------|---------|
|
|
70
|
+
| HTML | `validate_report.py` | 全 PASS(模式预算 + 页高溢出估算 + 模型一致性 + 图表数据表策略 + **图表多样性(类型数下限 / 不连续同型)** + **组合版式比例(research)** + **内容级质量(版式节奏连用 / so-what 实质 / research 标题含判断)** + 页型↔版式对应 + 锚点闭环 + 图表登记与最小尺寸 + Exhibit 连续性 + 强调带/待核实/**素材图片与配图占位**(零外链·alt·版式与比例锁定类对应·占位可见标签·内联体积)+ 预览配套断言) |
|
|
71
|
+
| PPTX(交付基线) | `validate_pptx.py --strict --model=` | 0 errors / 0 warnings:模型往返保真 + 按类型原生图表断言 + 数据表落位 + 容器级溢出 + 表格语义字号与密度 + 越界/文本溢出/字号下限/未声明图片 + **图片版式·裁切·多图数量·相对路径文件存在性** |
|
|
72
|
+
| PPTX(深度模式) | 加 `--deep` | 再把**连续文本流**与**空间锚点**纳入 strict |
|
|
73
|
+
| 双裁判 | `cross_verify.py`(python-pptx) | A/B 产物均可严格解析 + 逐页文本一致 + 溢出启发式 |
|
|
74
|
+
| 素材图片 | `probe_image_export.py` | 真实位图 build→strict 0/0 且 `pictures == 声明数`(示例矩阵刻意零图片,真实素材路径/版式/门禁靠它覆盖) |
|
|
75
|
+
| 风格与单源 | `audit_styles.py` | 9 风格 × light/dark × 8 组 WCAG 配对 + engine.css ↔ JSON 双源逐字段一致 + **编码色板 9×2×5(双源 + 对 bg 对比 ≥3.0 + 同套两两可区分)** + ui.js 色板一致 + 单源完整性 |
|
|
76
|
+
| 技能工程 | `audit_skill.py` | 体积预算(SKILL.md ≤13KB / L1 playbook / 单份 reference / 模板)+ description 软硬上限 + **三档披露(L0/L1/L2)** + **Gate 0 位于六项问询之前** + 交互轮次与必读文件预算声明 + 引用完整性 + **内容重复(SKILL.md↔references 归一化滑窗 ≥20 字含 CJK)** |
|
|
77
|
+
| 文档一致性 | `audit_docs.py` | § 引用可解析(含大写后缀)+ 文件前缀规范 + §46 覆盖 36 图表/29 页型 + 图表代码节齐备 + 元数据与必需文件 + **任务路由可解析(TASK_ROUTES 每条实际抽取)** |
|
|
78
|
+
| CSS 覆盖率 | `audit_css.py` | engine.css 选择器类 ↔ 消费方语料(剔除注入镜像);**报告制**(列出疑似死类,不设门禁) |
|
|
79
|
+
| 单源注入 | `sync_runtime.py` | 注入成功 + 完整性校验(①–⑪:双端引用 / 页型四件套 / 图表登记四元组 / 可达性 / 实现冒烟 / **骨架尺寸单源 7d** / 语义字阶 / 布局 IR / **版本一致性**)PASS + 哈希摘要一致 |
|
|
80
|
+
| 反向验证 | `negative_tests.py` | 14 例故障注入(编号/引用/备注/数值/标题/字号/登记/多样性/外链/tbd 图例/主题与模式矛盾/锚点)→ 校验器必须报错 |
|
|
81
|
+
| Eval(四类目标) | `evals/run_evals.py` | 结果(零外链 / strict 0/0 / 模型一致)+ 风格(图表多样性 / 组合版式 / 结构图形)+ 过程(Gate 0)+ 效率(轮次 ≤3 / 工具调用 ≤25 / 读取 ≤40KB);定性部分用 `evals/rubric.schema.json` |
|
|
82
|
+
| 全链路 | `regression.py` | 以上全部 + 9 示例端到端 + **任务路由全量遍历**(与 audit_docs ⑥ 共用 `verify_routes`) |
|
|
83
|
+
|
|
84
|
+
## 五、按需深度模式
|
|
85
|
+
|
|
86
|
+
**触发**:用户明示「高保真 / 1:1 / 精确还原 / 正式交付…」(`deepMode.triggers`)或页面含复杂信息图(`deepMode.complexCharts`)。
|
|
87
|
+
|
|
88
|
+
**多做的三件事**:
|
|
89
|
+
1. `validate_pptx.py --strict --deep` → 连续文本流 + 空间锚点(页头标题对齐版心左边界,容差 `anchorTolerance.keyPx`)纳入 strict
|
|
90
|
+
2. `--emit-manifest=<path>` → 机器可验证的 manifest(锚点注册 / 容器清单 / 数据表清单 / 图表通道清单 / 图片资产登记 / 门禁计数)
|
|
91
|
+
3. `render_compare.py` → 渲染对照材料(PPTX→PDF→位图 + HTML/PPTX 并排对照页 + 自动偏差登记),**缺依赖自动跳过**
|
|
92
|
+
|
|
93
|
+
**明确不引入**:SHA-256 冻结签名、逐页人工验收确认、ImageGen 逐页蓝图、PowerPoint COM 硬门禁(详见 `high-fidelity.md` §八)。
|
|
94
|
+
|
|
95
|
+
## 六、工作流
|
|
96
|
+
|
|
97
|
+
```
|
|
98
|
+
听意图 → Gate 0 先给参考图(theme-overview*.png)
|
|
99
|
+
→ 六项问询一次收集(形式参数:模式/篇幅/风格/主题/交付格式/参考图)
|
|
100
|
+
→ 按路径分流:轻量路径(1 张规划卡)/ 完整路径(内容架构七步法 + 按需大纲确认)
|
|
101
|
+
→ scaffold_report.py 起骨架(锁模式/风格/主题 + 预生成 REPORT_MODEL,不读模板全文)
|
|
102
|
+
→ 照规划卡填内容(每页 = 主件 + 从件 + 注释层)→ 自校验自修复 → 交付
|
|
103
|
+
→ [深度模式] --deep --emit-manifest + render_compare
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
**效率预算(硬)**:交互轮次 ≤3;必读 = `SKILL.md`(L0)+ `references/playbook.md`(L1)共 2 份;L2 深度文件按需读、读完即停。
|
|
107
|
+
**断点可恢复**:`REPORT_MODEL` 即恢复锚点——上下文中断后重跑 `extract_model.py` → `build_pptx.js` 即恢复,无需重新生成内容。
|
|
108
|
+
|
|
109
|
+
## 七、发布态与兼容
|
|
110
|
+
|
|
111
|
+
- **发布版**:**v9.0**(布局语法 + 模型单写 + 硬门禁)。**版本唯一事实源 = `layout-constants.json` `version`**;`model-schema.json` / `layoutSlots` 同步该值、技能 `package.json` 为 `9.0.0`;`sync_runtime.py` 校验一致。变更流水见仓库根 `CHANGELOG.md`。
|
|
112
|
+
- **兼容回落**:排版比例尺只有 `modeTypeScale` 一套;未知模式回落 `presentation` 档。环境变量 `TOP_PPT_NODE_EXE` / `TOP_PPT_NODE_PATH`(旧名已移除)。
|
|
113
|
+
- **文档分工**:本文件管架构与机制;日常规范见 `references/*`,业界对标依据见 `industry-benchmark.md`。
|
|
114
|
+
|
|
115
|
+
## 八、目录职责
|
|
116
|
+
|
|
117
|
+
| 路径 | 职责 |
|
|
118
|
+
|------|------|
|
|
119
|
+
| `SKILL.md` | **L0** 路由与门禁:触发边界、**Gate 0 参考图先行**、三模式速选、六项问询、两条路径、**12 条铁律**、阶段路由(L0/L1/L2)、效率预算 |
|
|
120
|
+
| `references/playbook.md` | **L1 唯一常读入口(决策层)**:三模式契约、两条路径、页型选型、**组合版式矩阵**、**图表选型决策树**、内容规则 Top-12、信息图页型族、配色与主题、校验命令 |
|
|
121
|
+
| `references/modes.md` | 三模式密度契约与锁定版式(L2:定模式细节时读) |
|
|
122
|
+
| `references/outline-design.md` | 内容架构七步法 + 跨页叙事节奏 + 细节保全 |
|
|
123
|
+
| `references/design-system.md` | MD3 对齐、设计原理(CRAP/7:2:1/字体矩阵)、页高模型、12 列网格、语义字阶、SVG 语义类(生成时主读) |
|
|
124
|
+
| `references/design-system-engine.md` | 顶栏/卡片/列表/表格/页脚/动效等 CSS 类实现目录(维护者;生成时勿读) |
|
|
125
|
+
| `references/styles.md` | 9 套风格定义与选型 |
|
|
126
|
+
| `references/content-rules.md` | 页内写作规则、密度三档、表格语义字号、容器内边距、连续文本流 |
|
|
127
|
+
| `references/components.md` | 结构组件与锁定版式(`components.md` §1–§15c、§32–§50)+ 版式选型表 + **组合版式矩阵(§46c)** |
|
|
128
|
+
| `references/charts.md` | 图表全库(`charts.md` §16–§31、§29b、§35、§52–§70)+ **误用反例与多样性纪律(§66)** |
|
|
129
|
+
| `references/infographics.md` | 信息图铁律与边界(逻辑入口);代码在 `infographics-stats.md` / `infographics-structure.md` |
|
|
130
|
+
| `references/icons.md` | 内联 SVG 图标库与使用准则 |
|
|
131
|
+
| `references/pptx-export.md` | PPTX 导出通道、模型字段、双单源、29 页型、图表双通道 |
|
|
132
|
+
| `references/high-fidelity.md` | 按需深度模式规范(触发、容差、manifest、渲染对照) |
|
|
133
|
+
| `references/failure-modes.md` | 十四类失败模式 + 修复顺序铁律 + 错误解释纠正表 |
|
|
134
|
+
| `references/industry-benchmark.md` | 业界对标与采纳/不采纳决策依据 |
|
|
135
|
+
| `scripts/*` | 校验器、**骨架生成器 `scaffold_report.py`**、注入器、回归、**三项审计(styles / docs / skill)**、打包(全部标准库;PPTX 精导需 Node + pptxgenjs) |
|
|
136
|
+
| `evals/*` | Eval 框架:`prompts.csv`(14 条,含负对照)+ `rubric.schema.json`(风格目标评分契约)+ `run_evals.py`(结果/过程/风格/效率四类目标) |
|
|
137
|
+
| `assets/templates/*` | 三模式模板 + 公共引擎/UI(标记块由 `sync_runtime.py` 注入) |
|
|
138
|
+
| `assets/examples/*` | 示例矩阵(含信息图页型与原生图表技巧) |
|
|
@@ -1,109 +1,109 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
# -*- coding: utf-8 -*-
|
|
3
|
-
"""TopPPT HTML · CSS 类覆盖率审计(engine.css ↔ 消费方语料 · 零依赖 · 报告制)
|
|
4
|
-
|
|
5
|
-
用法:
|
|
6
|
-
python scripts/audit_css.py # 输出未引用类清单与占比(仅报告,不影响退出码)
|
|
7
|
-
python scripts/audit_css.py --json # 机器可读
|
|
8
|
-
|
|
9
|
-
为什么需要它:
|
|
10
|
-
engine.css(≈53KB)是三模板与全部交付报告的样式单源。新增版式/组件后旧类
|
|
11
|
-
可能不再被任何模板、示例或运行时引用——这类「视觉债」此前无任何工具度量。
|
|
12
|
-
本脚本做**报告制**审计(不设门禁):列出疑似未引用类,供维护者决策
|
|
13
|
-
「删除 / 保留(如为未来预留)/ 标注豁免」。
|
|
14
|
-
|
|
15
|
-
口径(刻意宽松,宁可漏报不可误报):
|
|
16
|
-
· 类「已使用」= 类名以子串形式出现在任一消费方文本中(模板/示例/运行时 JS/画廊/
|
|
17
|
-
build_examples 内容包)——JS 动态拼接的类名片段也能命中
|
|
18
|
-
· engine.css 侧只统计**选择器位置的类**(块内属性值如 `.5em` 不算;类名以字母开头)
|
|
19
|
-
· 明确豁免:engine.css 自身注释中提到的类、以 `--` 开头的 CSS 变量名(非类)
|
|
20
|
-
|
|
21
|
-
何时跑:删除/新增 engine.css 规则、清理模板示例之后(与 audit_styles 互补——
|
|
22
|
-
后者管 token 双源一致性,本工具管选择器死活)。
|
|
23
|
-
"""
|
|
24
|
-
from __future__ import annotations
|
|
25
|
-
|
|
26
|
-
import json
|
|
27
|
-
import re
|
|
28
|
-
import sys
|
|
29
|
-
from pathlib import Path
|
|
30
|
-
|
|
31
|
-
try:
|
|
32
|
-
sys.stdout.reconfigure(encoding='utf-8', errors='replace')
|
|
33
|
-
except Exception:
|
|
34
|
-
pass
|
|
35
|
-
|
|
36
|
-
ROOT = Path(__file__).resolve().parent.parent
|
|
37
|
-
ENGINE = ROOT / 'assets' / 'templates' / 'engine.css'
|
|
38
|
-
|
|
39
|
-
# 消费方语料(模板含 engine.css 注入副本,组装时剔除 __TOPPPT_ENGINE__ 镜像块防自命中)
|
|
40
|
-
# references/*.md 计入消费方:其中的代码配方是交付报告类名的合法来源(智能体照文档写报告)
|
|
41
|
-
CONSUMERS = (
|
|
42
|
-
list((ROOT / 'assets' / 'templates').glob('*.html'))
|
|
43
|
-
+ list((ROOT / 'assets' / 'examples').glob('*.html'))
|
|
44
|
-
+ [ROOT / 'assets' / 'pptx-export.js', ROOT / 'assets' / 'style-gallery.html',
|
|
45
|
-
ROOT / 'assets' / 'templates' / 'ui.js']
|
|
46
|
-
+ [ROOT / 'scripts' / 'build_examples.py', ROOT / 'scripts' / 'build_pptx.js',
|
|
47
|
-
ROOT / 'scripts' / 'scaffold_report.py']
|
|
48
|
-
+ sorted((ROOT / 'references').glob('*.md'))
|
|
49
|
-
)
|
|
50
|
-
|
|
51
|
-
SELECTOR_CLASS = re.compile(r'\.([a-zA-Z][-\w]*)')
|
|
52
|
-
# 模板/示例内嵌的 engine.css 注入镜像块——语料必须剔除,否则所有类名自命中、审计恒空
|
|
53
|
-
_ENGINE_MIRROR = re.compile(r'/\* __TOPPPT_ENGINE_START__ \*/[\s\S]*?/\* __TOPPPT_ENGINE_END__ \*/')
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
def strip_engine_mirror(text: str) -> str:
|
|
57
|
-
return _ENGINE_MIRROR.sub('', text)
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
def strip_blocks(css: str) -> str:
|
|
61
|
-
"""去掉 {...} 声明块,只留选择器区(注释一并去掉;块替换为空格防相邻选择器拼接)。"""
|
|
62
|
-
css = re.sub(r'/\*[\s\S]*?\*/', '', css)
|
|
63
|
-
# 循环剥嵌套块(@media 外层块的声明块在首轮被替换后,外层变空壳再剥一次)
|
|
64
|
-
for _ in range(3):
|
|
65
|
-
new = re.sub(r'\{[^{}]*\}', ' ', css)
|
|
66
|
-
if new == css:
|
|
67
|
-
break
|
|
68
|
-
css = new
|
|
69
|
-
return css
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
def main() -> int:
|
|
73
|
-
if not ENGINE.exists():
|
|
74
|
-
print('错误: 缺 assets/templates/engine.css')
|
|
75
|
-
return 2
|
|
76
|
-
|
|
77
|
-
selector_zone = strip_blocks(ENGINE.read_text(encoding='utf-8'))
|
|
78
|
-
classes: list[str] = sorted(set(SELECTOR_CLASS.findall(selector_zone)))
|
|
79
|
-
|
|
80
|
-
corpus = '\n'.join(
|
|
81
|
-
strip_engine_mirror(p.read_text(encoding='utf-8'))
|
|
82
|
-
for p in CONSUMERS if p and p.exists())
|
|
83
|
-
|
|
84
|
-
unused = [c for c in classes if c not in corpus]
|
|
85
|
-
used = len(classes) - len(unused)
|
|
86
|
-
|
|
87
|
-
if '--json' in sys.argv[1:]:
|
|
88
|
-
print(json.dumps({'total': len(classes), 'used': used,
|
|
89
|
-
'unused': unused,
|
|
90
|
-
'coverage': round(used / len(classes), 4) if classes else 1.0},
|
|
91
|
-
ensure_ascii=False, indent=2))
|
|
92
|
-
return 0
|
|
93
|
-
|
|
94
|
-
print('CSS 类覆盖率审计 · engine.css(报告制 · 不设门禁)')
|
|
95
|
-
print('-' * 56)
|
|
96
|
-
print(f' 选择器类总数:{len(classes)} · 被消费方引用:{used}'
|
|
97
|
-
f'(覆盖率 {used / max(1, len(classes)):.0%})')
|
|
98
|
-
if unused:
|
|
99
|
-
print(f' 疑似未引用({len(unused)} 个,请逐个判断 删除/保留/豁免):')
|
|
100
|
-
for c in unused:
|
|
101
|
-
print(f' · .{c}')
|
|
102
|
-
print(' 注意:JS 变量拼接类名的极端形态可能漏判,删除前先全局搜一遍。')
|
|
103
|
-
else:
|
|
104
|
-
print(' 未发现未引用类。')
|
|
105
|
-
return 0
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
if __name__ == '__main__':
|
|
109
|
-
sys.exit(main())
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""TopPPT HTML · CSS 类覆盖率审计(engine.css ↔ 消费方语料 · 零依赖 · 报告制)
|
|
4
|
+
|
|
5
|
+
用法:
|
|
6
|
+
python scripts/audit_css.py # 输出未引用类清单与占比(仅报告,不影响退出码)
|
|
7
|
+
python scripts/audit_css.py --json # 机器可读
|
|
8
|
+
|
|
9
|
+
为什么需要它:
|
|
10
|
+
engine.css(≈53KB)是三模板与全部交付报告的样式单源。新增版式/组件后旧类
|
|
11
|
+
可能不再被任何模板、示例或运行时引用——这类「视觉债」此前无任何工具度量。
|
|
12
|
+
本脚本做**报告制**审计(不设门禁):列出疑似未引用类,供维护者决策
|
|
13
|
+
「删除 / 保留(如为未来预留)/ 标注豁免」。
|
|
14
|
+
|
|
15
|
+
口径(刻意宽松,宁可漏报不可误报):
|
|
16
|
+
· 类「已使用」= 类名以子串形式出现在任一消费方文本中(模板/示例/运行时 JS/画廊/
|
|
17
|
+
build_examples 内容包)——JS 动态拼接的类名片段也能命中
|
|
18
|
+
· engine.css 侧只统计**选择器位置的类**(块内属性值如 `.5em` 不算;类名以字母开头)
|
|
19
|
+
· 明确豁免:engine.css 自身注释中提到的类、以 `--` 开头的 CSS 变量名(非类)
|
|
20
|
+
|
|
21
|
+
何时跑:删除/新增 engine.css 规则、清理模板示例之后(与 audit_styles 互补——
|
|
22
|
+
后者管 token 双源一致性,本工具管选择器死活)。
|
|
23
|
+
"""
|
|
24
|
+
from __future__ import annotations
|
|
25
|
+
|
|
26
|
+
import json
|
|
27
|
+
import re
|
|
28
|
+
import sys
|
|
29
|
+
from pathlib import Path
|
|
30
|
+
|
|
31
|
+
try:
|
|
32
|
+
sys.stdout.reconfigure(encoding='utf-8', errors='replace')
|
|
33
|
+
except Exception:
|
|
34
|
+
pass
|
|
35
|
+
|
|
36
|
+
ROOT = Path(__file__).resolve().parent.parent
|
|
37
|
+
ENGINE = ROOT / 'assets' / 'templates' / 'engine.css'
|
|
38
|
+
|
|
39
|
+
# 消费方语料(模板含 engine.css 注入副本,组装时剔除 __TOPPPT_ENGINE__ 镜像块防自命中)
|
|
40
|
+
# references/*.md 计入消费方:其中的代码配方是交付报告类名的合法来源(智能体照文档写报告)
|
|
41
|
+
CONSUMERS = (
|
|
42
|
+
list((ROOT / 'assets' / 'templates').glob('*.html'))
|
|
43
|
+
+ list((ROOT / 'assets' / 'examples').glob('*.html'))
|
|
44
|
+
+ [ROOT / 'assets' / 'pptx-export.js', ROOT / 'assets' / 'style-gallery.html',
|
|
45
|
+
ROOT / 'assets' / 'templates' / 'ui.js']
|
|
46
|
+
+ [ROOT / 'scripts' / 'build_examples.py', ROOT / 'scripts' / 'build_pptx.js',
|
|
47
|
+
ROOT / 'scripts' / 'scaffold_report.py']
|
|
48
|
+
+ sorted((ROOT / 'references').glob('*.md'))
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
SELECTOR_CLASS = re.compile(r'\.([a-zA-Z][-\w]*)')
|
|
52
|
+
# 模板/示例内嵌的 engine.css 注入镜像块——语料必须剔除,否则所有类名自命中、审计恒空
|
|
53
|
+
_ENGINE_MIRROR = re.compile(r'/\* __TOPPPT_ENGINE_START__ \*/[\s\S]*?/\* __TOPPPT_ENGINE_END__ \*/')
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def strip_engine_mirror(text: str) -> str:
|
|
57
|
+
return _ENGINE_MIRROR.sub('', text)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def strip_blocks(css: str) -> str:
|
|
61
|
+
"""去掉 {...} 声明块,只留选择器区(注释一并去掉;块替换为空格防相邻选择器拼接)。"""
|
|
62
|
+
css = re.sub(r'/\*[\s\S]*?\*/', '', css)
|
|
63
|
+
# 循环剥嵌套块(@media 外层块的声明块在首轮被替换后,外层变空壳再剥一次)
|
|
64
|
+
for _ in range(3):
|
|
65
|
+
new = re.sub(r'\{[^{}]*\}', ' ', css)
|
|
66
|
+
if new == css:
|
|
67
|
+
break
|
|
68
|
+
css = new
|
|
69
|
+
return css
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def main() -> int:
|
|
73
|
+
if not ENGINE.exists():
|
|
74
|
+
print('错误: 缺 assets/templates/engine.css')
|
|
75
|
+
return 2
|
|
76
|
+
|
|
77
|
+
selector_zone = strip_blocks(ENGINE.read_text(encoding='utf-8'))
|
|
78
|
+
classes: list[str] = sorted(set(SELECTOR_CLASS.findall(selector_zone)))
|
|
79
|
+
|
|
80
|
+
corpus = '\n'.join(
|
|
81
|
+
strip_engine_mirror(p.read_text(encoding='utf-8'))
|
|
82
|
+
for p in CONSUMERS if p and p.exists())
|
|
83
|
+
|
|
84
|
+
unused = [c for c in classes if c not in corpus]
|
|
85
|
+
used = len(classes) - len(unused)
|
|
86
|
+
|
|
87
|
+
if '--json' in sys.argv[1:]:
|
|
88
|
+
print(json.dumps({'total': len(classes), 'used': used,
|
|
89
|
+
'unused': unused,
|
|
90
|
+
'coverage': round(used / len(classes), 4) if classes else 1.0},
|
|
91
|
+
ensure_ascii=False, indent=2))
|
|
92
|
+
return 0
|
|
93
|
+
|
|
94
|
+
print('CSS 类覆盖率审计 · engine.css(报告制 · 不设门禁)')
|
|
95
|
+
print('-' * 56)
|
|
96
|
+
print(f' 选择器类总数:{len(classes)} · 被消费方引用:{used}'
|
|
97
|
+
f'(覆盖率 {used / max(1, len(classes)):.0%})')
|
|
98
|
+
if unused:
|
|
99
|
+
print(f' 疑似未引用({len(unused)} 个,请逐个判断 删除/保留/豁免):')
|
|
100
|
+
for c in unused:
|
|
101
|
+
print(f' · .{c}')
|
|
102
|
+
print(' 注意:JS 变量拼接类名的极端形态可能漏判,删除前先全局搜一遍。')
|
|
103
|
+
else:
|
|
104
|
+
print(' 未发现未引用类。')
|
|
105
|
+
return 0
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
if __name__ == '__main__':
|
|
109
|
+
sys.exit(main())
|