@topmindspace/tms-skills 0.1.17 → 0.1.18
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 +39 -0
- package/README.en.md +3 -3
- package/README.md +3 -3
- package/package.json +1 -1
- package/top-ppt-html/README.en.md +2 -2
- package/top-ppt-html/README.md +1 -1
- package/top-ppt-html/SKILL.md +2 -2
- package/top-ppt-html/assets/examples/2026-09-09-architecture-graphite-dark.html +122 -48
- package/top-ppt-html/assets/examples/2026-09-09-presentation-business-blue.html +122 -48
- package/top-ppt-html/assets/examples/2026-09-09-research-mckinsey.html +122 -48
- package/top-ppt-html/assets/examples/2026-09-26-topmind-tms-skills-showcase.html +126 -52
- package/top-ppt-html/assets/examples/2026-09-26-topmind-tms-skills-showcase.model.json +3 -3
- package/top-ppt-html/assets/pptx-export.js +118 -45
- package/top-ppt-html/assets/style-gallery.html +19 -3
- package/top-ppt-html/assets/templates/architecture.html +122 -48
- package/top-ppt-html/assets/templates/engine.css +3 -2
- package/top-ppt-html/assets/templates/presentation.html +122 -48
- package/top-ppt-html/assets/templates/research.html +122 -48
- package/top-ppt-html/package-lock.json +2 -2
- package/top-ppt-html/package.json +1 -1
- package/top-ppt-html/references/chart-decision-tree.md +26 -25
- package/top-ppt-html/references/components-atoms.md +5 -4
- package/top-ppt-html/references/content-rules.md +3 -1
- package/top-ppt-html/references/failure-modes.md +29 -3
- package/top-ppt-html/references/layouts-combo.md +1 -1
- package/top-ppt-html/references/modes.md +1 -1
- package/top-ppt-html/references/page-type-matrix.md +10 -6
- package/top-ppt-html/references/pptx-export.md +3 -3
- package/top-ppt-html/scripts/build_pptx.js +101 -42
- package/top-ppt-html/scripts/cross_verify.py +84 -1
- package/top-ppt-html/scripts/extract_model.py +5 -0
- package/top-ppt-html/scripts/layout-constants.json +42 -2
- package/top-ppt-html/scripts/lib_layout_regions.js +6 -3
- package/top-ppt-html/scripts/model-schema.json +1 -0
- package/top-ppt-html/scripts/render_from_model.py +133 -17
- package/top-ppt-html/scripts/sync_runtime.py +19 -3
- package/top-ppt-html/scripts/test_feedback_gates.py +73 -0
- package/top-ppt-html/scripts/validate_pptx.py +148 -0
- package/top-ppt-html/scripts/validate_report.py +24 -0
|
@@ -818,8 +818,9 @@ color:var(--accent);font-variant-numeric:tabular-nums;line-height:1;min-width:2.
|
|
|
818
818
|
.agenda__a:hover .agenda__go{color:var(--accent);transform:translateX(4px)}
|
|
819
819
|
.agenda__go svg{width:20px;height:20px;display:block}
|
|
820
820
|
@media (min-width:900px){
|
|
821
|
-
|
|
822
|
-
.agenda--2col
|
|
821
|
+
/* R4:列优先(先左列后右列),与 PPTX Agenda 列填充一致——grid 行优先会打乱阅读顺序 */
|
|
822
|
+
.agenda--2col{display:block;column-count:2;column-gap:clamp(20px,3vw,48px)}
|
|
823
|
+
.agenda--2col .agenda__i{break-inside:avoid;border-top:1px solid var(--border-soft)}}
|
|
823
824
|
/* ── 页码指示(翻页导航) ── */
|
|
824
825
|
.pager{position:fixed;right:clamp(10px,1.6vw,26px);top:50%;transform:translateY(-50%);
|
|
825
826
|
z-index:60;display:flex;flex-direction:column;align-items:center;gap:10px}
|
|
@@ -2078,7 +2079,7 @@ window.REPORT_MODEL = {
|
|
|
2078
2079
|
|
|
2079
2080
|
/* __TOPPPT_RUNTIME_START__ */
|
|
2080
2081
|
/* ══ PPTX 导出运行时(assets/pptx-export.js 的内联副本 · 由 scripts/sync_runtime.py 注入,禁止手改) ══ */
|
|
2081
|
-
/* __TOPPPT_RUNTIME_SHA__:
|
|
2082
|
+
/* __TOPPPT_RUNTIME_SHA__:73cff2ca1dbc78a8 */
|
|
2082
2083
|
/* ═══════════════════════════════════════════════════════════════════════════
|
|
2083
2084
|
* TopPPT HTML· 浏览器端 PPTX 预览运行时
|
|
2084
2085
|
* 零依赖、随报告单文件分发;OOXML 序列化器(仅供预览,页面不导出文件)。
|
|
@@ -2237,6 +2238,15 @@ function regionOf(pageType, slotId, opts) {
|
|
|
2237
2238
|
return { x: MX, y: P.heroY || 2.9, w: CW * (P.heroW || 0.52), h: P.heroH || 1.6,
|
|
2238
2239
|
dividerX: P.dividerX, metricY0: P.metricY0, metricRowH: P.metricRowH };
|
|
2239
2240
|
}
|
|
2241
|
+
/* R3:kpi 支撑指标列——落在 dividerX 右侧(默认全宽返回会压住 hero) */
|
|
2242
|
+
if (type === 'kpi' && slot === 'metrics') {
|
|
2243
|
+
var dxK = P.dividerX != null ? P.dividerX : 0.56;
|
|
2244
|
+
return {
|
|
2245
|
+
x: MX + CW * dxK + 0.35, y: P.metricY0 || 2.95,
|
|
2246
|
+
w: CW * (1 - dxK) - 0.5,
|
|
2247
|
+
h: Math.max(0.6, bottomDefault - (P.metricY0 || 2.95)),
|
|
2248
|
+
};
|
|
2249
|
+
}
|
|
2240
2250
|
if (type === 'comparison' && (slot === 'left' || slot === 'primary' || slot === 'right')) {
|
|
2241
2251
|
var cg = P.panelGap || 0.4, pw = (CW - cg) / 2;
|
|
2242
2252
|
if (slot === 'right') return { x: MX + pw + cg, y: P.panelY || 2.55, w: pw, h: P.panelH || 3.4 };
|
|
@@ -2269,15 +2279,22 @@ function regionOf(pageType, slotId, opts) {
|
|
|
2269
2279
|
}
|
|
2270
2280
|
if (type === 'image' && slot === 'primary') {
|
|
2271
2281
|
var iy2 = Math.max(P.y || 2.3, opts.top || 0);
|
|
2272
|
-
|
|
2282
|
+
/* R2:有图注时图片不得越过 contentBottomWithNote(图注落注释带,属 annotation self) */
|
|
2283
|
+
var iBottom = (opts.bottom != null) ? opts.bottom
|
|
2284
|
+
: (opts.caption ? (L.contentBottomWithNote || 6.4) : bottomDefault);
|
|
2285
|
+
return { x: MX, y: iy2, w: CW, h: Math.max(1.2, Math.min(P.h || 4.2, iBottom - iy2)) };
|
|
2273
2286
|
}
|
|
2274
2287
|
if ((type === 'halftable') && (slot === 'left' || slot === 'primary')) {
|
|
2275
2288
|
var rs = PT.research || {};
|
|
2276
|
-
|
|
2289
|
+
var ht = opts.top != null ? opts.top : topDefault;
|
|
2290
|
+
var hb = opts.bottom != null ? opts.bottom : bottomDefault;
|
|
2291
|
+
return { x: MX, y: ht, w: CW * (rs.halfTableW || 0.52), h: Math.max(1, hb - ht) };
|
|
2277
2292
|
}
|
|
2278
2293
|
if ((type === 'halftable') && (slot === 'right' || slot === 'secondary')) {
|
|
2279
2294
|
var rs2 = PT.research || {};
|
|
2280
|
-
|
|
2295
|
+
var ht2 = opts.top != null ? opts.top : topDefault;
|
|
2296
|
+
var hb2 = opts.bottom != null ? opts.bottom : bottomDefault;
|
|
2297
|
+
return { x: MX + CW * (rs2.halfChartX || 0.57), y: ht2, w: CW * (rs2.halfChartW || 0.43), h: Math.max(1, hb2 - ht2) };
|
|
2281
2298
|
}
|
|
2282
2299
|
if (type === 'matrix' && slot === 'primary') {
|
|
2283
2300
|
var rm = PT.research || {};
|
|
@@ -2298,7 +2315,7 @@ function regionOf(pageType, slotId, opts) {
|
|
|
2298
2315
|
/* __TOPPPT_SCHEMA_START__ */
|
|
2299
2316
|
/* ── 由 scripts/sync_runtime.py 从 scripts/model-schema.json 注入 · 禁止手改 ── */
|
|
2300
2317
|
/* DSL schema 单源:浏览器端 validateModel 与 scripts/extract_model.py 消费同一份定义 */
|
|
2301
|
-
var MODEL_SCHEMA = {"version":"0.1","modes":["presentation","research","architecture"],"model":{"required":["title","sections:array","closing.title","closing.points:array"],"optional":["mode","style","theme","subtitle","meta","agenda:array"],"agendaMin":{"presentation":3,"research":3,"architecture":0},"sectionsMin":1,"sectionsRecommended":3,"agendaComfortMax":16},"commonSectionFields":["eyebrow","title","lead","soWhat","footnote","flags","image","exhibitNo","layoutPreset"],"flagsHint":"flags = 待核实/待二次修改条目字符串数组(如 '2027 增速 xx%(口径未定)');渲染为 accent 强调色标注行,提示用户核对。","imageHint":"image = 素材图片/配图占位对象。三选一必填:src(用户图,data: 内联或相对路径,零外链铁律禁 http(s))|items(多图版式:grid/compare/wall,元素 {src?, alt?, caption?, placeholder?})|placeholder:true(无图时出配图占位,原生形状渲染、pictures 不增)。其余可选:layout(full/half/bleed/grid/compare/wall,见 layout-constants.json imageSpec.layouts)、fit(cover 默认 / contain)、caption、alt、hint、ratio。","imageLayoutHint":"layout 与 HTML 版式对应:full=版心全宽图|half=左图右注(points 为右栏注解)|bleed=通栏出血|grid=多图网格(items 2/3/4/6 张)|compare=双图 A/B 对比(items 2 张)|wall=Logo 墙(items 3–12 张小图)。占位符标签文本由双引擎按 imageSpec 拼同一串,保证 A/B 通道逐页文本一致。","chartTypes":["bar","hbar","stack","stackline","line","dualline","area","donut","multidonut","pie","radar","scatter","bubble","waterfall","gauge","pareto","funnel","gantt","vsbar","progress","sparkline","slope","dumbbell","lollipop","dotplot","bulletchart","waffle","radialbar","rose","candlestick"],"chartTypesHint":"chart.type 可取值 = 本表(30 类:16 原生 + 14 形状还原)。其余 6 类复杂信息图(sankey/treemap/boxplot/network/marimekko/streamgraph)是**专属页型**而非 chart.type——它们要按列/段/节点等结构化载荷表达,故走 sections[].type。不变量:charts.registry.types == chartTypes ∪ 上述 6 类页型(sync_runtime.py 校验)。","chartDataTable":{"enum":["notes","inline","appendix","off"],"default":"notes","rule":"非原生图表(registry.pptx=shape)的 dataTable 不得为 off;缺失时按 registry 默认值补齐。"},"pageTypes":{"points":{"label":"要点列表页","modes":["presentation","research","architecture"],"required":["points:array"],"optional":["metrics:array"]},"metrics":{"label":"指标带页(4–6 个核心数字)","modes":["presentation","research"],"required":["metrics:array"]},"kpi":{"label":"大数指标页(hero 大数字 + 支撑指标行)","modes":["presentation","research"],"required":["hero:array"],"optional":["metrics:array"]},"table":{"label":"对比表页","modes":["presentation","research","architecture"],"required":["table.head:array","table.rows:array"],"optional":["table.colW:array"]},"timeline":{"label":"时间线 / 路线页","modes":["presentation","research"],"required":["phases:array"]},"steps":{"label":"步骤条页(N 步横排 + 箭头,可分组)","modes":["presentation","research","architecture"],"required":["steps:array"],"optional":["groups:array","note"]},"bar":{"label":"图表页(chart.type 见 chartTypes;native 通道走原生数据图表,shape 通道走高保真形状还原 + 数据表)","modes":["presentation","research"],"required":["chart.labels:array","chart.values:array"],"optional":["chart.type","chart.series:array","chart.points:array","chart.max","chart.unit","chart.colors:array","chart.start:array","chart.target:array","chart.dataTable","note"]},"donut":{"label":"环形图页(原生 pie 楔形 + 图例)","modes":["presentation","research"],"required":["chart.labels:array","chart.values:array"],"optional":["chart.unit","chart.centerLabel","chart.colors:array","chart.dataTable","note"]},"heatmap":{"label":"热力矩阵页(行 × 列 + 强度色阶)","modes":["presentation","research"],"required":["rowHeads:array","colHeads:array","cells:array"],"optional":["unit","scaleLabel:array","note"]},"bullet":{"label":"达成对比页(实际 vs 目标条)","modes":["presentation","research"],"required":["items:array"],"optional":["unit","max","note"]},"pyramid":{"label":"金字塔页(层级递进,顶层最窄)","modes":["presentation","research","architecture"],"required":["levels:array"],"optional":["note"]},"image":{"label":"素材图片页(full/half/bleed/grid/compare/wall,效果与位置锁定;支持配图占位)","modes":["presentation","research","architecture"],"required":["anyof:image.src:str|image.items:array|image.placeholder"],"optional":["image.caption","image.alt","image.layout","image.fit","image.ratio","image.hint","points:array","note"]},"cards":{"label":"卡片网格页(cards 元素须为 {title, points:[[k,v]…]},不接受元组)","modes":["presentation","research"],"required":["cards:array"],"optional":["columns"]},"split":{"label":"双区组合页(left / right 各可为 要点|图表|表格|图片:type = points(默认)| table | image | 图表类型名;图表/表格字段直接挂在对应侧)","modes":["presentation","research"],"required":["anyof:left.points:array|left.type:str"],"optional":["left.cap","left.image","left.labels:array","left.values:array","left.series:array","left.points:array","left.max","left.unit","left.colors:array","left.dataTable","left.head:array","left.rows:array","left.colW:array","right.type","right.cap","right.image","right.labels:array","right.values:array","right.series:array","right.points:array","right.max","right.unit","right.colors:array","right.dataTable","right.head:array","right.rows:array","right.colW:array"]},"comparison":{"label":"对比页(左右双栏 + 可选结论条)","modes":["presentation","research"],"required":["left.title","left.points:array","right.title","right.points:array"],"optional":["verdict"]},"quote":{"label":"引用 / 金句页(深色全幅)","modes":["presentation","research"],"required":["quote","author"],"optional":["context"]},"diagram":{"label":"分层架构页(architecture 模式走全幅几何)","modes":["presentation","research","architecture"],"required":["layers:array"],"optional":["legend:array"]},"exhibit":{"label":"Exhibit 编号图表页(research R2)","modes":["research"],"required":["chart.labels:array","chart.values:array"],"optional":["exhibitNo","chart.type","chart.series:array","chart.points:array","chart.max","chart.unit","chart.colors:array","chart.dataTable","note"]},"twocol":{"label":"双栏论证页(research R1)","modes":["research"],"required":["paragraphs:array"]},"threecol":{"label":"三栏证据页(research R6)","modes":["research"],"required":["paragraphs:array"]},"halftable":{"label":"半表半图页(research R7)","modes":["research"],"required":["table.head:array","table.rows:array","chart.labels:array","chart.values:array"],"optional":["table.colW:array","chart.type","chart.series:array","chart.points:array","chart.max","chart.unit","chart.colors:array","chart.dataTable","note"]},"matrix":{"label":"矩阵图页(research R8)","modes":["research"],"required":["rowHeads:array","colHeads:array","cells:array"]},"lane":{"label":"泳道页(architecture A2)","modes":["architecture"],"required":["lanes:array"]},"sankey":{"label":"桑基图页(节点-流带,流向与流量)","modes":["research","architecture"],"required":["flows:array"],"optional":["unit","chart.dataTable","note"]},"treemap":{"label":"树图页(面积编码的层级构成)","modes":["research","architecture"],"required":["items:array"],"optional":["unit","chart.dataTable","note"]},"boxplot":{"label":"箱线图页(分布对比:min/q1/median/q3/max)","modes":["research","architecture"],"required":["groups:array"],"optional":["unit","chart.dataTable","note"]},"network":{"label":"关系网络页(节点-边拓扑)","modes":["research","architecture"],"required":["nodes:array","edges:array"],"optional":["chart.dataTable","note"]},"marimekko":{"label":"马赛克图页(列宽 × 列高双重编码)","modes":["research","architecture"],"required":["cols:array","cells:array"],"optional":["unit","chart.dataTable","note"]},"streamgraph":{"label":"流带图页(时间上的构成演变)","modes":["research","architecture"],"required":["series:array"],"optional":["labels:array","chart.dataTable","note"]}}};
|
|
2318
|
+
var MODEL_SCHEMA = {"version":"0.1","modes":["presentation","research","architecture"],"model":{"required":["title","sections:array","closing.title","closing.points:array"],"optional":["mode","style","theme","subtitle","meta","agenda:array"],"agendaMin":{"presentation":3,"research":3,"architecture":0},"sectionsMin":1,"sectionsRecommended":3,"agendaComfortMax":16},"commonSectionFields":["eyebrow","title","lead","soWhat","footnote","flags","image","exhibitNo","layoutPreset"],"mutualExclusionHint":"verdict 与 soWhat 互斥:二者共用 annotation 结论条槽位,同页双填必叠印。comparison 页填 verdict 时不要再填 soWhat(渲染器已自动二选一,但模型侧应只给一个)。","flagsHint":"flags = 待核实/待二次修改条目字符串数组(如 '2027 增速 xx%(口径未定)');渲染为 accent 强调色标注行,提示用户核对。","imageHint":"image = 素材图片/配图占位对象。三选一必填:src(用户图,data: 内联或相对路径,零外链铁律禁 http(s))|items(多图版式:grid/compare/wall,元素 {src?, alt?, caption?, placeholder?})|placeholder:true(无图时出配图占位,原生形状渲染、pictures 不增)。其余可选:layout(full/half/bleed/grid/compare/wall,见 layout-constants.json imageSpec.layouts)、fit(cover 默认 / contain)、caption、alt、hint、ratio。","imageLayoutHint":"layout 与 HTML 版式对应:full=版心全宽图|half=左图右注(points 为右栏注解)|bleed=通栏出血|grid=多图网格(items 2/3/4/6 张)|compare=双图 A/B 对比(items 2 张)|wall=Logo 墙(items 3–12 张小图)。占位符标签文本由双引擎按 imageSpec 拼同一串,保证 A/B 通道逐页文本一致。","chartTypes":["bar","hbar","stack","stackline","line","dualline","area","donut","multidonut","pie","radar","scatter","bubble","waterfall","gauge","pareto","funnel","gantt","vsbar","progress","sparkline","slope","dumbbell","lollipop","dotplot","bulletchart","waffle","radialbar","rose","candlestick"],"chartTypesHint":"chart.type 可取值 = 本表(30 类:16 原生 + 14 形状还原)。其余 6 类复杂信息图(sankey/treemap/boxplot/network/marimekko/streamgraph)是**专属页型**而非 chart.type——它们要按列/段/节点等结构化载荷表达,故走 sections[].type。不变量:charts.registry.types == chartTypes ∪ 上述 6 类页型(sync_runtime.py 校验)。","chartDataTable":{"enum":["notes","inline","appendix","off"],"default":"notes","rule":"非原生图表(registry.pptx=shape)的 dataTable 不得为 off;缺失时按 registry 默认值补齐。"},"pageTypes":{"points":{"label":"要点列表页","modes":["presentation","research","architecture"],"required":["points:array"],"optional":["metrics:array"]},"metrics":{"label":"指标带页(4–6 个核心数字)","modes":["presentation","research"],"required":["metrics:array"]},"kpi":{"label":"大数指标页(hero 大数字 + 支撑指标行)","modes":["presentation","research"],"required":["hero:array"],"optional":["metrics:array"]},"table":{"label":"对比表页","modes":["presentation","research","architecture"],"required":["table.head:array","table.rows:array"],"optional":["table.colW:array"]},"timeline":{"label":"时间线 / 路线页","modes":["presentation","research"],"required":["phases:array"]},"steps":{"label":"步骤条页(N 步横排 + 箭头,可分组)","modes":["presentation","research","architecture"],"required":["steps:array"],"optional":["groups:array","note"]},"bar":{"label":"图表页(chart.type 见 chartTypes;native 通道走原生数据图表,shape 通道走高保真形状还原 + 数据表)","modes":["presentation","research"],"required":["chart.labels:array","chart.values:array"],"optional":["chart.type","chart.series:array","chart.points:array","chart.max","chart.unit","chart.colors:array","chart.start:array","chart.target:array","chart.dataTable","note"]},"donut":{"label":"环形图页(原生 pie 楔形 + 图例)","modes":["presentation","research"],"required":["chart.labels:array","chart.values:array"],"optional":["chart.unit","chart.centerLabel","chart.colors:array","chart.dataTable","note"]},"heatmap":{"label":"热力矩阵页(行 × 列 + 强度色阶)","modes":["presentation","research"],"required":["rowHeads:array","colHeads:array","cells:array"],"optional":["unit","scaleLabel:array","note"]},"bullet":{"label":"达成对比页(实际 vs 目标条)","modes":["presentation","research"],"required":["items:array"],"optional":["unit","max","note"]},"pyramid":{"label":"金字塔页(层级递进,顶层最窄)","modes":["presentation","research","architecture"],"required":["levels:array"],"optional":["note"]},"image":{"label":"素材图片页(full/half/bleed/grid/compare/wall,效果与位置锁定;支持配图占位)","modes":["presentation","research","architecture"],"required":["anyof:image.src:str|image.items:array|image.placeholder"],"optional":["image.caption","image.alt","image.layout","image.fit","image.ratio","image.hint","points:array","note"]},"cards":{"label":"卡片网格页(cards 元素须为 {title, points:[[k,v]…]},不接受元组)","modes":["presentation","research"],"required":["cards:array"],"optional":["columns"]},"split":{"label":"双区组合页(left / right 各可为 要点|图表|表格|图片:type = points(默认)| table | image | 图表类型名;图表/表格字段直接挂在对应侧)","modes":["presentation","research"],"required":["anyof:left.points:array|left.type:str"],"optional":["left.cap","left.image","left.labels:array","left.values:array","left.series:array","left.points:array","left.max","left.unit","left.colors:array","left.dataTable","left.head:array","left.rows:array","left.colW:array","right.type","right.cap","right.image","right.labels:array","right.values:array","right.series:array","right.points:array","right.max","right.unit","right.colors:array","right.dataTable","right.head:array","right.rows:array","right.colW:array"]},"comparison":{"label":"对比页(左右双栏 + 可选结论条)","modes":["presentation","research"],"required":["left.title","left.points:array","right.title","right.points:array"],"optional":["verdict"]},"quote":{"label":"引用 / 金句页(深色全幅)","modes":["presentation","research"],"required":["quote","author"],"optional":["context"]},"diagram":{"label":"分层架构页(architecture 模式走全幅几何)","modes":["presentation","research","architecture"],"required":["layers:array"],"optional":["legend:array"]},"exhibit":{"label":"Exhibit 编号图表页(research R2)","modes":["research"],"required":["chart.labels:array","chart.values:array"],"optional":["exhibitNo","chart.type","chart.series:array","chart.points:array","chart.max","chart.unit","chart.colors:array","chart.dataTable","note"]},"twocol":{"label":"双栏论证页(research R1)","modes":["research"],"required":["paragraphs:array"]},"threecol":{"label":"三栏证据页(research R6)","modes":["research"],"required":["paragraphs:array"]},"halftable":{"label":"半表半图页(research R7)","modes":["research"],"required":["table.head:array","table.rows:array","chart.labels:array","chart.values:array"],"optional":["table.colW:array","chart.type","chart.series:array","chart.points:array","chart.max","chart.unit","chart.colors:array","chart.dataTable","note"]},"matrix":{"label":"矩阵图页(research R8)","modes":["research"],"required":["rowHeads:array","colHeads:array","cells:array"]},"lane":{"label":"泳道页(architecture A2)","modes":["architecture"],"required":["lanes:array"]},"sankey":{"label":"桑基图页(节点-流带,流向与流量)","modes":["research","architecture"],"required":["flows:array"],"optional":["unit","chart.dataTable","note"]},"treemap":{"label":"树图页(面积编码的层级构成)","modes":["research","architecture"],"required":["items:array"],"optional":["unit","chart.dataTable","note"]},"boxplot":{"label":"箱线图页(分布对比:min/q1/median/q3/max)","modes":["research","architecture"],"required":["groups:array"],"optional":["unit","chart.dataTable","note"]},"network":{"label":"关系网络页(节点-边拓扑)","modes":["research","architecture"],"required":["nodes:array","edges:array"],"optional":["chart.dataTable","note"]},"marimekko":{"label":"马赛克图页(列宽 × 列高双重编码)","modes":["research","architecture"],"required":["cols:array","cells:array"],"optional":["unit","chart.dataTable","note"]},"streamgraph":{"label":"流带图页(时间上的构成演变)","modes":["research","architecture"],"required":["series:array"],"optional":["labels:array","chart.dataTable","note"]}}};
|
|
2302
2319
|
/* __TOPPPT_SCHEMA_END__ */
|
|
2303
2320
|
|
|
2304
2321
|
var _mode = 'presentation'; /* 当前模式(slidesOf 里按 model.mode 设置) */
|
|
@@ -2589,6 +2606,15 @@ function footnoteLine(sh, text, S) {
|
|
|
2589
2606
|
sh.push(txSp(R.x, R.y, R.w, R.h,
|
|
2590
2607
|
[[{ t: text, sz: 9.5, col: S.faint, font: S.font }]]));
|
|
2591
2608
|
}
|
|
2609
|
+
/* R2:sec.note 与 sec.footnote 共用注释带末行(footnoteY=6.72)。
|
|
2610
|
+
note.y=6.55 会压进 so-what 带(6.05–6.67)——同页二者必须合并为一行。 */
|
|
2611
|
+
function noteOrFootnote(sh, sec, S) {
|
|
2612
|
+
var parts = [];
|
|
2613
|
+
if (sec && sec.note) parts.push(String(sec.note));
|
|
2614
|
+
if (sec && sec.footnote) parts.push(String(sec.footnote));
|
|
2615
|
+
if (!parts.length) return;
|
|
2616
|
+
footnoteLine(sh, parts.join(' · '), S);
|
|
2617
|
+
}
|
|
2592
2618
|
/* 待核实清单条(accent 强调色标注 · 与 build_pptx.js / HTML .flagbar 同源) */
|
|
2593
2619
|
function flagBar(sh, items, S, yTop) {
|
|
2594
2620
|
var F = PT.flagbar || { hdH: 0.26, rowH: 0.24, maxRows: 3, gap: 0.12 };
|
|
@@ -2777,7 +2803,9 @@ function vbarShapes(sh, ec, S, x, y, w, h, dcols) {
|
|
|
2777
2803
|
: [{ name: '数值', values: ec.values || [] }];
|
|
2778
2804
|
var nb = Math.max(1, labels.length);
|
|
2779
2805
|
var ns = Math.max(1, series.length);
|
|
2780
|
-
|
|
2806
|
+
/* R2:类目标签带必须含在 h 内(原 baseY+0.08 画在图区外,会压进 so-what/来源行) */
|
|
2807
|
+
var labelH = 0.36;
|
|
2808
|
+
var baseY = y + Math.max(0.6, h - labelH), plotW = w, step = plotW / nb;
|
|
2781
2809
|
var cluster = Math.min(1.1, step * (ns > 1 ? 0.78 : 0.55));
|
|
2782
2810
|
var bw = cluster / ns;
|
|
2783
2811
|
var all = [];
|
|
@@ -2786,26 +2814,27 @@ function vbarShapes(sh, ec, S, x, y, w, h, dcols) {
|
|
|
2786
2814
|
});
|
|
2787
2815
|
var vmax = ec.max || Math.max.apply(null, all.concat([1]));
|
|
2788
2816
|
var top = all.length ? Math.max.apply(null, all) : 0;
|
|
2817
|
+
var plotH = Math.max(0.35, baseY - y);
|
|
2789
2818
|
sh.push(shape('rect', x, baseY, plotW, 0.02, S.line));
|
|
2790
2819
|
series.forEach(function (se, si) {
|
|
2791
2820
|
var vals = se.values || [];
|
|
2792
2821
|
var col = dcols ? dcols[si % 5] : (si === 0 ? S.accent : S.faint);
|
|
2793
2822
|
vals.forEach(function (v, i) {
|
|
2794
2823
|
var num = Number(v) || 0;
|
|
2795
|
-
var hh = Math.max(0.05, num / vmax * (
|
|
2824
|
+
var hh = Math.max(0.05, num / vmax * (plotH - 0.35));
|
|
2796
2825
|
var bx = x + i * step + (step - cluster) / 2 + si * bw;
|
|
2797
2826
|
var hot = ns === 1 && num === top;
|
|
2798
2827
|
sh.push(shape('roundRect', bx, baseY - hh, Math.max(0.04, bw * 0.92), hh,
|
|
2799
2828
|
hot ? (dcols ? dcols[i % 5] : S.accent) : col, { adj: 6000 }));
|
|
2800
2829
|
if (ns === 1) {
|
|
2801
|
-
sh.push(txSp(bx - 0.3, baseY - hh - 0.
|
|
2830
|
+
sh.push(txSp(bx - 0.3, baseY - hh - 0.36, bw + 0.6, 0.30,
|
|
2802
2831
|
[[{ t: valText(num, ec.unit), sz: 12.5, b: 1, col: hot ? S.accent : S.body, font: S.font }]], { align: 'ctr' }));
|
|
2803
2832
|
}
|
|
2804
2833
|
});
|
|
2805
2834
|
});
|
|
2806
2835
|
labels.forEach(function (lb, i) {
|
|
2807
2836
|
var lx = x + i * step;
|
|
2808
|
-
sh.push(txSp(lx, baseY + 0.
|
|
2837
|
+
sh.push(txSp(lx, baseY + 0.06, step, labelH - 0.08,
|
|
2809
2838
|
[[{ t: lb, sz: 10.5, col: S.faint, font: S.font }]], { align: 'ctr' }));
|
|
2810
2839
|
});
|
|
2811
2840
|
/* Multi-series legend along top of plot (keeps category labels; avoids blank chart). */
|
|
@@ -2828,7 +2857,10 @@ function lineShapes(sh, ec, S, x, y, w, h, dcols) {
|
|
|
2828
2857
|
var all = [];
|
|
2829
2858
|
series.forEach(function (se) { (se.values || []).forEach(function (v) { all.push(Number(v) || 0); }); });
|
|
2830
2859
|
var vmax = ec.max || Math.max.apply(null, all.concat([1]));
|
|
2831
|
-
|
|
2860
|
+
/* R2:类目标签带含在 h 内,禁画到图区外压 so-what */
|
|
2861
|
+
var labelH = 0.36;
|
|
2862
|
+
var baseY = y + Math.max(0.6, h - labelH);
|
|
2863
|
+
var plotH = Math.max(0.35, baseY - y - 0.2);
|
|
2832
2864
|
sh.push(shape('rect', x, baseY, w, 0.02, S.line));
|
|
2833
2865
|
var stepX = n > 1 ? w / (n - 1) : 0;
|
|
2834
2866
|
series.forEach(function (se, si) {
|
|
@@ -2848,7 +2880,7 @@ function lineShapes(sh, ec, S, x, y, w, h, dcols) {
|
|
|
2848
2880
|
});
|
|
2849
2881
|
labels.forEach(function (lb, i) {
|
|
2850
2882
|
var px = x + (n > 1 ? i * stepX : w / 2);
|
|
2851
|
-
sh.push(txSp(px - 0.5, baseY + 0.
|
|
2883
|
+
sh.push(txSp(px - 0.5, baseY + 0.06, 1.0, labelH - 0.08,
|
|
2852
2884
|
[[{ t: lb, sz: 10.5, col: S.faint, font: S.font }]], { align: 'ctr' }));
|
|
2853
2885
|
});
|
|
2854
2886
|
}
|
|
@@ -3300,25 +3332,44 @@ function slidesOf(model, S) {
|
|
|
3300
3332
|
c.push(txSp(MX, CV.subtitleY, CW - 1, CV.subtitleH, [[{ t: model.subtitle || '', sz: 18, col: S.soft, font: S.font }]]));
|
|
3301
3333
|
slides.push(c);
|
|
3302
3334
|
|
|
3303
|
-
/* Agenda(>8
|
|
3335
|
+
/* Agenda(>8 条自动双列;>12 条自动分页;长标题截断+全称入 notes;列优先与 HTML 一致) */
|
|
3304
3336
|
var items = model.agenda || [];
|
|
3305
3337
|
if (items.length) {
|
|
3306
|
-
var
|
|
3307
|
-
|
|
3308
|
-
var
|
|
3309
|
-
|
|
3310
|
-
|
|
3311
|
-
|
|
3312
|
-
|
|
3313
|
-
|
|
3314
|
-
var
|
|
3315
|
-
var
|
|
3316
|
-
|
|
3317
|
-
|
|
3318
|
-
|
|
3319
|
-
|
|
3338
|
+
var ROWS_PER_PAGE = 12;
|
|
3339
|
+
var agPages = [];
|
|
3340
|
+
for (var ai = 0; ai < items.length; ai += ROWS_PER_PAGE) agPages.push(items.slice(ai, ai + ROWS_PER_PAGE));
|
|
3341
|
+
agPages.forEach(function (pageItems, pi) {
|
|
3342
|
+
var ag = [];
|
|
3343
|
+
var sub = agPages.length > 1 ? '(' + (pi + 1) + '/' + agPages.length + ')' : '';
|
|
3344
|
+
head(ag, 'AGENDA', '报告大纲' + sub);
|
|
3345
|
+
var colN = pageItems.length > 8 ? 2 : 1;
|
|
3346
|
+
var perCol = Math.ceil(pageItems.length / colN) || 1;
|
|
3347
|
+
var rowH = Math.min(1.05, (CONTENT_BOTTOM - CONTENT_TOP - 0.25) / Math.max(1, perCol));
|
|
3348
|
+
var numSz = rowH >= 0.85 ? 30 : (rowH >= 0.65 ? 22 : 18);
|
|
3349
|
+
var tSz = rowH >= 0.85 ? 16 : 14, dSz = rowH >= 0.85 ? 12 : 11;
|
|
3350
|
+
var agNotes = [];
|
|
3351
|
+
pageItems.forEach(function (it, i) {
|
|
3352
|
+
var col = Math.floor(i / perCol), row = i % perCol;
|
|
3353
|
+
var x = MX + col * (CW / colN), y = CONTENT_TOP + row * rowH;
|
|
3354
|
+
var titleBoxW = CW / colN - 1.15;
|
|
3355
|
+
var rawTitle = String(it[1] || '');
|
|
3356
|
+
var titleEstW = estTextH(rawTitle, titleBoxW, tSz, 1.2);
|
|
3357
|
+
var maxTitleH = rowH * 0.55;
|
|
3358
|
+
var title = rawTitle;
|
|
3359
|
+
if (titleEstW > maxTitleH) {
|
|
3360
|
+
var maxChars = Math.max(10, Math.floor(rawTitle.length * (maxTitleH / titleEstW)));
|
|
3361
|
+
if (rawTitle.length > maxChars) {
|
|
3362
|
+
title = rawTitle.slice(0, maxChars - 1) + '…';
|
|
3363
|
+
agNotes.push('条目 ' + it[0] + ' 全称:' + rawTitle);
|
|
3364
|
+
}
|
|
3365
|
+
}
|
|
3366
|
+
ag.push(txSp(x, y, 0.9, Math.min(0.9, rowH), [[{ t: it[0], sz: numSz, b: 1, col: S.accent, font: S.font }]]));
|
|
3367
|
+
ag.push(txSp(x + 1.0, y + 0.03, titleBoxW, Math.min(0.95, rowH),
|
|
3368
|
+
[[{ t: title, sz: tSz, b: 1, col: S.ink, font: S.font }],
|
|
3369
|
+
[{ t: it[2] || '', sz: dSz, col: S.body, font: S.font }]], { lineSpacing: tSz + 5 }));
|
|
3370
|
+
});
|
|
3371
|
+
slides.push(ag);
|
|
3320
3372
|
});
|
|
3321
|
-
slides.push(ag);
|
|
3322
3373
|
}
|
|
3323
3374
|
|
|
3324
3375
|
/* 章节页 */
|
|
@@ -3404,12 +3455,13 @@ function slidesOf(model, S) {
|
|
|
3404
3455
|
{ t: p[1], sz: fz - 1, col: S.body, font: S.font }]]));
|
|
3405
3456
|
});
|
|
3406
3457
|
});
|
|
3458
|
+
/* R2:verdict 与 soWhat 共用 annotation 槽位——只画一个(verdict 优先) */
|
|
3407
3459
|
if (sec.verdict) {
|
|
3408
3460
|
var vR = regOf('exhibit', 'annotation') || { x: MX, y: PT.exhibit.soWhatY, w: CW, h: 0.62 };
|
|
3409
|
-
sh.push(shape('rect', vR.x, vR.y, vR.w, vR.h, S.accent));
|
|
3461
|
+
sh.push(shape('rect', vR.x, vR.y, vR.w, vR.h, S.accent, { name: 'tr:soWhat' }));
|
|
3410
3462
|
sh.push(txSp(vR.x + 0.22, vR.y + 0.06, vR.w - 0.44, vR.h - 0.12,
|
|
3411
3463
|
[[{ t: '结论 ', sz: 11, b: 1, col: S.onAccent, font: S.font, spc: 150 },
|
|
3412
|
-
{ t: sec.verdict, sz: 12.5, b: 1, col: S.onAccent, font: S.font }]], { anchor: 'ctr' }));
|
|
3464
|
+
{ t: sec.verdict, sz: 12.5, b: 1, col: S.onAccent, font: S.font }]], { anchor: 'ctr', name: 'tr:soWhat' }));
|
|
3413
3465
|
}
|
|
3414
3466
|
} else if (type === 'quote') {
|
|
3415
3467
|
var Q = PT.quote;
|
|
@@ -3442,7 +3494,6 @@ function slidesOf(model, S) {
|
|
|
3442
3494
|
} else if (type === 'donut') {
|
|
3443
3495
|
var dcn = sec.chart || {};
|
|
3444
3496
|
if (dcn.labels && dcn.values) donutChart(sh, dcn, S, dcn.colors || donutColors(model.style, S, model.theme), bodyBottom);
|
|
3445
|
-
if (sec.note) sh.push(txSp(MX, PT.note.y, CW, PT.note.h, [[{ t: sec.note, sz: 11, col: S.faint, font: S.font }]]));
|
|
3446
3497
|
} else if (type === 'heatmap') {
|
|
3447
3498
|
var H = PT.heatmap;
|
|
3448
3499
|
var hR = regOf('heatmap', 'primary', { bottom: bodyBottom }) || { x: MX, y: H.y, w: CW, h: bodyBottom - H.y };
|
|
@@ -3589,7 +3640,7 @@ function slidesOf(model, S) {
|
|
|
3589
3640
|
} else if (type === 'bar') {
|
|
3590
3641
|
var ch = sec.chart || {};
|
|
3591
3642
|
var dcols2 = (ch.colors || dataColors(model.style, model.theme));
|
|
3592
|
-
var botY = chartBottom(!!sec.soWhat, !!sec.footnote
|
|
3643
|
+
var botY = chartBottom(!!sec.soWhat, !!(sec.footnote || sec.note)) - 0.05;
|
|
3593
3644
|
var isHBar = ch.type === 'hbar';
|
|
3594
3645
|
var bRR = regOf('bar', isHBar ? 'hbar' : 'primary', { bottom: botY }) ||
|
|
3595
3646
|
{ x: isHBar ? MX : MX + PT.bar.chartX, y: isHBar ? PT.bar.hbarY0 : PT.bar.chartY,
|
|
@@ -3616,13 +3667,12 @@ function slidesOf(model, S) {
|
|
|
3616
3667
|
} else {
|
|
3617
3668
|
chartBlockShapes(sh, ch, S, bx, bRR.y, bw, Math.max(1.5, bRR.h), dcols2);
|
|
3618
3669
|
}
|
|
3619
|
-
if (sec.note) sh.push(txSp(MX, PT.note.y, CW, PT.note.h, [[{ t: sec.note, sz: 11, col: S.faint, font: S.font }]]));
|
|
3620
3670
|
} else if (type === 'twocol' || type === 'threecol') {
|
|
3621
3671
|
var ps2 = sec.paragraphs || [];
|
|
3622
3672
|
var nCol = (type === 'threecol') ? 3 : 2;
|
|
3623
3673
|
var gapC = (type === 'threecol') ? PT.research.col3Gap : PT.twocol.colGap;
|
|
3624
|
-
|
|
3625
|
-
|
|
3674
|
+
/* R3:多栏均分用版心全宽(regOf('twocol') 返回单栏宽,当总宽会压扁三栏) */
|
|
3675
|
+
var tcR = { x: MX, y: bodyY, w: CW, h: bodyBottom - bodyY };
|
|
3626
3676
|
var gC = cols(tcR.w, nCol, gapC);
|
|
3627
3677
|
var perC = Math.ceil(ps2.length / nCol);
|
|
3628
3678
|
var availC = tcR.h;
|
|
@@ -3818,7 +3868,7 @@ function slidesOf(model, S) {
|
|
|
3818
3868
|
var dStart = dgR.y;
|
|
3819
3869
|
var dEnd = dgR.y + dgR.h;
|
|
3820
3870
|
var nLay = Math.max(1, lys.length);
|
|
3821
|
-
var lh2 = Math.max(0.
|
|
3871
|
+
var lh2 = Math.max(0.5, Math.min(D.maxLayerH, (dEnd - dStart) / nLay - D.layerGap));
|
|
3822
3872
|
lys.forEach(function (lay, li) {
|
|
3823
3873
|
var y9 = dStart + li * (lh2 + D.layerGap);
|
|
3824
3874
|
var focus = lay[2] === 'focus';
|
|
@@ -3834,9 +3884,26 @@ function slidesOf(model, S) {
|
|
|
3834
3884
|
var ntt = isRec(nd) ? nd.t : nd;
|
|
3835
3885
|
var nds = isRec(nd) ? (nd.d || '') : '';
|
|
3836
3886
|
sh.push(shape('roundRect', x10, y9, nw2, lh2, ndacc ? S.soft : S.surface, { adj: 4000, line: S.line }));
|
|
3837
|
-
|
|
3838
|
-
|
|
3839
|
-
|
|
3887
|
+
/* R1: 注解不得「浮回」标题上。标题顶对齐,注解钳在标题下边;空间不足降级单框混排。 */
|
|
3888
|
+
var padXn = 0.12, padYn = 0.10;
|
|
3889
|
+
var innerWn = nw2 - padXn * 2;
|
|
3890
|
+
var titleHn = 0.32, noteHn = 0.34, gapTNn = 0.04;
|
|
3891
|
+
var needHn = titleHn + gapTNn + (nds ? noteHn : 0) + padYn * 2;
|
|
3892
|
+
if (!nds || lh2 >= needHn) {
|
|
3893
|
+
var tYn = y9 + padYn;
|
|
3894
|
+
sh.push(txSp(x10 + padXn, nds ? tYn : y9 + lh2 / 2 - 0.18, innerWn, titleHn,
|
|
3895
|
+
[[{ t: ntt, sz: nds ? 11 : 12.5, b: 1, col: ndacc ? S.accent : S.ink, font: S.font }]],
|
|
3896
|
+
{ anchor: 'ctr' }));
|
|
3897
|
+
if (nds) {
|
|
3898
|
+
var nYn = Math.max(tYn + titleHn + gapTNn, y9 + lh2 - padYn - noteHn);
|
|
3899
|
+
sh.push(txSp(x10 + padXn, nYn, innerWn, Math.min(noteHn, y9 + lh2 - padYn - nYn),
|
|
3900
|
+
[[{ t: nds, sz: 10, col: S.faint, font: S.font }]], { anchor: 't' }));
|
|
3901
|
+
}
|
|
3902
|
+
} else {
|
|
3903
|
+
sh.push(txSp(x10 + padXn, y9 + padYn * 0.5, innerWn, lh2 - padYn,
|
|
3904
|
+
[[{ t: ntt, sz: 11, b: 1, col: ndacc ? S.accent : S.ink, font: S.font }],
|
|
3905
|
+
[{ t: nds, sz: 9, col: S.faint, font: S.font }]], { anchor: 't', lineSpacing: 12 }));
|
|
3906
|
+
}
|
|
3840
3907
|
});
|
|
3841
3908
|
if (li < lys.length - 1) {
|
|
3842
3909
|
var cy2 = y9 + lh2 + D.layerGap / 2;
|
|
@@ -3846,10 +3913,14 @@ function slidesOf(model, S) {
|
|
|
3846
3913
|
}
|
|
3847
3914
|
});
|
|
3848
3915
|
if (sec.legend) {
|
|
3849
|
-
|
|
3850
|
-
|
|
3851
|
-
|
|
3852
|
-
|
|
3916
|
+
/* R2:图例进图区底部(与 HTML .arch__legend 同源),禁用 PH-0.62 固定偏移 */
|
|
3917
|
+
var lgH2 = 0.28;
|
|
3918
|
+
var lgY2 = Math.max(dStart, bodyBottom - lgH2 - 0.02);
|
|
3919
|
+
var nLg2 = Math.max(1, sec.legend.length);
|
|
3920
|
+
var step2 = Math.min(2.4, CW / nLg2);
|
|
3921
|
+
sec.legend.forEach(function (lg2, li2) {
|
|
3922
|
+
sh.push(txSp(MX + CW - nLg2 * step2 + li2 * step2, lgY2, step2 - 0.05, lgH2,
|
|
3923
|
+
[[{ t: '● ' + lg2, sz: 10, col: S.faint, font: S.font }]]));
|
|
3853
3924
|
});
|
|
3854
3925
|
}
|
|
3855
3926
|
} else if (type === 'exhibit') {
|
|
@@ -3906,8 +3977,10 @@ function slidesOf(model, S) {
|
|
|
3906
3977
|
}
|
|
3907
3978
|
}
|
|
3908
3979
|
/* research 通用可选件:so-what 结论条 + 页脚来源行 + 待核实条(共用同一槽位) */
|
|
3909
|
-
|
|
3910
|
-
if (sec.
|
|
3980
|
+
/* R2:verdict 已占 annotation 槽位时跳过 soWhat,禁止同槽双条叠印 */
|
|
3981
|
+
if (sec.soWhat && !sec.verdict) soWhatBar(sh, sec.soWhat, S);
|
|
3982
|
+
/* R2:note 与 footnote 共用注释带末行(禁 note.y=6.55 压 so-what) */
|
|
3983
|
+
noteOrFootnote(sh, sec, S);
|
|
3911
3984
|
if (flagH) flagBar(sh, flagItems, S, flagY);
|
|
3912
3985
|
slides.push(sh);
|
|
3913
3986
|
});
|
|
@@ -4077,9 +4150,10 @@ function validateModel(model) {
|
|
|
4077
4150
|
}
|
|
4078
4151
|
var agMax = (M.model && M.model.agendaComfortMax) || 16;
|
|
4079
4152
|
if ((model.agenda || []).length > agMax) {
|
|
4080
|
-
warnings.push('agenda ' + model.agenda.length + ' 条超出单页舒适上限 ' + agMax + '
|
|
4153
|
+
warnings.push('agenda ' + model.agenda.length + ' 条超出单页舒适上限 ' + agMax + ',将自动分页');
|
|
4081
4154
|
}
|
|
4082
|
-
var
|
|
4155
|
+
var agPageCount = hasAgenda ? Math.ceil(model.agenda.length / 12) : 0;
|
|
4156
|
+
var pages = 1 + agPageCount + secs.length + 1; /* 封面+大纲(可多页)+章节+收尾 */
|
|
4083
4157
|
return { ok: missing.length === 0, missing: missing, warnings: warnings, pages: pages };
|
|
4084
4158
|
}
|
|
4085
4159
|
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "top-ppt-html",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.18",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "top-ppt-html",
|
|
9
|
-
"version": "0.1.
|
|
9
|
+
"version": "0.1.18",
|
|
10
10
|
"license": "MIT",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"pptxgenjs": "^4.0.0"
|
|
@@ -2,34 +2,35 @@
|
|
|
2
2
|
|
|
3
3
|
> **何时读**:选图 / 分析意图→图型;常读入口仍是 `playbook.md` §五(核图 8 + 多样性摘要)。读完即停。
|
|
4
4
|
> 完整登记与代码 → `extract_snippet.py --chart <类型>`;误用纪律 → `charts.md`(先 `charts-discipline`,核图 8,extended 按需)。
|
|
5
|
+
> **PPTX 还原度**:`high` 双通道等价|`medium` 形状可读但视觉降级|`low` PPTX 显著退化。low 图型见表末「改判」列——交付 PPTX 时优先 `bar`/`hbar`/`line`/`donut`,或 `dataTable=inline` 强制补数据表。
|
|
5
6
|
|
|
6
7
|
## 决策表
|
|
7
8
|
|
|
8
|
-
| 分析意图(读者要得出的结论) | 首选 | 次选 | 禁用 / 改判条件 |
|
|
9
|
-
|
|
10
|
-
| 谁大谁小 | `hbar` | `lollipop` / `bar` | 类别 >8 → Top7 + 附录 |
|
|
11
|
-
| 随时间怎么变 | `line` | `area
|
|
12
|
-
| 占比是多少 | `donut` | `pie` / `multidonut` | 扇区 >5 → 合并"其他"或 `treemap
|
|
13
|
-
| 每 1% 的直觉 | `waffle` | `donut` | 类别 >3 改 `marimekko
|
|
14
|
-
| 结构在变形 | `streamgraph` | `stackline
|
|
15
|
-
| 总量 × 构成双编码 | `marimekko` | `treemap
|
|
16
|
-
| 份额悬殊(头部集中) | `treemap` | `donut
|
|
17
|
-
| 从 A 到 B 是谁贡献的 | `waterfall` | `pareto` | 段 >6 → 合并 |
|
|
18
|
-
| 主要矛盾是哪几个 | `pareto` | `waterfall` | 类别 >7 → 合并 |
|
|
19
|
-
| 转化/流失在哪一环 | `funnel` | `sankey
|
|
20
|
-
| 多对多的流向 | `sankey` | `network
|
|
21
|
-
| 谁和谁相连 | `network` | `diagram` | 节点 >18 / 边 >30 →
|
|
22
|
-
| 两期谁升谁降 | `slope` | `dumbbell` | 系列 >6 → 取 Top 6 |
|
|
23
|
-
| 单指标前后对比(多对象) | `dumbbell` | `vsbar
|
|
24
|
-
| 实际 vs 目标(含区间) | `bulletchart` | `bullet` | 行 >6 → 拆页 |
|
|
25
|
-
| 达成率(多指标同心) | `radialbar` | `gauge
|
|
26
|
-
| 一个值够不够 | `gauge` | `radialbar` | 单值叙事,勿与图并存 |
|
|
27
|
-
| 分布不是均值 | `boxplot` | `dotplot
|
|
28
|
-
| 双维定位 + 第三维权重 | `bubble` | `scatter` | 气泡 >8 → 标注关键项 |
|
|
29
|
-
| 六维能力画像 | `radar` | `rose
|
|
30
|
-
| 区间波动(开高低收) | `candlestick` | `line` | 蜡烛 >12 → 聚合 |
|
|
31
|
-
| 多项目完成度 | `progress` | `bullet` | 行 >6 → 拆页 |
|
|
32
|
-
| 迷你趋势(卡内) | `sparkline` | — | 只用于指标卡,不与大图并存 |
|
|
9
|
+
| 分析意图(读者要得出的结论) | 首选 | 还原度 | 次选 | 禁用 / 改判条件 |
|
|
10
|
+
|---------------------------|------|--------|------|----------------|
|
|
11
|
+
| 谁大谁小 | `hbar` | high | `lollipop` / `bar` | 类别 >8 → Top7 + 附录 |
|
|
12
|
+
| 随时间怎么变 | `line` | high | `area`(low)/ `dualline` | 时间点 >8 不标点值;PPTX 慎用 area |
|
|
13
|
+
| 占比是多少 | `donut` | high | `pie` / `multidonut` | 扇区 >5 → 合并"其他"或 `treemap`(low) |
|
|
14
|
+
| 每 1% 的直觉 | `waffle` | medium | `donut` | 类别 >3 改 `marimekko`(low) |
|
|
15
|
+
| 结构在变形 | `streamgraph` | **low** | `stackline`(high) | 系列 >6 合并;PPTX 优先 stackline |
|
|
16
|
+
| 总量 × 构成双编码 | `marimekko` | **low** | `treemap`(low) | 列 >6 / 段 >4 → 拆;PPTX 改 stack+table |
|
|
17
|
+
| 份额悬殊(头部集中) | `treemap` | **low** | `donut`(high) | 叶 >16 → 合并;PPTX 改 donut/hbar |
|
|
18
|
+
| 从 A 到 B 是谁贡献的 | `waterfall` | high | `pareto` | 段 >6 → 合并 |
|
|
19
|
+
| 主要矛盾是哪几个 | `pareto` | high | `waterfall` | 类别 >7 → 合并 |
|
|
20
|
+
| 转化/流失在哪一环 | `funnel` | medium | `sankey`(low) | 层 >5 → 改 `hbar` |
|
|
21
|
+
| 多对多的流向 | `sankey` | **low** | `network`(low) | 节点 >12 / 流带 >24 → 拆;PPTX 改 table |
|
|
22
|
+
| 谁和谁相连 | `network` | **low** | `diagram` | 节点 >18 / 边 >30 → 拆;PPTX 改 diagram |
|
|
23
|
+
| 两期谁升谁降 | `slope` | medium | `dumbbell` | 系列 >6 → 取 Top 6 |
|
|
24
|
+
| 单指标前后对比(多对象) | `dumbbell` | medium | `vsbar`(high) | 行 >6 → 拆页 |
|
|
25
|
+
| 实际 vs 目标(含区间) | `bulletchart` | medium | `bullet` | 行 >6 → 拆页 |
|
|
26
|
+
| 达成率(多指标同心) | `radialbar` | medium | `gauge`(high) | 环 >5 → 改 `bullet` |
|
|
27
|
+
| 一个值够不够 | `gauge` | high | `radialbar` | 单值叙事,勿与图并存 |
|
|
28
|
+
| 分布不是均值 | `boxplot` | **low** | `dotplot`(medium) | 组 >8 → 拆页;PPTX 改 dotplot/hbar |
|
|
29
|
+
| 双维定位 + 第三维权重 | `bubble` | high | `scatter` | 气泡 >8 → 标注关键项 |
|
|
30
|
+
| 六维能力画像 | `radar` | **low** | `rose`(medium) | 维度 >6 → 拆两组;PPTX 改 bar/rose |
|
|
31
|
+
| 区间波动(开高低收) | `candlestick` | medium | `line` | 蜡烛 >12 → 聚合 |
|
|
32
|
+
| 多项目完成度 | `progress` | high | `bullet` | 行 >6 → 拆页 |
|
|
33
|
+
| 迷你趋势(卡内) | `sparkline` | high | — | 只用于指标卡,不与大图并存 |
|
|
33
34
|
|
|
34
35
|
## 七种最常见的误用(出现即改判)
|
|
35
36
|
|
|
@@ -525,7 +525,7 @@ python scripts/prepare_images.py ./photos --layout grid --mode path # 大图
|
|
|
525
525
|
|
|
526
526
|
---
|
|
527
527
|
|
|
528
|
-
## 14. Agenda 多列(条目
|
|
528
|
+
## 14. Agenda 多列(条目 >8 个时)
|
|
529
529
|
|
|
530
530
|
```html
|
|
531
531
|
<ol class="agenda agenda--2col rv">
|
|
@@ -535,12 +535,13 @@ python scripts/prepare_images.py ./photos --layout grid --mode path # 大图
|
|
|
535
535
|
|
|
536
536
|
```css
|
|
537
537
|
@media (min-width:900px){
|
|
538
|
-
|
|
539
|
-
.agenda--2col
|
|
538
|
+
/* 列优先(先左列后右列),与 PPTX Agenda 列填充一致 */
|
|
539
|
+
.agenda--2col{display:block;column-count:2;column-gap:clamp(20px,3vw,48px)}
|
|
540
|
+
.agenda--2col .agenda__i{break-inside:avoid;border-top:1px solid var(--border-soft)}
|
|
540
541
|
}
|
|
541
542
|
```
|
|
542
543
|
|
|
543
|
-
> **阈值(layout-constants `contentQuality.agenda`)**:≤8 单列;**>8 须 `.agenda--2col
|
|
544
|
+
> **阈值(layout-constants `contentQuality.agenda`)**:≤8 单列;**>8 须 `.agenda--2col`(两列,列优先)**;>12 条 PPTX 自动分页(Agenda I/II);>16 拆「上篇/下篇」或只列一级章节。单条标题 ≤36 字(`titleMaxChars`,超长截断+全称沉 notes)。放不下先换列/分页,禁单列撑爆一屏。
|
|
544
545
|
> 条目仍偏高:压缩 `.agenda__a` 的 `padding`(如 `clamp(12px,1.6vh,18px)`)。
|
|
545
546
|
|
|
546
547
|
---
|
|
@@ -261,8 +261,10 @@ DGIG: 数据 = 能自我增强的生产资料
|
|
|
261
261
|
### Agenda 条目录入过多时
|
|
262
262
|
|
|
263
263
|
- ≤8 条:单列即可。
|
|
264
|
-
- 9–
|
|
264
|
+
- 9–12 条:**改 2 列**(列优先,与 PPTX 一致),或压缩 `agenda__a` 的 `padding`。
|
|
265
|
+
- 13–16 条:PPTX **自动分页**(Agenda I/II);HTML 仍双列,建议精炼到 12 条内或改章节级大纲。
|
|
265
266
|
- >16 条:拆「上篇/下篇」两个 Agenda 区,或只列一级章节、二级折叠。
|
|
267
|
+
- 单条标题 **≤36 字**(`titleMaxChars`):超长截断、全称沉演讲者备注。
|
|
266
268
|
|
|
267
269
|
---
|
|
268
270
|
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# 失败模式库(交付前自检)
|
|
2
2
|
|
|
3
|
-
> **何时读本文**:生成完成、跑完校验脚本后,若想确认"没有踩坑"
|
|
3
|
+
> **何时读本文**:生成完成、跑完校验脚本后,若想确认"没有踩坑",按本文**二十类**模式逐条对照;或当校验器报出某类问题时,查本文的**修复顺序铁律**(先改什么、不要改什么)。
|
|
4
4
|
> 本文与校验器一一对应:每类模式给出**识别信号 → 校验器错误码 → 修复动作**。
|
|
5
|
-
> F1–F14 为结构/导出/内容硬缺陷;F15–F16 为内容节奏与导出诚实性;F17 为 v9
|
|
5
|
+
> F1–F14 为结构/导出/内容硬缺陷;F15–F16 为内容节奏与导出诚实性;F17 为 v9 截图级缺陷;F18 为截断/溢出/半空卡;F19 为几何叠印/同槽双条(PPTX 导出硬伤);F20 为 Agenda 容量/图表退化/图标缺失。
|
|
6
6
|
|
|
7
|
-
##
|
|
7
|
+
## 一、二十类失败模式
|
|
8
8
|
|
|
9
9
|
### F1 内容不足 `underfill`
|
|
10
10
|
|
|
@@ -225,3 +225,29 @@
|
|
|
225
225
|
|
|
226
226
|
反模式:为稀疏 demo 感删证据 / so-what。原则见 `presentation-craft.md`「长文与信息承载」。
|
|
227
227
|
|
|
228
|
+
## F19 · 几何叠印 / 同槽双条 / 注释带侵入(v9.2 · PPTX 导出硬伤)
|
|
229
|
+
|
|
230
|
+
| 信号 | 错误码 | 修复 |
|
|
231
|
+
|------|--------|------|
|
|
232
|
+
| 元素两两重叠 ≥0.05in² | `ELEMENT_OVERLAP` | 错开坐标或合并为同一文本框;同页 >3 处=几何算法脱钩,需重构布局 |
|
|
233
|
+
| 架构节点标题/注解叠印 | (R1 根因) | 注解钳制在标题下边;节点高不足降级单框混排(双引擎已修,自定义几何勿回退) |
|
|
234
|
+
| verdict 与 soWhat 同页双填叠印 | `MODEL_VERDICT_SOWHAT_CONFLICT`(extract_model WARN) | **二选一**:comparison 填 `verdict` 就不要填 `soWhat`;渲染器自动去重但模型侧应只给一个 |
|
|
235
|
+
| 图例/类目标签压 so-what/来源行 | `ANNOTATION_BAND_OVERLAP` | 图例落图区底部(`bodyBottom-0.28`);类目标签含在图高 `h` 内;禁 `PH-0.62` 固定偏移 |
|
|
236
|
+
| `sec.note` 压 so-what 带 | (R2 根因) | `note` 与 `footnote` 共用注释带末行 `footnoteY=6.72`,同页自动合并;禁用 `note.y=6.55` |
|
|
237
|
+
| 版心填充 <55%(宽/高任一轴) | `LAYOUT_FILL` | 多栏按版心全宽推导列宽;低密度页补从件(指标/小图/对照表) |
|
|
238
|
+
|
|
239
|
+
**根因**:几何算法与内容量脱钩(固定偏移、单栏宽当总宽、同槽位无仲裁)。**门禁**:`validate_pptx.py --strict` 已含 `ELEMENT_OVERLAP` / `LAYOUT_FILL` / `ANNOTATION_BAND_OVERLAP`;`cross_verify` 比对 Agenda 阅读顺序、图片数、图表数据标签。
|
|
240
|
+
|
|
241
|
+
## F20 · Agenda 溢出 / 图表退化 / 图标缺失(v9.2 · 容量与跨通道)
|
|
242
|
+
|
|
243
|
+
| 信号 | 错误码 | 修复 |
|
|
244
|
+
|------|--------|------|
|
|
245
|
+
| Agenda >16 条撑爆一屏 | `Agenda 条数 ≤16`(validate_report) | 拆上/下篇或改章节级大纲 6–8 条;PPTX 已自动分页(>12 条) |
|
|
246
|
+
| Agenda 单条标题 >36 字 | `Agenda 单条标题 ≤36 字`(WARN) | 截断+全称沉 notes;或精炼标题 |
|
|
247
|
+
| Agenda 两通道阅读顺序不一致 | `Agenda序=DRIFT`(cross_verify) | 列优先(先左列后右列);HTML 用 `column-count`,PPTX 用列填充 |
|
|
248
|
+
| low 图型无数据表 | `图表 PPTX 还原度提示`(WARN) | area/radar/treemap/sankey/streamgraph/marimekko/boxplot/network 在 PPTX 显著退化——改 bar/line/hbar/donut 或 `dataTable=inline` |
|
|
249
|
+
| ≥2 卡且无 `.card__ico` | `要点/卡片页含 .card__ico`(WARN) | 从 `icons.md` 取语义图标(`render_from_model.py` 已内置 `card_head()` 自动出图标) |
|
|
250
|
+
| 图片占位标签 A/B 不同串 | `图片数=DRIFT` / 文本覆盖不足 | 占位标签由 `imageSpec` 同源拼接;`render_from_model` 与 PPTX `imgPlaceholderLabel` 必须同串 |
|
|
251
|
+
|
|
252
|
+
**根因**:页型容量契约缺失 + 图表双通道无还原度约定 + 图标/占位文本两处手写。**门禁**:`contentQuality.agenda`(singleMax/splitMax/titleMaxChars)、`charts.fidelityMap`、`cross_verify --full-ab` 三项一致性检查。
|
|
253
|
+
|
|
@@ -110,7 +110,7 @@
|
|
|
110
110
|
```
|
|
111
111
|
|
|
112
112
|
> 每栏要点 ≤4 条、同构对仗(同一维度正面 vs 反面);结论条默认**不写标签字样**(无「So what」/「结论」),靠 MD3 衬条强调;罕见需标签时用 `.sowhat--labeled` + (已弃用默认标签)。
|
|
113
|
-
> **PPTX 映射**:`comparison` 页型——`left/right = {title, points:[[k,v]…]}`,左 surface / 右 soft 面板 + `verdict`(结论条,accent
|
|
113
|
+
> **PPTX 映射**:`comparison` 页型——`left/right = {title, points:[[k,v]…]}`,左 surface / 右 soft 面板 + `verdict`(结论条,accent 实底)。**`verdict` 与 `soWhat` 互斥**(共用 annotation 结论条槽位,同页双填必叠印):comparison 填 `verdict` 时不要再填 `soWhat`;渲染器已自动二选一(verdict 优先),模型侧应只给一个。
|
|
114
114
|
|
|
115
115
|
---
|
|
116
116
|
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
| 论证结构 | 主张 → 证据 → 节奏休止 | **发现→证据→含义 + so-what**(每页都是论证) | 标题 → 图 → 图注 |
|
|
47
47
|
| 组件体系 | 通用组件 + 混排版式 A–F | **R1–R8 锁定版式** | **A1–A3 图示版式** |
|
|
48
48
|
| 节奏休止 | 金句页(全文 1–2 处 · 强调带) | R4 指标带(每 3–4 页一处) | 收尾极简页(强调带) |
|
|
49
|
-
| Agenda | 必备第二页 |
|
|
49
|
+
| Agenda | 必备第二页 | 必备(>8 条双列;>12 条自动分页) | 内容页 ≤4 可省 |
|
|
50
50
|
| 页头页脚 | eyebrow+标题+导语 | + so-what/来源行(仅关键页) | 极简(图例即页脚语义) |
|
|
51
51
|
| Exhibit 编号 | 不用 | **全篇连续编号 + 来源行** | 不用 |
|
|
52
52
|
| 引用密度 | 关键数字带 [n] | 关键数据 [n] + 文末参考资料 | 依据类 [1]–[3] |
|