@topmindspace/tms-skills 0.1.16 → 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 +47 -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
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,50 @@
|
|
|
1
|
+
## [0.1.18] - 2026-09-28
|
|
2
|
+
|
|
3
|
+
### PPTX 导出质量整改 · 双通道几何收敛 + 门禁盲区补齐
|
|
4
|
+
|
|
5
|
+
> 源起:2026-09-28 研究报告双通道交付复盘(HTML/PPTX),PPTX 有 4 类硬伤(含两处大面积文字叠印)、HTML 有 4 类空间利用问题、门禁全绿而效果不合格。本次按六类根因(R1–R7)系统整改。
|
|
6
|
+
|
|
7
|
+
#### A. 几何叠印硬伤(R1/R2 · PPTX 导出)
|
|
8
|
+
- **架构节点标题/注解叠印**:注解改为钳制在标题下边(原底对齐 `y=lh-0.5` 在 `lh≈0.59` 时与标题完全重合);节点高不足降级单框混排。双引擎同步。
|
|
9
|
+
- **verdict/soWhat 同槽叠印**:共用 annotation 结论条槽位——渲染器互斥(verdict 优先)、`extract_model` WARN、`model-schema` 加 `mutualExclusionHint`。HTML 同步去重。
|
|
10
|
+
- **图例压来源行**:图例改落图区底部(`bodyBottom-0.28`),禁 `PH-0.62` 固定偏移。
|
|
11
|
+
- **`sec.note` 压 so-what 带**:`note` 与 `footnote` 合并到 `footnoteY=6.72`,禁用 `note.y=6.55` 绘制。
|
|
12
|
+
- **图表类目标签越出图区**:标签带含在图高 `h` 内(原画到 `y+h` 外)。
|
|
13
|
+
- **图片底边侵入注释带**:有图注时下界收到 `contentBottomWithNote`。
|
|
14
|
+
|
|
15
|
+
#### B. 列宽 / 槽位几何(R3)
|
|
16
|
+
- **三栏只占半幅(栏宽 1.69in)**:`regionOf('twocol')` 单栏宽被当总宽——改用版心全宽推导。双引擎同步。
|
|
17
|
+
- **KPI 支撑指标铺满整页**:A 通道 `regOf('kpi','metrics')` 缺分支——补 dividerX 右侧分栏。
|
|
18
|
+
- **halftable 图表越过 so-what**:`regOf('halftable')` 忽略 opts.top/bottom——传导 opts。
|
|
19
|
+
|
|
20
|
+
#### C. Agenda 分页与列序统一(R4)
|
|
21
|
+
- >8 条自动双列(列优先);**>12 条自动分页**(Agenda I/II);长标题截断全称沉 notes(`titleMaxChars=36`)。双引擎 + HTML 同步。
|
|
22
|
+
|
|
23
|
+
#### D. 门禁盲区补齐(R6)
|
|
24
|
+
- **新增 `ELEMENT_OVERLAP`**(元素两两重叠 ≥0.05in²)与 **`LAYOUT_FILL`**(版心填充率 <55%)。
|
|
25
|
+
- **Agenda 容量契约**:条数 ≤16、标题 ≤36 字、>8 条须双列。
|
|
26
|
+
- **图表还原度分级**(`charts.fidelityMap`):low = area/radar/treemap/sankey/streamgraph/marimekko/boxplot/network;决策树标注 + 改判建议。
|
|
27
|
+
- **跨通道一致性**(`cross_verify`):Agenda 阅读顺序、图片数量、图表数据标签。
|
|
28
|
+
|
|
29
|
+
#### E. 图标与图片正确使用
|
|
30
|
+
- **图标**:`render_from_model` 内置语义图标库 + `card_head()`;PPTX 保持 accent 方块路标。
|
|
31
|
+
- **图片**:`r_image` 支持真图渲染、占位同串、`fit=contain`、多图版式(grid/compare/wall)。
|
|
32
|
+
|
|
33
|
+
#### 文档同步
|
|
34
|
+
- `page-type-matrix` 容量契约 + 还原度;`failure-modes` F19/F20;`pptx-export` 几何铁律;`chart-decision-tree` 还原度列;`content-rules` / `components-atoms` / `modes` / `layouts-combo` 契约更新。
|
|
35
|
+
- Pages 源同步:`docs/showcase.html` / `docs/style-gallery.html` 与技能侧 assets 副本逐字节一致(meta → v0.1.18)。
|
|
36
|
+
|
|
37
|
+
### 红线未动
|
|
38
|
+
反截断、图表多样性地板、Mode A craft、runtime SHA 同版本门禁、中文「演示文稿」术语、整仓 npm 发版、motion=none、结论条无「So what」标签、无左侧 accent 装饰轨、大纲 >8 → 2col。
|
|
39
|
+
|
|
40
|
+
## [0.1.17] - 2026-09-26
|
|
41
|
+
|
|
42
|
+
### Release
|
|
43
|
+
- Republish of the **0.1.16 craft** (结论条去左轨 · anti-AI-flavor · Showcase 全页刷新). npm registry left `0.1.16` in a staged/conflict state (`E409 previously staged`); content identical to the `v0.1.16` GitHub Release / docs Pages source.
|
|
44
|
+
|
|
45
|
+
### 红线未动
|
|
46
|
+
反截断、图表多样性地板、Mode A craft、runtime SHA 同版本门禁、中文「演示文稿」术语、整仓 npm 发版、motion=none、结论条无「So what」标签、无左侧 accent 装饰轨。
|
|
47
|
+
|
|
1
48
|
## [0.1.16] - 2026-09-26
|
|
2
49
|
|
|
3
50
|
### Craft · 结论条去 AI 味:取消左侧 accent 装饰轨
|
package/README.en.md
CHANGED
|
@@ -76,9 +76,9 @@ See [top-ppt-html/README.md](./top-ppt-html/README.md) and [SKILL.md](./top-ppt-
|
|
|
76
76
|
|
|
77
77
|
| Skill | Version | What it does |
|
|
78
78
|
|-------|---------|--------------|
|
|
79
|
-
| [`top-ppt-html`](./top-ppt-html/) | **0.1.
|
|
79
|
+
| [`top-ppt-html`](./top-ppt-html/) | **0.1.18** | Formal business decks: paginated HTML + editable 16:9 PPTX; dual delivery · MD3-inspired density; 3 modes × 9 styles; strict 0/0 |
|
|
80
80
|
|
|
81
|
-
Installer [`@topmindspace/tms-skills`](https://www.npmjs.com/package/@topmindspace/tms-skills) is also **0.1.
|
|
81
|
+
Installer [`@topmindspace/tms-skills`](https://www.npmjs.com/package/@topmindspace/tms-skills) is also **0.1.18** (whole-repo same-tag releases).
|
|
82
82
|
|
|
83
83
|
## Install
|
|
84
84
|
|
|
@@ -88,7 +88,7 @@ Installer [`@topmindspace/tms-skills`](https://www.npmjs.com/package/@topmindspa
|
|
|
88
88
|
npx @topmindspace/tms-skills list
|
|
89
89
|
npx @topmindspace/tms-skills install top-ppt-html
|
|
90
90
|
npx @topmindspace/tms-skills install top-ppt-html --to ./.claude/skills
|
|
91
|
-
npx @topmindspace/tms-skills@0.1.
|
|
91
|
+
npx @topmindspace/tms-skills@0.1.18 install top-ppt-html # pin
|
|
92
92
|
```
|
|
93
93
|
|
|
94
94
|
```bash
|
package/README.md
CHANGED
|
@@ -74,9 +74,9 @@
|
|
|
74
74
|
|
|
75
75
|
| 技能 | 版本 | 做什么 |
|
|
76
76
|
|------|------|--------|
|
|
77
|
-
| [`top-ppt-html`](./top-ppt-html/) | **0.1.
|
|
77
|
+
| [`top-ppt-html`](./top-ppt-html/) | **0.1.18** | 正式商务演示:HTML 可翻页 + 16:9 可编辑 PPTX;双交付 · MD3 密度克制;三模式 × 九风格;strict 0/0 |
|
|
78
78
|
|
|
79
|
-
安装器 [`@topmindspace/tms-skills`](https://www.npmjs.com/package/@topmindspace/tms-skills) 同为 **0.1.
|
|
79
|
+
安装器 [`@topmindspace/tms-skills`](https://www.npmjs.com/package/@topmindspace/tms-skills) 同为 **0.1.18**(整仓同 tag 发版)。
|
|
80
80
|
|
|
81
81
|
## 安装
|
|
82
82
|
|
|
@@ -86,7 +86,7 @@
|
|
|
86
86
|
npx @topmindspace/tms-skills list
|
|
87
87
|
npx @topmindspace/tms-skills install top-ppt-html
|
|
88
88
|
npx @topmindspace/tms-skills install top-ppt-html --to ./.claude/skills
|
|
89
|
-
npx @topmindspace/tms-skills@0.1.
|
|
89
|
+
npx @topmindspace/tms-skills@0.1.18 install top-ppt-html # 钉版本
|
|
90
90
|
```
|
|
91
91
|
|
|
92
92
|
```bash
|
package/package.json
CHANGED
|
@@ -18,7 +18,7 @@ Plenty of PPT skills exist. This one is for **formal business presenting**: clea
|
|
|
18
18
|
</p>
|
|
19
19
|
|
|
20
20
|
- Skill id: `top-ppt-html`; brand: **TopPPT HTML**
|
|
21
|
-
- Version: **v0.1.
|
|
21
|
+
- Version: **v0.1.18** (same tag as `@topmindspace/tms-skills@0.1.18`)
|
|
22
22
|
- **Agent entry**: `SKILL.md` → `references/playbook.md` (L1) → L2 on demand
|
|
23
23
|
- **Human maintainers**: this README (install / commands / layout); do not treat it as the generation spec
|
|
24
24
|
|
|
@@ -73,7 +73,7 @@ Also: arrow-key paging; **Esc** closes modals. After style/theme change, re-run
|
|
|
73
73
|
|
|
74
74
|
```bash
|
|
75
75
|
npx @topmindspace/tms-skills install top-ppt-html
|
|
76
|
-
npx @topmindspace/tms-skills@0.1.
|
|
76
|
+
npx @topmindspace/tms-skills@0.1.18 install top-ppt-html
|
|
77
77
|
npx github:topmindspace/tms-skills install top-ppt-html
|
|
78
78
|
```
|
|
79
79
|
|
package/top-ppt-html/README.md
CHANGED
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
</p>
|
|
19
19
|
|
|
20
20
|
- 技能标识:`top-ppt-html`;品牌名:**TopPPT HTML**
|
|
21
|
-
- 版本:**v0.1.
|
|
21
|
+
- 版本:**v0.1.18**(与 `@topmindspace/tms-skills@0.1.18` 同 tag)
|
|
22
22
|
- **智能体入口**:`SKILL.md` → `references/playbook.md`(L1)→ L2 按需
|
|
23
23
|
- **人类维护者**:本 README(安装 / 命令 / 目录);勿把本文件当生成规范
|
|
24
24
|
|
package/top-ppt-html/SKILL.md
CHANGED
|
@@ -4,7 +4,7 @@ description: "Use when 用户要做报告、演示、汇报、PPT、slides、dec
|
|
|
4
4
|
license: MIT
|
|
5
5
|
compatibility: "Python 3 stdlib for HTML generation; Node >=18 + pptxgenjs for PPTX; optional playwright for browser regression / theme captures."
|
|
6
6
|
metadata:
|
|
7
|
-
version: "0.1.
|
|
7
|
+
version: "0.1.18"
|
|
8
8
|
author: TopMindspace
|
|
9
9
|
---
|
|
10
10
|
# TopPPT HTML
|
|
@@ -132,7 +132,7 @@ metadata:
|
|
|
132
132
|
|
|
133
133
|
## 版本口径
|
|
134
134
|
|
|
135
|
-
包 semver(现 0.1.
|
|
135
|
+
包 semver(现 0.1.18)≠ schema 线(layout-constants / model-schema,现 `0.1`);patch 不抬 schema。
|
|
136
136
|
|
|
137
137
|
## 环境依赖
|
|
138
138
|
|
|
@@ -817,8 +817,9 @@ color:var(--accent);font-variant-numeric:tabular-nums;line-height:1;min-width:2.
|
|
|
817
817
|
.agenda__a:hover .agenda__go{color:var(--accent);transform:translateX(4px)}
|
|
818
818
|
.agenda__go svg{width:20px;height:20px;display:block}
|
|
819
819
|
@media (min-width:900px){
|
|
820
|
-
|
|
821
|
-
.agenda--2col
|
|
820
|
+
/* R4:列优先(先左列后右列),与 PPTX Agenda 列填充一致——grid 行优先会打乱阅读顺序 */
|
|
821
|
+
.agenda--2col{display:block;column-count:2;column-gap:clamp(20px,3vw,48px)}
|
|
822
|
+
.agenda--2col .agenda__i{break-inside:avoid;border-top:1px solid var(--border-soft)}}
|
|
822
823
|
/* ── 页码指示(翻页导航) ── */
|
|
823
824
|
.pager{position:fixed;right:clamp(10px,1.6vw,26px);top:50%;transform:translateY(-50%);
|
|
824
825
|
z-index:60;display:flex;flex-direction:column;align-items:center;gap:10px}
|
|
@@ -1993,7 +1994,7 @@ window.REPORT_MODEL = {
|
|
|
1993
1994
|
|
|
1994
1995
|
/* __TOPPPT_RUNTIME_START__ */
|
|
1995
1996
|
/* ══ PPTX 导出运行时(assets/pptx-export.js 的内联副本 · 由 scripts/sync_runtime.py 注入,禁止手改) ══ */
|
|
1996
|
-
/* __TOPPPT_RUNTIME_SHA__:
|
|
1997
|
+
/* __TOPPPT_RUNTIME_SHA__:73cff2ca1dbc78a8 */
|
|
1997
1998
|
/* ═══════════════════════════════════════════════════════════════════════════
|
|
1998
1999
|
* TopPPT HTML· 浏览器端 PPTX 预览运行时
|
|
1999
2000
|
* 零依赖、随报告单文件分发;OOXML 序列化器(仅供预览,页面不导出文件)。
|
|
@@ -2152,6 +2153,15 @@ function regionOf(pageType, slotId, opts) {
|
|
|
2152
2153
|
return { x: MX, y: P.heroY || 2.9, w: CW * (P.heroW || 0.52), h: P.heroH || 1.6,
|
|
2153
2154
|
dividerX: P.dividerX, metricY0: P.metricY0, metricRowH: P.metricRowH };
|
|
2154
2155
|
}
|
|
2156
|
+
/* R3:kpi 支撑指标列——落在 dividerX 右侧(默认全宽返回会压住 hero) */
|
|
2157
|
+
if (type === 'kpi' && slot === 'metrics') {
|
|
2158
|
+
var dxK = P.dividerX != null ? P.dividerX : 0.56;
|
|
2159
|
+
return {
|
|
2160
|
+
x: MX + CW * dxK + 0.35, y: P.metricY0 || 2.95,
|
|
2161
|
+
w: CW * (1 - dxK) - 0.5,
|
|
2162
|
+
h: Math.max(0.6, bottomDefault - (P.metricY0 || 2.95)),
|
|
2163
|
+
};
|
|
2164
|
+
}
|
|
2155
2165
|
if (type === 'comparison' && (slot === 'left' || slot === 'primary' || slot === 'right')) {
|
|
2156
2166
|
var cg = P.panelGap || 0.4, pw = (CW - cg) / 2;
|
|
2157
2167
|
if (slot === 'right') return { x: MX + pw + cg, y: P.panelY || 2.55, w: pw, h: P.panelH || 3.4 };
|
|
@@ -2184,15 +2194,22 @@ function regionOf(pageType, slotId, opts) {
|
|
|
2184
2194
|
}
|
|
2185
2195
|
if (type === 'image' && slot === 'primary') {
|
|
2186
2196
|
var iy2 = Math.max(P.y || 2.3, opts.top || 0);
|
|
2187
|
-
|
|
2197
|
+
/* R2:有图注时图片不得越过 contentBottomWithNote(图注落注释带,属 annotation self) */
|
|
2198
|
+
var iBottom = (opts.bottom != null) ? opts.bottom
|
|
2199
|
+
: (opts.caption ? (L.contentBottomWithNote || 6.4) : bottomDefault);
|
|
2200
|
+
return { x: MX, y: iy2, w: CW, h: Math.max(1.2, Math.min(P.h || 4.2, iBottom - iy2)) };
|
|
2188
2201
|
}
|
|
2189
2202
|
if ((type === 'halftable') && (slot === 'left' || slot === 'primary')) {
|
|
2190
2203
|
var rs = PT.research || {};
|
|
2191
|
-
|
|
2204
|
+
var ht = opts.top != null ? opts.top : topDefault;
|
|
2205
|
+
var hb = opts.bottom != null ? opts.bottom : bottomDefault;
|
|
2206
|
+
return { x: MX, y: ht, w: CW * (rs.halfTableW || 0.52), h: Math.max(1, hb - ht) };
|
|
2192
2207
|
}
|
|
2193
2208
|
if ((type === 'halftable') && (slot === 'right' || slot === 'secondary')) {
|
|
2194
2209
|
var rs2 = PT.research || {};
|
|
2195
|
-
|
|
2210
|
+
var ht2 = opts.top != null ? opts.top : topDefault;
|
|
2211
|
+
var hb2 = opts.bottom != null ? opts.bottom : bottomDefault;
|
|
2212
|
+
return { x: MX + CW * (rs2.halfChartX || 0.57), y: ht2, w: CW * (rs2.halfChartW || 0.43), h: Math.max(1, hb2 - ht2) };
|
|
2196
2213
|
}
|
|
2197
2214
|
if (type === 'matrix' && slot === 'primary') {
|
|
2198
2215
|
var rm = PT.research || {};
|
|
@@ -2213,7 +2230,7 @@ function regionOf(pageType, slotId, opts) {
|
|
|
2213
2230
|
/* __TOPPPT_SCHEMA_START__ */
|
|
2214
2231
|
/* ── 由 scripts/sync_runtime.py 从 scripts/model-schema.json 注入 · 禁止手改 ── */
|
|
2215
2232
|
/* DSL schema 单源:浏览器端 validateModel 与 scripts/extract_model.py 消费同一份定义 */
|
|
2216
|
-
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"]}}};
|
|
2233
|
+
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"]}}};
|
|
2217
2234
|
/* __TOPPPT_SCHEMA_END__ */
|
|
2218
2235
|
|
|
2219
2236
|
var _mode = 'presentation'; /* 当前模式(slidesOf 里按 model.mode 设置) */
|
|
@@ -2504,6 +2521,15 @@ function footnoteLine(sh, text, S) {
|
|
|
2504
2521
|
sh.push(txSp(R.x, R.y, R.w, R.h,
|
|
2505
2522
|
[[{ t: text, sz: 9.5, col: S.faint, font: S.font }]]));
|
|
2506
2523
|
}
|
|
2524
|
+
/* R2:sec.note 与 sec.footnote 共用注释带末行(footnoteY=6.72)。
|
|
2525
|
+
note.y=6.55 会压进 so-what 带(6.05–6.67)——同页二者必须合并为一行。 */
|
|
2526
|
+
function noteOrFootnote(sh, sec, S) {
|
|
2527
|
+
var parts = [];
|
|
2528
|
+
if (sec && sec.note) parts.push(String(sec.note));
|
|
2529
|
+
if (sec && sec.footnote) parts.push(String(sec.footnote));
|
|
2530
|
+
if (!parts.length) return;
|
|
2531
|
+
footnoteLine(sh, parts.join(' · '), S);
|
|
2532
|
+
}
|
|
2507
2533
|
/* 待核实清单条(accent 强调色标注 · 与 build_pptx.js / HTML .flagbar 同源) */
|
|
2508
2534
|
function flagBar(sh, items, S, yTop) {
|
|
2509
2535
|
var F = PT.flagbar || { hdH: 0.26, rowH: 0.24, maxRows: 3, gap: 0.12 };
|
|
@@ -2692,7 +2718,9 @@ function vbarShapes(sh, ec, S, x, y, w, h, dcols) {
|
|
|
2692
2718
|
: [{ name: '数值', values: ec.values || [] }];
|
|
2693
2719
|
var nb = Math.max(1, labels.length);
|
|
2694
2720
|
var ns = Math.max(1, series.length);
|
|
2695
|
-
|
|
2721
|
+
/* R2:类目标签带必须含在 h 内(原 baseY+0.08 画在图区外,会压进 so-what/来源行) */
|
|
2722
|
+
var labelH = 0.36;
|
|
2723
|
+
var baseY = y + Math.max(0.6, h - labelH), plotW = w, step = plotW / nb;
|
|
2696
2724
|
var cluster = Math.min(1.1, step * (ns > 1 ? 0.78 : 0.55));
|
|
2697
2725
|
var bw = cluster / ns;
|
|
2698
2726
|
var all = [];
|
|
@@ -2701,26 +2729,27 @@ function vbarShapes(sh, ec, S, x, y, w, h, dcols) {
|
|
|
2701
2729
|
});
|
|
2702
2730
|
var vmax = ec.max || Math.max.apply(null, all.concat([1]));
|
|
2703
2731
|
var top = all.length ? Math.max.apply(null, all) : 0;
|
|
2732
|
+
var plotH = Math.max(0.35, baseY - y);
|
|
2704
2733
|
sh.push(shape('rect', x, baseY, plotW, 0.02, S.line));
|
|
2705
2734
|
series.forEach(function (se, si) {
|
|
2706
2735
|
var vals = se.values || [];
|
|
2707
2736
|
var col = dcols ? dcols[si % 5] : (si === 0 ? S.accent : S.faint);
|
|
2708
2737
|
vals.forEach(function (v, i) {
|
|
2709
2738
|
var num = Number(v) || 0;
|
|
2710
|
-
var hh = Math.max(0.05, num / vmax * (
|
|
2739
|
+
var hh = Math.max(0.05, num / vmax * (plotH - 0.35));
|
|
2711
2740
|
var bx = x + i * step + (step - cluster) / 2 + si * bw;
|
|
2712
2741
|
var hot = ns === 1 && num === top;
|
|
2713
2742
|
sh.push(shape('roundRect', bx, baseY - hh, Math.max(0.04, bw * 0.92), hh,
|
|
2714
2743
|
hot ? (dcols ? dcols[i % 5] : S.accent) : col, { adj: 6000 }));
|
|
2715
2744
|
if (ns === 1) {
|
|
2716
|
-
sh.push(txSp(bx - 0.3, baseY - hh - 0.
|
|
2745
|
+
sh.push(txSp(bx - 0.3, baseY - hh - 0.36, bw + 0.6, 0.30,
|
|
2717
2746
|
[[{ t: valText(num, ec.unit), sz: 12.5, b: 1, col: hot ? S.accent : S.body, font: S.font }]], { align: 'ctr' }));
|
|
2718
2747
|
}
|
|
2719
2748
|
});
|
|
2720
2749
|
});
|
|
2721
2750
|
labels.forEach(function (lb, i) {
|
|
2722
2751
|
var lx = x + i * step;
|
|
2723
|
-
sh.push(txSp(lx, baseY + 0.
|
|
2752
|
+
sh.push(txSp(lx, baseY + 0.06, step, labelH - 0.08,
|
|
2724
2753
|
[[{ t: lb, sz: 10.5, col: S.faint, font: S.font }]], { align: 'ctr' }));
|
|
2725
2754
|
});
|
|
2726
2755
|
/* Multi-series legend along top of plot (keeps category labels; avoids blank chart). */
|
|
@@ -2743,7 +2772,10 @@ function lineShapes(sh, ec, S, x, y, w, h, dcols) {
|
|
|
2743
2772
|
var all = [];
|
|
2744
2773
|
series.forEach(function (se) { (se.values || []).forEach(function (v) { all.push(Number(v) || 0); }); });
|
|
2745
2774
|
var vmax = ec.max || Math.max.apply(null, all.concat([1]));
|
|
2746
|
-
|
|
2775
|
+
/* R2:类目标签带含在 h 内,禁画到图区外压 so-what */
|
|
2776
|
+
var labelH = 0.36;
|
|
2777
|
+
var baseY = y + Math.max(0.6, h - labelH);
|
|
2778
|
+
var plotH = Math.max(0.35, baseY - y - 0.2);
|
|
2747
2779
|
sh.push(shape('rect', x, baseY, w, 0.02, S.line));
|
|
2748
2780
|
var stepX = n > 1 ? w / (n - 1) : 0;
|
|
2749
2781
|
series.forEach(function (se, si) {
|
|
@@ -2763,7 +2795,7 @@ function lineShapes(sh, ec, S, x, y, w, h, dcols) {
|
|
|
2763
2795
|
});
|
|
2764
2796
|
labels.forEach(function (lb, i) {
|
|
2765
2797
|
var px = x + (n > 1 ? i * stepX : w / 2);
|
|
2766
|
-
sh.push(txSp(px - 0.5, baseY + 0.
|
|
2798
|
+
sh.push(txSp(px - 0.5, baseY + 0.06, 1.0, labelH - 0.08,
|
|
2767
2799
|
[[{ t: lb, sz: 10.5, col: S.faint, font: S.font }]], { align: 'ctr' }));
|
|
2768
2800
|
});
|
|
2769
2801
|
}
|
|
@@ -3215,25 +3247,44 @@ function slidesOf(model, S) {
|
|
|
3215
3247
|
c.push(txSp(MX, CV.subtitleY, CW - 1, CV.subtitleH, [[{ t: model.subtitle || '', sz: 18, col: S.soft, font: S.font }]]));
|
|
3216
3248
|
slides.push(c);
|
|
3217
3249
|
|
|
3218
|
-
/* Agenda(>8
|
|
3250
|
+
/* Agenda(>8 条自动双列;>12 条自动分页;长标题截断+全称入 notes;列优先与 HTML 一致) */
|
|
3219
3251
|
var items = model.agenda || [];
|
|
3220
3252
|
if (items.length) {
|
|
3221
|
-
var
|
|
3222
|
-
|
|
3223
|
-
var
|
|
3224
|
-
|
|
3225
|
-
|
|
3226
|
-
|
|
3227
|
-
|
|
3228
|
-
|
|
3229
|
-
var
|
|
3230
|
-
var
|
|
3231
|
-
|
|
3232
|
-
|
|
3233
|
-
|
|
3234
|
-
|
|
3253
|
+
var ROWS_PER_PAGE = 12;
|
|
3254
|
+
var agPages = [];
|
|
3255
|
+
for (var ai = 0; ai < items.length; ai += ROWS_PER_PAGE) agPages.push(items.slice(ai, ai + ROWS_PER_PAGE));
|
|
3256
|
+
agPages.forEach(function (pageItems, pi) {
|
|
3257
|
+
var ag = [];
|
|
3258
|
+
var sub = agPages.length > 1 ? '(' + (pi + 1) + '/' + agPages.length + ')' : '';
|
|
3259
|
+
head(ag, 'AGENDA', '报告大纲' + sub);
|
|
3260
|
+
var colN = pageItems.length > 8 ? 2 : 1;
|
|
3261
|
+
var perCol = Math.ceil(pageItems.length / colN) || 1;
|
|
3262
|
+
var rowH = Math.min(1.05, (CONTENT_BOTTOM - CONTENT_TOP - 0.25) / Math.max(1, perCol));
|
|
3263
|
+
var numSz = rowH >= 0.85 ? 30 : (rowH >= 0.65 ? 22 : 18);
|
|
3264
|
+
var tSz = rowH >= 0.85 ? 16 : 14, dSz = rowH >= 0.85 ? 12 : 11;
|
|
3265
|
+
var agNotes = [];
|
|
3266
|
+
pageItems.forEach(function (it, i) {
|
|
3267
|
+
var col = Math.floor(i / perCol), row = i % perCol;
|
|
3268
|
+
var x = MX + col * (CW / colN), y = CONTENT_TOP + row * rowH;
|
|
3269
|
+
var titleBoxW = CW / colN - 1.15;
|
|
3270
|
+
var rawTitle = String(it[1] || '');
|
|
3271
|
+
var titleEstW = estTextH(rawTitle, titleBoxW, tSz, 1.2);
|
|
3272
|
+
var maxTitleH = rowH * 0.55;
|
|
3273
|
+
var title = rawTitle;
|
|
3274
|
+
if (titleEstW > maxTitleH) {
|
|
3275
|
+
var maxChars = Math.max(10, Math.floor(rawTitle.length * (maxTitleH / titleEstW)));
|
|
3276
|
+
if (rawTitle.length > maxChars) {
|
|
3277
|
+
title = rawTitle.slice(0, maxChars - 1) + '…';
|
|
3278
|
+
agNotes.push('条目 ' + it[0] + ' 全称:' + rawTitle);
|
|
3279
|
+
}
|
|
3280
|
+
}
|
|
3281
|
+
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 }]]));
|
|
3282
|
+
ag.push(txSp(x + 1.0, y + 0.03, titleBoxW, Math.min(0.95, rowH),
|
|
3283
|
+
[[{ t: title, sz: tSz, b: 1, col: S.ink, font: S.font }],
|
|
3284
|
+
[{ t: it[2] || '', sz: dSz, col: S.body, font: S.font }]], { lineSpacing: tSz + 5 }));
|
|
3285
|
+
});
|
|
3286
|
+
slides.push(ag);
|
|
3235
3287
|
});
|
|
3236
|
-
slides.push(ag);
|
|
3237
3288
|
}
|
|
3238
3289
|
|
|
3239
3290
|
/* 章节页 */
|
|
@@ -3319,12 +3370,13 @@ function slidesOf(model, S) {
|
|
|
3319
3370
|
{ t: p[1], sz: fz - 1, col: S.body, font: S.font }]]));
|
|
3320
3371
|
});
|
|
3321
3372
|
});
|
|
3373
|
+
/* R2:verdict 与 soWhat 共用 annotation 槽位——只画一个(verdict 优先) */
|
|
3322
3374
|
if (sec.verdict) {
|
|
3323
3375
|
var vR = regOf('exhibit', 'annotation') || { x: MX, y: PT.exhibit.soWhatY, w: CW, h: 0.62 };
|
|
3324
|
-
sh.push(shape('rect', vR.x, vR.y, vR.w, vR.h, S.accent));
|
|
3376
|
+
sh.push(shape('rect', vR.x, vR.y, vR.w, vR.h, S.accent, { name: 'tr:soWhat' }));
|
|
3325
3377
|
sh.push(txSp(vR.x + 0.22, vR.y + 0.06, vR.w - 0.44, vR.h - 0.12,
|
|
3326
3378
|
[[{ t: '结论 ', sz: 11, b: 1, col: S.onAccent, font: S.font, spc: 150 },
|
|
3327
|
-
{ t: sec.verdict, sz: 12.5, b: 1, col: S.onAccent, font: S.font }]], { anchor: 'ctr' }));
|
|
3379
|
+
{ t: sec.verdict, sz: 12.5, b: 1, col: S.onAccent, font: S.font }]], { anchor: 'ctr', name: 'tr:soWhat' }));
|
|
3328
3380
|
}
|
|
3329
3381
|
} else if (type === 'quote') {
|
|
3330
3382
|
var Q = PT.quote;
|
|
@@ -3357,7 +3409,6 @@ function slidesOf(model, S) {
|
|
|
3357
3409
|
} else if (type === 'donut') {
|
|
3358
3410
|
var dcn = sec.chart || {};
|
|
3359
3411
|
if (dcn.labels && dcn.values) donutChart(sh, dcn, S, dcn.colors || donutColors(model.style, S, model.theme), bodyBottom);
|
|
3360
|
-
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 }]]));
|
|
3361
3412
|
} else if (type === 'heatmap') {
|
|
3362
3413
|
var H = PT.heatmap;
|
|
3363
3414
|
var hR = regOf('heatmap', 'primary', { bottom: bodyBottom }) || { x: MX, y: H.y, w: CW, h: bodyBottom - H.y };
|
|
@@ -3504,7 +3555,7 @@ function slidesOf(model, S) {
|
|
|
3504
3555
|
} else if (type === 'bar') {
|
|
3505
3556
|
var ch = sec.chart || {};
|
|
3506
3557
|
var dcols2 = (ch.colors || dataColors(model.style, model.theme));
|
|
3507
|
-
var botY = chartBottom(!!sec.soWhat, !!sec.footnote
|
|
3558
|
+
var botY = chartBottom(!!sec.soWhat, !!(sec.footnote || sec.note)) - 0.05;
|
|
3508
3559
|
var isHBar = ch.type === 'hbar';
|
|
3509
3560
|
var bRR = regOf('bar', isHBar ? 'hbar' : 'primary', { bottom: botY }) ||
|
|
3510
3561
|
{ x: isHBar ? MX : MX + PT.bar.chartX, y: isHBar ? PT.bar.hbarY0 : PT.bar.chartY,
|
|
@@ -3531,13 +3582,12 @@ function slidesOf(model, S) {
|
|
|
3531
3582
|
} else {
|
|
3532
3583
|
chartBlockShapes(sh, ch, S, bx, bRR.y, bw, Math.max(1.5, bRR.h), dcols2);
|
|
3533
3584
|
}
|
|
3534
|
-
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 }]]));
|
|
3535
3585
|
} else if (type === 'twocol' || type === 'threecol') {
|
|
3536
3586
|
var ps2 = sec.paragraphs || [];
|
|
3537
3587
|
var nCol = (type === 'threecol') ? 3 : 2;
|
|
3538
3588
|
var gapC = (type === 'threecol') ? PT.research.col3Gap : PT.twocol.colGap;
|
|
3539
|
-
|
|
3540
|
-
|
|
3589
|
+
/* R3:多栏均分用版心全宽(regOf('twocol') 返回单栏宽,当总宽会压扁三栏) */
|
|
3590
|
+
var tcR = { x: MX, y: bodyY, w: CW, h: bodyBottom - bodyY };
|
|
3541
3591
|
var gC = cols(tcR.w, nCol, gapC);
|
|
3542
3592
|
var perC = Math.ceil(ps2.length / nCol);
|
|
3543
3593
|
var availC = tcR.h;
|
|
@@ -3733,7 +3783,7 @@ function slidesOf(model, S) {
|
|
|
3733
3783
|
var dStart = dgR.y;
|
|
3734
3784
|
var dEnd = dgR.y + dgR.h;
|
|
3735
3785
|
var nLay = Math.max(1, lys.length);
|
|
3736
|
-
var lh2 = Math.max(0.
|
|
3786
|
+
var lh2 = Math.max(0.5, Math.min(D.maxLayerH, (dEnd - dStart) / nLay - D.layerGap));
|
|
3737
3787
|
lys.forEach(function (lay, li) {
|
|
3738
3788
|
var y9 = dStart + li * (lh2 + D.layerGap);
|
|
3739
3789
|
var focus = lay[2] === 'focus';
|
|
@@ -3749,9 +3799,26 @@ function slidesOf(model, S) {
|
|
|
3749
3799
|
var ntt = isRec(nd) ? nd.t : nd;
|
|
3750
3800
|
var nds = isRec(nd) ? (nd.d || '') : '';
|
|
3751
3801
|
sh.push(shape('roundRect', x10, y9, nw2, lh2, ndacc ? S.soft : S.surface, { adj: 4000, line: S.line }));
|
|
3752
|
-
|
|
3753
|
-
|
|
3754
|
-
|
|
3802
|
+
/* R1: 注解不得「浮回」标题上。标题顶对齐,注解钳在标题下边;空间不足降级单框混排。 */
|
|
3803
|
+
var padXn = 0.12, padYn = 0.10;
|
|
3804
|
+
var innerWn = nw2 - padXn * 2;
|
|
3805
|
+
var titleHn = 0.32, noteHn = 0.34, gapTNn = 0.04;
|
|
3806
|
+
var needHn = titleHn + gapTNn + (nds ? noteHn : 0) + padYn * 2;
|
|
3807
|
+
if (!nds || lh2 >= needHn) {
|
|
3808
|
+
var tYn = y9 + padYn;
|
|
3809
|
+
sh.push(txSp(x10 + padXn, nds ? tYn : y9 + lh2 / 2 - 0.18, innerWn, titleHn,
|
|
3810
|
+
[[{ t: ntt, sz: nds ? 11 : 12.5, b: 1, col: ndacc ? S.accent : S.ink, font: S.font }]],
|
|
3811
|
+
{ anchor: 'ctr' }));
|
|
3812
|
+
if (nds) {
|
|
3813
|
+
var nYn = Math.max(tYn + titleHn + gapTNn, y9 + lh2 - padYn - noteHn);
|
|
3814
|
+
sh.push(txSp(x10 + padXn, nYn, innerWn, Math.min(noteHn, y9 + lh2 - padYn - nYn),
|
|
3815
|
+
[[{ t: nds, sz: 10, col: S.faint, font: S.font }]], { anchor: 't' }));
|
|
3816
|
+
}
|
|
3817
|
+
} else {
|
|
3818
|
+
sh.push(txSp(x10 + padXn, y9 + padYn * 0.5, innerWn, lh2 - padYn,
|
|
3819
|
+
[[{ t: ntt, sz: 11, b: 1, col: ndacc ? S.accent : S.ink, font: S.font }],
|
|
3820
|
+
[{ t: nds, sz: 9, col: S.faint, font: S.font }]], { anchor: 't', lineSpacing: 12 }));
|
|
3821
|
+
}
|
|
3755
3822
|
});
|
|
3756
3823
|
if (li < lys.length - 1) {
|
|
3757
3824
|
var cy2 = y9 + lh2 + D.layerGap / 2;
|
|
@@ -3761,10 +3828,14 @@ function slidesOf(model, S) {
|
|
|
3761
3828
|
}
|
|
3762
3829
|
});
|
|
3763
3830
|
if (sec.legend) {
|
|
3764
|
-
|
|
3765
|
-
|
|
3766
|
-
|
|
3767
|
-
|
|
3831
|
+
/* R2:图例进图区底部(与 HTML .arch__legend 同源),禁用 PH-0.62 固定偏移 */
|
|
3832
|
+
var lgH2 = 0.28;
|
|
3833
|
+
var lgY2 = Math.max(dStart, bodyBottom - lgH2 - 0.02);
|
|
3834
|
+
var nLg2 = Math.max(1, sec.legend.length);
|
|
3835
|
+
var step2 = Math.min(2.4, CW / nLg2);
|
|
3836
|
+
sec.legend.forEach(function (lg2, li2) {
|
|
3837
|
+
sh.push(txSp(MX + CW - nLg2 * step2 + li2 * step2, lgY2, step2 - 0.05, lgH2,
|
|
3838
|
+
[[{ t: '● ' + lg2, sz: 10, col: S.faint, font: S.font }]]));
|
|
3768
3839
|
});
|
|
3769
3840
|
}
|
|
3770
3841
|
} else if (type === 'exhibit') {
|
|
@@ -3821,8 +3892,10 @@ function slidesOf(model, S) {
|
|
|
3821
3892
|
}
|
|
3822
3893
|
}
|
|
3823
3894
|
/* research 通用可选件:so-what 结论条 + 页脚来源行 + 待核实条(共用同一槽位) */
|
|
3824
|
-
|
|
3825
|
-
if (sec.
|
|
3895
|
+
/* R2:verdict 已占 annotation 槽位时跳过 soWhat,禁止同槽双条叠印 */
|
|
3896
|
+
if (sec.soWhat && !sec.verdict) soWhatBar(sh, sec.soWhat, S);
|
|
3897
|
+
/* R2:note 与 footnote 共用注释带末行(禁 note.y=6.55 压 so-what) */
|
|
3898
|
+
noteOrFootnote(sh, sec, S);
|
|
3826
3899
|
if (flagH) flagBar(sh, flagItems, S, flagY);
|
|
3827
3900
|
slides.push(sh);
|
|
3828
3901
|
});
|
|
@@ -3992,9 +4065,10 @@ function validateModel(model) {
|
|
|
3992
4065
|
}
|
|
3993
4066
|
var agMax = (M.model && M.model.agendaComfortMax) || 16;
|
|
3994
4067
|
if ((model.agenda || []).length > agMax) {
|
|
3995
|
-
warnings.push('agenda ' + model.agenda.length + ' 条超出单页舒适上限 ' + agMax + '
|
|
4068
|
+
warnings.push('agenda ' + model.agenda.length + ' 条超出单页舒适上限 ' + agMax + ',将自动分页');
|
|
3996
4069
|
}
|
|
3997
|
-
var
|
|
4070
|
+
var agPageCount = hasAgenda ? Math.ceil(model.agenda.length / 12) : 0;
|
|
4071
|
+
var pages = 1 + agPageCount + secs.length + 1; /* 封面+大纲(可多页)+章节+收尾 */
|
|
3998
4072
|
return { ok: missing.length === 0, missing: missing, warnings: warnings, pages: pages };
|
|
3999
4073
|
}
|
|
4000
4074
|
|