@lemonppt/renderer 0.2.0 → 0.2.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.
Files changed (72) hide show
  1. package/dist/client/client-render.js +65 -5401
  2. package/dist/client/editor-script.js +3355 -0
  3. package/dist/client/theme-echarts.js +65 -25
  4. package/dist/client-render.d.ts +1 -7
  5. package/dist/client-render.d.ts.map +1 -1
  6. package/dist/client-render.js +1 -11
  7. package/dist/client-render.js.map +1 -1
  8. package/dist/editor-script.d.ts +1 -1
  9. package/dist/editor-script.d.ts.map +1 -1
  10. package/dist/editor-script.js +80 -77
  11. package/dist/editor-script.js.map +1 -1
  12. package/dist/export-pdf.js +1 -1
  13. package/dist/export-pdf.js.map +1 -1
  14. package/dist/export-pptx.d.ts +1132 -2
  15. package/dist/export-pptx.d.ts.map +1 -1
  16. package/dist/export-pptx.js +1208 -19525
  17. package/dist/export-pptx.js.map +1 -1
  18. package/dist/index.d.ts +1 -0
  19. package/dist/index.d.ts.map +1 -1
  20. package/dist/index.js +1 -0
  21. package/dist/index.js.map +1 -1
  22. package/dist/render-editor-data.d.ts +21 -0
  23. package/dist/render-editor-data.d.ts.map +1 -0
  24. package/dist/render-editor-data.js +1068 -0
  25. package/dist/render-editor-data.js.map +1 -0
  26. package/dist/render.d.ts +0 -2
  27. package/dist/render.d.ts.map +1 -1
  28. package/dist/render.js +7 -323
  29. package/dist/render.js.map +1 -1
  30. package/dist/theme01-pptx.d.ts +12 -0
  31. package/dist/theme01-pptx.d.ts.map +1 -0
  32. package/dist/theme01-pptx.js +2954 -0
  33. package/dist/theme01-pptx.js.map +1 -0
  34. package/dist/theme02-pptx.d.ts +5 -0
  35. package/dist/theme02-pptx.d.ts.map +1 -0
  36. package/dist/theme02-pptx.js +1063 -0
  37. package/dist/theme02-pptx.js.map +1 -0
  38. package/dist/theme03-pptx.d.ts +5 -0
  39. package/dist/theme03-pptx.d.ts.map +1 -0
  40. package/dist/theme03-pptx.js +3279 -0
  41. package/dist/theme03-pptx.js.map +1 -0
  42. package/dist/theme04-pptx.d.ts +6 -0
  43. package/dist/theme04-pptx.d.ts.map +1 -0
  44. package/dist/theme04-pptx.js +5496 -0
  45. package/dist/theme04-pptx.js.map +1 -0
  46. package/dist/theme05-pptx.d.ts +5 -0
  47. package/dist/theme05-pptx.d.ts.map +1 -0
  48. package/dist/theme05-pptx.js +3152 -0
  49. package/dist/theme05-pptx.js.map +1 -0
  50. package/dist/theme06-pptx.d.ts +124 -0
  51. package/dist/theme06-pptx.d.ts.map +1 -0
  52. package/dist/theme06-pptx.js +8154 -0
  53. package/dist/theme06-pptx.js.map +1 -0
  54. package/dist/theme07-pptx.d.ts +49 -0
  55. package/dist/theme07-pptx.d.ts.map +1 -0
  56. package/dist/theme07-pptx.js +2579 -0
  57. package/dist/theme07-pptx.js.map +1 -0
  58. package/dist/theme08-pptx.d.ts +19 -0
  59. package/dist/theme08-pptx.d.ts.map +1 -0
  60. package/dist/theme08-pptx.js +1411 -0
  61. package/dist/theme08-pptx.js.map +1 -0
  62. package/dist/theme09-pptx.d.ts +18 -0
  63. package/dist/theme09-pptx.d.ts.map +1 -0
  64. package/dist/theme09-pptx.js +1513 -0
  65. package/dist/theme09-pptx.js.map +1 -0
  66. package/dist/theme10-pptx.d.ts +18 -0
  67. package/dist/theme10-pptx.d.ts.map +1 -0
  68. package/dist/theme10-pptx.js +2889 -0
  69. package/dist/theme10-pptx.js.map +1 -0
  70. package/package.json +7 -6
  71. package/templates/editor.html +580 -0
  72. package/templates/editor.js +222 -0
@@ -0,0 +1,3152 @@
1
+ // lemonPPT - AI-powered presentation generation
2
+ // Copyright (c) 2026 lemonforme
3
+ // SPDX-License-Identifier: AGPL-3.0-or-later
4
+ //
5
+ // theme05 PPTX 版式专属渲染器(从 export-pptx.ts 抽出)。
6
+ // 共享基础设施(全局 helper + 通用渲染器 + 类型 + theme05 局部 helper/const)从 export-pptx.ts 导入;
7
+ // theme01 通用渲染器从 theme01-pptx.ts 复用;其他已抽出主题的渲染器从各自 theme<K>-pptx.ts 复用。
8
+ // 注册通过 registerTheme05Renderers 回调注入。
9
+ import { COLORS, FONTS, addTheme05Card, addTheme05SpectrumBar, CHART_COLORS, addImageMaybe, theme05SchemeColor, addTheme05Pill, addTheme05ConclusionCard, layoutTreemap } from './export-pptx.js';
10
+ function renderTheme05CoverV1(slide, props) {
11
+ if (props.tag) {
12
+ slide.addText(props.tag, {
13
+ x: 0.65, y: 0.78, w: 4.0, h: 0.25,
14
+ fontSize: 12, color: COLORS.accent, fontFace: FONTS.mono, bold: true,
15
+ });
16
+ }
17
+ slide.addText(props.title ?? '', {
18
+ x: 0.65, y: 1.3, w: 6.5, h: 1.4,
19
+ fontSize: 52, color: COLORS.primary, bold: true, valign: 'top', fontFace: FONTS.heading,
20
+ });
21
+ if (props.subtitle) {
22
+ slide.addText(props.subtitle, {
23
+ x: 0.65, y: 2.75, w: 6.5, h: 0.6,
24
+ fontSize: 18, color: COLORS.secondary, valign: 'top', fontFace: FONTS.body,
25
+ });
26
+ }
27
+ const metrics = (props.metrics || []).slice(0, 4);
28
+ if (metrics.length > 0) {
29
+ const cardW = 2.05;
30
+ const startX = 7.05;
31
+ metrics.forEach((m, idx) => {
32
+ const y = 0.9 + idx * 1.05;
33
+ addTheme05Card(slide, startX, y, cardW, 0.9, !!m.accent);
34
+ slide.addText(`${m.value ?? ''}${m.unit ?? ''}`, {
35
+ x: startX + 0.12, y: y + 0.12, w: cardW - 0.24, h: 0.4,
36
+ fontSize: 26, color: m.accent ? COLORS.white : COLORS.primary, bold: true, fontFace: FONTS.heading,
37
+ });
38
+ slide.addText(m.label ?? '', {
39
+ x: startX + 0.12, y: y + 0.52, w: cardW - 0.24, h: 0.22,
40
+ fontSize: 10, color: m.accent ? 'FFFFFF' : COLORS.secondary, fontFace: FONTS.body,
41
+ });
42
+ });
43
+ }
44
+ addTheme05SpectrumBar(slide);
45
+ const footerText = [props.footnoteLeft, props.footnoteRight].filter(Boolean).join(' ');
46
+ if (footerText) {
47
+ slide.addText(footerText, {
48
+ x: 0.65, y: 5.32, w: 8.7, h: 0.2,
49
+ fontSize: 9, color: COLORS.secondary, align: 'center', fontFace: FONTS.mono,
50
+ });
51
+ }
52
+ }
53
+ function renderTheme05ChapterV1(slide, props) {
54
+ if (props.tag) {
55
+ slide.addText(props.tag, {
56
+ x: 0.65, y: 0.78, w: 4.0, h: 0.25,
57
+ fontSize: 12, color: COLORS.accent, fontFace: FONTS.mono, bold: true,
58
+ });
59
+ }
60
+ slide.addText(props.number ?? '', {
61
+ x: 0.65, y: 1.6, w: 8.7, h: 1.4,
62
+ fontSize: 110, color: COLORS.accent, bold: true, valign: 'top', fontFace: FONTS.heading,
63
+ });
64
+ slide.addText(props.title ?? '', {
65
+ x: 0.65, y: 3.1, w: 8.7, h: 0.9,
66
+ fontSize: 44, color: COLORS.primary, bold: true, valign: 'top', fontFace: FONTS.heading,
67
+ });
68
+ if (props.subtitle) {
69
+ slide.addText(props.subtitle, {
70
+ x: 0.65, y: 4.05, w: 8.7, h: 0.5,
71
+ fontSize: 16, color: COLORS.secondary, valign: 'top', fontFace: FONTS.body,
72
+ });
73
+ }
74
+ addTheme05SpectrumBar(slide);
75
+ }
76
+ function renderTheme05ContentV1(slide, props) {
77
+ if (props.kicker) {
78
+ slide.addText(props.kicker, {
79
+ x: 0.65, y: 0.78, w: 4.0, h: 0.25,
80
+ fontSize: 11, color: COLORS.secondary, fontFace: FONTS.mono,
81
+ });
82
+ }
83
+ slide.addText(props.title ?? '', {
84
+ x: 0.65, y: 1.2, w: 4.2, h: 0.9,
85
+ fontSize: 38, color: COLORS.primary, bold: true, valign: 'top', fontFace: FONTS.heading,
86
+ });
87
+ slide.addShape('rect', {
88
+ x: 0.65, y: 2.12, w: 1.4, h: 0.05,
89
+ fill: { color: COLORS.accent },
90
+ });
91
+ if (props.subtitle) {
92
+ slide.addText(props.subtitle, {
93
+ x: 0.65, y: 2.35, w: 4.2, h: 0.7,
94
+ fontSize: 13, color: COLORS.secondary, valign: 'top', fontFace: FONTS.body,
95
+ });
96
+ }
97
+ const bullets = (props.bullets || []).slice(0, 8);
98
+ if (bullets.length > 0) {
99
+ const startX = 5.05;
100
+ const cardW = 4.1;
101
+ bullets.forEach((bullet, idx) => {
102
+ const text = typeof bullet === 'string' ? bullet : bullet.item ?? '';
103
+ const y = 0.85 + idx * 0.6;
104
+ slide.addShape('ellipse', {
105
+ x: startX + 0.05, y: y + 0.16, w: 0.1, h: 0.1,
106
+ fill: { color: COLORS.accent },
107
+ });
108
+ slide.addText(text, {
109
+ x: startX + 0.3, y, w: cardW - 0.3, h: 0.55,
110
+ fontSize: 16, color: COLORS.primary, align: 'left', valign: 'top', fontFace: FONTS.body,
111
+ });
112
+ });
113
+ }
114
+ const showConclusion = props.showConclusion !== false;
115
+ const conclusion = props.conclusion;
116
+ const hasConclusion = showConclusion && conclusion && (conclusion.value || conclusion.label || conclusion.description);
117
+ if (hasConclusion) {
118
+ const y = 4.55;
119
+ addTheme05Card(slide, 5.05, y, 4.1, 0.85, true);
120
+ let cursorY = y + 0.12;
121
+ if (conclusion.value) {
122
+ slide.addText(conclusion.value, {
123
+ x: 5.25, y: cursorY, w: 3.7, h: 0.35,
124
+ fontSize: 24, color: COLORS.white, bold: true, fontFace: FONTS.heading,
125
+ });
126
+ cursorY += 0.36;
127
+ }
128
+ if (conclusion.label) {
129
+ slide.addText(conclusion.label, {
130
+ x: 5.25, y: cursorY, w: 3.7, h: 0.2,
131
+ fontSize: 10, color: 'FFFFFF', fontFace: FONTS.body,
132
+ });
133
+ cursorY += 0.22;
134
+ }
135
+ if (conclusion.description) {
136
+ slide.addText(conclusion.description, {
137
+ x: 5.25, y: cursorY, w: 3.7, h: 0.35,
138
+ fontSize: 10, color: 'FFFFFF', valign: 'top', fontFace: FONTS.body,
139
+ });
140
+ }
141
+ }
142
+ addTheme05SpectrumBar(slide);
143
+ }
144
+ function renderTheme05MetricV1(slide, props) {
145
+ if (props.kicker) {
146
+ slide.addText(props.kicker, {
147
+ x: 0.65, y: 0.78, w: 4.0, h: 0.25,
148
+ fontSize: 11, color: COLORS.secondary, fontFace: FONTS.mono,
149
+ });
150
+ }
151
+ slide.addText(props.title ?? '', {
152
+ x: 0.65, y: 1.15, w: 8.7, h: 0.7,
153
+ fontSize: 38, color: COLORS.primary, bold: true, valign: 'top', fontFace: FONTS.heading,
154
+ });
155
+ if (props.subtitle) {
156
+ slide.addText(props.subtitle, {
157
+ x: 0.65, y: 1.85, w: 8.7, h: 0.35,
158
+ fontSize: 13, color: COLORS.secondary, valign: 'top', fontFace: FONTS.body,
159
+ });
160
+ }
161
+ slide.addText(props.value ?? '', {
162
+ x: 0.65, y: 2.45, w: 4.0, h: 1.0,
163
+ fontSize: 80, color: COLORS.accent, bold: true, valign: 'top', fontFace: FONTS.heading,
164
+ });
165
+ if (props.unit) {
166
+ slide.addText(props.unit, {
167
+ x: 3.2, y: 2.95, w: 1.5, h: 0.35,
168
+ fontSize: 22, color: COLORS.primary, bold: true, valign: 'top', fontFace: FONTS.heading,
169
+ });
170
+ }
171
+ const metrics = (props.metrics || []).slice(0, 4);
172
+ if (metrics.length > 0) {
173
+ const cardW = 2.05;
174
+ const gap = 0.18;
175
+ const startX = 0.65;
176
+ metrics.forEach((m, idx) => {
177
+ const x = startX + idx * (cardW + gap);
178
+ const y = 4.05;
179
+ addTheme05Card(slide, x, y, cardW, 1.1, !!m.accent);
180
+ slide.addText(`${m.value ?? ''}${m.unit ?? ''}`, {
181
+ x, y: y + 0.15, w: cardW, h: 0.45,
182
+ fontSize: 28, color: m.accent ? COLORS.white : COLORS.primary, bold: true, align: 'center', fontFace: FONTS.heading,
183
+ });
184
+ slide.addText(m.label ?? '', {
185
+ x, y: y + 0.62, w: cardW, h: 0.25,
186
+ fontSize: 10, color: m.accent ? 'FFFFFF' : COLORS.secondary, align: 'center', fontFace: FONTS.body,
187
+ });
188
+ });
189
+ }
190
+ addTheme05SpectrumBar(slide);
191
+ }
192
+ function renderTheme05ChartV1(slide, props) {
193
+ if (props.kicker) {
194
+ slide.addText(props.kicker, {
195
+ x: 0.65, y: 0.78, w: 4.0, h: 0.25,
196
+ fontSize: 11, color: COLORS.accent, fontFace: FONTS.mono, bold: true,
197
+ });
198
+ }
199
+ slide.addText(props.title ?? '', {
200
+ x: 0.65, y: 1.15, w: 8.7, h: 0.7,
201
+ fontSize: 38, color: COLORS.primary, bold: true, valign: 'top', fontFace: FONTS.heading,
202
+ });
203
+ if (props.subtitle) {
204
+ slide.addText(props.subtitle, {
205
+ x: 0.65, y: 1.85, w: 8.7, h: 0.3,
206
+ fontSize: 13, color: COLORS.secondary, valign: 'top', fontFace: FONTS.body,
207
+ });
208
+ }
209
+ const labels = props.labels || [];
210
+ const data = props.data || [];
211
+ const showConclusion = props.showConclusion !== false;
212
+ const conclusion = props.conclusion;
213
+ const hasConclusion = showConclusion && conclusion && (conclusion.value || conclusion.label || conclusion.description);
214
+ const chartW = hasConclusion ? 5.6 : 8.7;
215
+ if (labels.length > 0 && data.length > 0) {
216
+ const chartType = props.type === 'line' ? 'line' : 'bar';
217
+ slide.addChart(chartType, [{
218
+ name: props.title || '',
219
+ labels: labels.map((l) => (typeof l === 'string' ? l : l.item ?? '')),
220
+ values: data.map((d) => (typeof d === 'number' ? d : Number(d.item ?? 0) || 0)),
221
+ }], {
222
+ x: 0.65, y: 2.25, w: chartW, h: 3.0,
223
+ chartColors: [COLORS.accent],
224
+ showValue: true,
225
+ dataLabelColor: COLORS.primary,
226
+ dataLabelFontSize: 10,
227
+ });
228
+ }
229
+ else {
230
+ slide.addText('(暂无图表数据)', {
231
+ x: 0.65, y: 3.0, w: chartW, h: 0.6,
232
+ fontSize: 16, color: COLORS.secondary, align: 'center', valign: 'middle', fontFace: FONTS.body,
233
+ });
234
+ }
235
+ if (props.unit) {
236
+ addTheme05Card(slide, 6.45, 2.25, 2.9, 0.7);
237
+ slide.addText('单位', {
238
+ x: 6.6, y: 2.32, w: 2.6, h: 0.2,
239
+ fontSize: 10, color: COLORS.secondary, fontFace: FONTS.body,
240
+ });
241
+ slide.addText(props.unit, {
242
+ x: 6.6, y: 2.52, w: 2.6, h: 0.35,
243
+ fontSize: 18, color: COLORS.primary, bold: true, fontFace: FONTS.heading,
244
+ });
245
+ }
246
+ if (hasConclusion) {
247
+ const panelY = props.unit ? 3.05 : 2.25;
248
+ const panelH = props.unit ? 2.2 : 3.0;
249
+ addTheme05Card(slide, 6.45, panelY, 2.9, panelH, true);
250
+ let cursorY = panelY + 0.18;
251
+ if (conclusion.value) {
252
+ slide.addText(conclusion.value, {
253
+ x: 6.65, y: cursorY, w: 2.5, h: 0.55,
254
+ fontSize: 32, color: COLORS.white, bold: true, fontFace: FONTS.heading,
255
+ });
256
+ cursorY += 0.58;
257
+ }
258
+ if (conclusion.label) {
259
+ slide.addText(conclusion.label, {
260
+ x: 6.65, y: cursorY, w: 2.5, h: 0.25,
261
+ fontSize: 10, color: 'FFFFFF', fontFace: FONTS.body,
262
+ });
263
+ cursorY += 0.32;
264
+ }
265
+ if (conclusion.description) {
266
+ slide.addText(conclusion.description, {
267
+ x: 6.65, y: cursorY, w: 2.5, h: panelH - (cursorY - panelY) - 0.25,
268
+ fontSize: 10, color: 'FFFFFF', valign: 'top', fontFace: FONTS.body,
269
+ });
270
+ }
271
+ }
272
+ addTheme05SpectrumBar(slide);
273
+ }
274
+ function renderTheme05RankV1(slide, props) {
275
+ if (props.kicker) {
276
+ slide.addText(props.kicker, {
277
+ x: 0.65, y: 0.78, w: 4.0, h: 0.25,
278
+ fontSize: 11, color: COLORS.accent, fontFace: FONTS.mono, bold: true,
279
+ });
280
+ }
281
+ slide.addText(props.title ?? '', {
282
+ x: 0.65, y: 1.15, w: 8.7, h: 0.7,
283
+ fontSize: 38, color: COLORS.primary, bold: true, valign: 'top', fontFace: FONTS.heading,
284
+ });
285
+ if (props.subtitle) {
286
+ slide.addText(props.subtitle, {
287
+ x: 0.65, y: 1.85, w: 8.7, h: 0.3,
288
+ fontSize: 13, color: COLORS.secondary, valign: 'top', fontFace: FONTS.body,
289
+ });
290
+ }
291
+ const rows = (props.rows || []).slice(0, 8);
292
+ if (rows.length > 0) {
293
+ const rowH = 0.48;
294
+ const startY = 2.35;
295
+ rows.forEach((row, idx) => {
296
+ const y = startY + idx * (rowH + 0.1);
297
+ const rankColor = idx === 0 ? COLORS.accent : COLORS.surfaceElevated;
298
+ slide.addShape('roundRect', {
299
+ x: 0.65, y, w: 0.5, h: rowH,
300
+ fill: { color: rankColor },
301
+ rectRadius: 0.06,
302
+ });
303
+ slide.addText(String(row.rank ?? idx + 1), {
304
+ x: 0.65, y, w: 0.5, h: rowH,
305
+ fontSize: 14, color: idx === 0 ? COLORS.white : COLORS.primary, bold: true, align: 'center', valign: 'middle', fontFace: FONTS.heading,
306
+ });
307
+ slide.addText(row.name ?? '', {
308
+ x: 1.3, y, w: 4.0, h: rowH,
309
+ fontSize: 15, color: COLORS.primary, bold: true, valign: 'middle', fontFace: FONTS.body,
310
+ });
311
+ slide.addText(row.value ?? '', {
312
+ x: 5.6, y, w: 1.8, h: rowH,
313
+ fontSize: 16, color: COLORS.primary, bold: true, align: 'right', valign: 'middle', fontFace: FONTS.mono,
314
+ });
315
+ if (row.change) {
316
+ slide.addText(row.change, {
317
+ x: 7.55, y: y + 0.08, w: 1.5, h: 0.32,
318
+ fontSize: 11, color: row.positive === false ? 'E85D4E' : '22C55E', bold: true, align: 'right', valign: 'middle', fontFace: FONTS.mono,
319
+ });
320
+ }
321
+ });
322
+ }
323
+ addTheme05SpectrumBar(slide);
324
+ }
325
+ function renderTheme05HeatmapV1(slide, props) {
326
+ if (props.kicker) {
327
+ slide.addText(props.kicker, {
328
+ x: 0.65, y: 0.78, w: 4.0, h: 0.25,
329
+ fontSize: 11, color: COLORS.accent, fontFace: FONTS.mono, bold: true,
330
+ });
331
+ }
332
+ slide.addText(props.title ?? '', {
333
+ x: 0.65, y: 1.15, w: 8.7, h: 0.7,
334
+ fontSize: 38, color: COLORS.primary, bold: true, valign: 'top', fontFace: FONTS.heading,
335
+ });
336
+ if (props.subtitle) {
337
+ slide.addText(props.subtitle, {
338
+ x: 0.65, y: 1.85, w: 8.7, h: 0.3,
339
+ fontSize: 13, color: COLORS.secondary, valign: 'top', fontFace: FONTS.body,
340
+ });
341
+ }
342
+ const values = (props.values || []).slice(0, 12);
343
+ const months = (props.months || []).slice(0, 12);
344
+ if (values.length > 0) {
345
+ const max = Math.max(...values.map((v) => (typeof v === 'number' ? v : Number(v.item ?? 0) || 0)), 1);
346
+ const cols = Math.min(values.length, 6);
347
+ const cellW = 1.35;
348
+ const cellH = 0.9;
349
+ const totalW = cols * cellW + (cols - 1) * 0.1;
350
+ const startX = (10 - totalW) / 2;
351
+ const startY = 2.4;
352
+ const heatColor = (value) => {
353
+ const r = value / max;
354
+ if (r > 0.75)
355
+ return COLORS.accent;
356
+ if (r > 0.5)
357
+ return CHART_COLORS[1] || 'F5A623';
358
+ if (r > 0.25)
359
+ return CHART_COLORS[2] || '0FA3B1';
360
+ return COLORS.surfaceElevated;
361
+ };
362
+ values.forEach((v, idx) => {
363
+ const value = typeof v === 'number' ? v : Number(v.item ?? 0) || 0;
364
+ const r = value / max;
365
+ const col = idx % cols;
366
+ const row = Math.floor(idx / cols);
367
+ const x = startX + col * (cellW + 0.1);
368
+ const y = startY + row * (cellH + 0.12);
369
+ slide.addShape('roundRect', {
370
+ x, y, w: cellW, h: cellH,
371
+ fill: { color: heatColor(value) },
372
+ rectRadius: 0.06,
373
+ });
374
+ slide.addText(String(value), {
375
+ x, y: y + 0.05, w: cellW, h: 0.4,
376
+ fontSize: 18, color: r > 0.5 ? COLORS.white : COLORS.primary, bold: true, align: 'center', valign: 'middle', fontFace: FONTS.heading,
377
+ });
378
+ if (months[idx]) {
379
+ const monthText = typeof months[idx] === 'string' ? months[idx] : months[idx].item ?? '';
380
+ slide.addText(monthText, {
381
+ x, y: y + 0.48, w: cellW, h: 0.3,
382
+ fontSize: 10, color: r > 0.5 ? 'FFFFFF' : COLORS.secondary, align: 'center', valign: 'middle', fontFace: FONTS.body,
383
+ });
384
+ }
385
+ });
386
+ }
387
+ if (props.peakLabel) {
388
+ addTheme05Card(slide, 0.65, 4.6, 4.0, 0.55, true);
389
+ slide.addText(props.peakLabel, {
390
+ x: 0.8, y: 4.68, w: 3.7, h: 0.38,
391
+ fontSize: 14, color: COLORS.white, bold: true, valign: 'middle', fontFace: FONTS.body,
392
+ });
393
+ }
394
+ addTheme05SpectrumBar(slide);
395
+ }
396
+ function renderTheme05WaterfallV1(slide, props) {
397
+ if (props.kicker) {
398
+ slide.addText(props.kicker, {
399
+ x: 0.65, y: 0.78, w: 4.0, h: 0.25,
400
+ fontSize: 11, color: COLORS.accent, fontFace: FONTS.mono, bold: true,
401
+ });
402
+ }
403
+ slide.addText(props.title ?? '', {
404
+ x: 0.65, y: 1.15, w: 8.7, h: 0.7,
405
+ fontSize: 38, color: COLORS.primary, bold: true, valign: 'top', fontFace: FONTS.heading,
406
+ });
407
+ if (props.subtitle) {
408
+ slide.addText(props.subtitle, {
409
+ x: 0.65, y: 1.85, w: 8.7, h: 0.3,
410
+ fontSize: 13, color: COLORS.secondary, valign: 'top', fontFace: FONTS.body,
411
+ });
412
+ }
413
+ const items = (props.items || []).slice(0, 8);
414
+ if (items.length > 0) {
415
+ const labels = items.map((item) => item.name ?? '');
416
+ const values = items.map((item) => Number(item.value ?? 0) || 0);
417
+ let cumulative = 0;
418
+ const baseData = [];
419
+ const changeData = [];
420
+ const changeColors = [];
421
+ values.forEach((v) => {
422
+ baseData.push(cumulative);
423
+ changeData.push(v);
424
+ changeColors.push(v >= 0 ? COLORS.accent : 'E85D4E');
425
+ cumulative += v;
426
+ });
427
+ const total = cumulative;
428
+ // 总计柱:从 0 开始到 total
429
+ baseData.push(0);
430
+ changeData.push(total);
431
+ changeColors.push(CHART_COLORS[2] || '0FA3B1');
432
+ const chartLabels = [...labels, props.totalLabel ?? '总计'];
433
+ slide.addChart('bar', [
434
+ {
435
+ name: '基础',
436
+ labels: chartLabels,
437
+ values: baseData,
438
+ },
439
+ {
440
+ name: '变化',
441
+ labels: chartLabels,
442
+ values: changeData,
443
+ },
444
+ ], {
445
+ x: 0.65, y: 2.35, w: 5.6, h: 3.0,
446
+ chartColors: [COLORS.surface, COLORS.accent],
447
+ showValue: true,
448
+ dataLabelColor: COLORS.primary,
449
+ dataLabelFontSize: 9,
450
+ barGrouping: 'stacked',
451
+ barDir: 'col',
452
+ dataLabelPosition: 'outEnd',
453
+ });
454
+ addTheme05Card(slide, 6.45, 2.35, 2.9, 3.0);
455
+ items.forEach((item, idx) => {
456
+ const y = 2.5 + idx * 0.36;
457
+ const color = (Number(item.value ?? 0) || 0) >= 0 ? COLORS.accent : 'E85D4E';
458
+ slide.addText(item.name ?? '', {
459
+ x: 6.6, y, w: 1.6, h: 0.28,
460
+ fontSize: 11, color: COLORS.primary, valign: 'middle', fontFace: FONTS.body,
461
+ });
462
+ slide.addText(`${(Number(item.value ?? 0) || 0) >= 0 ? '+' : ''}${item.value ?? 0}`, {
463
+ x: 8.2, y, w: 1.0, h: 0.28,
464
+ fontSize: 11, color, bold: true, align: 'right', valign: 'middle', fontFace: FONTS.mono,
465
+ });
466
+ });
467
+ slide.addShape('rect', {
468
+ x: 6.6, y: 2.5 + items.length * 0.36 + 0.08, w: 2.6, h: 0.02,
469
+ fill: { color: COLORS.border },
470
+ });
471
+ slide.addText(props.totalLabel ?? '总计', {
472
+ x: 6.6, y: 2.5 + items.length * 0.36 + 0.16, w: 1.6, h: 0.28,
473
+ fontSize: 12, color: COLORS.primary, bold: true, valign: 'middle', fontFace: FONTS.body,
474
+ });
475
+ slide.addText(String(total), {
476
+ x: 8.2, y: 2.5 + items.length * 0.36 + 0.16, w: 1.0, h: 0.28,
477
+ fontSize: 12, color: CHART_COLORS[2] || '0FA3B1', bold: true, align: 'right', valign: 'middle', fontFace: FONTS.mono,
478
+ });
479
+ }
480
+ else {
481
+ slide.addText('(暂无瀑布图数据)', {
482
+ x: 0.65, y: 3.0, w: 8.7, h: 0.6,
483
+ fontSize: 16, color: COLORS.secondary, align: 'center', valign: 'middle', fontFace: FONTS.body,
484
+ });
485
+ }
486
+ addTheme05SpectrumBar(slide);
487
+ }
488
+ function renderTheme05QuoteV1(slide, props) {
489
+ slide.addShape('rect', {
490
+ x: 0.65, y: 0.9, w: 0.12, h: 4.0,
491
+ fill: { color: COLORS.accent },
492
+ });
493
+ slide.addText('“', {
494
+ x: 1.0, y: 1.0, w: 1.0, h: 0.7,
495
+ fontSize: 80, color: COLORS.accent, bold: true, valign: 'top', fontFace: FONTS.heading,
496
+ });
497
+ slide.addText(props.quote ?? '', {
498
+ x: 1.0, y: 1.9, w: 7.5, h: 2.4,
499
+ fontSize: 32, color: COLORS.primary, bold: true, valign: 'top', fontFace: FONTS.heading,
500
+ });
501
+ if (props.source) {
502
+ slide.addText(props.source, {
503
+ x: 1.0, y: 4.4, w: 7.5, h: 0.3,
504
+ fontSize: 13, color: COLORS.secondary, valign: 'top', fontFace: FONTS.body,
505
+ });
506
+ }
507
+ addTheme05SpectrumBar(slide);
508
+ }
509
+ function renderTheme05ImageV1(slide, props) {
510
+ if (props.image) {
511
+ addImageMaybe(slide, props.image, 0.65, 0.65, 5.4, 4.45);
512
+ }
513
+ else {
514
+ slide.addShape('roundRect', {
515
+ x: 0.65, y: 0.65, w: 5.4, h: 4.45,
516
+ fill: { color: COLORS.surfaceElevated },
517
+ line: { color: COLORS.border, width: 1 },
518
+ rectRadius: 0.08,
519
+ });
520
+ slide.addText('点击上传图片', {
521
+ x: 0.65, y: 2.6, w: 5.4, h: 0.4,
522
+ fontSize: 14, color: COLORS.secondary, align: 'center', valign: 'middle', fontFace: FONTS.body,
523
+ });
524
+ }
525
+ const annotation = props.annotation;
526
+ if (annotation && (annotation.value || annotation.label)) {
527
+ addTheme05Card(slide, 4.8, 3.8, 1.9, 1.0, true);
528
+ if (annotation.value) {
529
+ slide.addText(annotation.value, {
530
+ x: 4.95, y: 3.9, w: 1.6, h: 0.4,
531
+ fontSize: 24, color: COLORS.white, bold: true, align: 'center', valign: 'middle', fontFace: FONTS.heading,
532
+ });
533
+ }
534
+ if (annotation.label) {
535
+ slide.addText(annotation.label, {
536
+ x: 4.95, y: 4.3, w: 1.6, h: 0.4,
537
+ fontSize: 10, color: 'FFFFFF', align: 'center', valign: 'middle', fontFace: FONTS.body,
538
+ });
539
+ }
540
+ }
541
+ if (props.kicker) {
542
+ slide.addText(props.kicker, {
543
+ x: 6.35, y: 0.78, w: 3.0, h: 0.25,
544
+ fontSize: 11, color: COLORS.accent, fontFace: FONTS.mono, bold: true,
545
+ });
546
+ }
547
+ slide.addText(props.title ?? '', {
548
+ x: 6.35, y: 1.2, w: 3.0, h: 1.0,
549
+ fontSize: 30, color: COLORS.primary, bold: true, valign: 'top', fontFace: FONTS.heading,
550
+ });
551
+ slide.addShape('rect', {
552
+ x: 6.35, y: 2.25, w: 1.0, h: 0.05,
553
+ fill: { color: COLORS.accent },
554
+ });
555
+ if (props.subtitle) {
556
+ slide.addText(props.subtitle, {
557
+ x: 6.35, y: 2.4, w: 3.0, h: 0.9,
558
+ fontSize: 13, color: COLORS.secondary, valign: 'top', fontFace: FONTS.body,
559
+ });
560
+ }
561
+ addTheme05SpectrumBar(slide);
562
+ }
563
+ function renderTheme05VersusV1(slide, props) {
564
+ const schemeColors = {
565
+ coral: 'E85D4E',
566
+ amber: 'F5A623',
567
+ teal: '0FA3B1',
568
+ indigo: '4A58D9',
569
+ violet: '7C3AED',
570
+ };
571
+ if (props.kicker) {
572
+ slide.addText(props.kicker, {
573
+ x: 0.65, y: 0.78, w: 4.0, h: 0.25,
574
+ fontSize: 11, color: COLORS.accent, fontFace: FONTS.mono, bold: true,
575
+ });
576
+ }
577
+ slide.addText(props.title ?? '', {
578
+ x: 0.65, y: 1.15, w: 8.7, h: 0.7,
579
+ fontSize: 38, color: COLORS.primary, bold: true, valign: 'top', fontFace: FONTS.heading,
580
+ });
581
+ if (props.subtitle) {
582
+ slide.addText(props.subtitle, {
583
+ x: 0.65, y: 1.85, w: 8.7, h: 0.3,
584
+ fontSize: 13, color: COLORS.secondary, valign: 'top', fontFace: FONTS.body,
585
+ });
586
+ }
587
+ const cardW = 3.7;
588
+ const cardH = 2.4;
589
+ const cardY = 2.4;
590
+ const badgeW = 0.9;
591
+ const gap = 0.25;
592
+ const leftX = (10 - cardW * 2 - badgeW - gap * 2) / 2;
593
+ const rightX = leftX + cardW + gap + badgeW + gap;
594
+ const badgeX = leftX + cardW + gap;
595
+ const sides = [
596
+ { key: 'left', x: leftX },
597
+ { key: 'right', x: rightX },
598
+ ];
599
+ sides.forEach(({ key, x }) => {
600
+ const side = props[key] || {};
601
+ const scheme = schemeColors[side.scheme] || COLORS.accent;
602
+ slide.addShape('roundRect', {
603
+ x, y: cardY, w: cardW, h: cardH,
604
+ fill: { color: COLORS.surfaceElevated },
605
+ line: { color: scheme, width: 2 },
606
+ rectRadius: 0.08,
607
+ });
608
+ slide.addText(side.label ?? '', {
609
+ x: x + 0.25, y: cardY + 0.25, w: cardW - 0.5, h: 0.35,
610
+ fontSize: 13, color: COLORS.secondary, valign: 'top', fontFace: FONTS.body,
611
+ });
612
+ slide.addText(side.value ?? '', {
613
+ x: x + 0.25, y: cardY + 0.75, w: cardW - 0.5, h: 0.9,
614
+ fontSize: 52, color: COLORS.primary, bold: true, valign: 'top', fontFace: FONTS.heading,
615
+ });
616
+ if (side.unit) {
617
+ slide.addText(side.unit, {
618
+ x: x + 0.25, y: cardY + 1.55, w: cardW - 0.5, h: 0.3,
619
+ fontSize: 16, color: COLORS.secondary, valign: 'top', fontFace: FONTS.heading,
620
+ });
621
+ }
622
+ });
623
+ slide.addShape('ellipse', {
624
+ x: badgeX, y: cardY + (cardH - badgeW) / 2, w: badgeW, h: badgeW,
625
+ fill: { color: COLORS.light },
626
+ line: { color: COLORS.border, width: 1 },
627
+ });
628
+ slide.addText('VS', {
629
+ x: badgeX, y: cardY + (cardH - badgeW) / 2, w: badgeW, h: badgeW,
630
+ fontSize: 20, color: COLORS.secondary, bold: true, align: 'center', valign: 'middle', fontFace: FONTS.mono,
631
+ });
632
+ if (props.footnote) {
633
+ slide.addText(props.footnote, {
634
+ x: 0.65, y: 5.0, w: 8.7, h: 0.25,
635
+ fontSize: 10, color: COLORS.secondary, align: 'center', fontFace: FONTS.body,
636
+ });
637
+ }
638
+ addTheme05SpectrumBar(slide);
639
+ }
640
+ function renderTheme05ProcessV1(slide, props) {
641
+ if (props.tag || props.tagLabel) {
642
+ const tagText = [props.tag, props.tagLabel].filter(Boolean).join(' · ');
643
+ slide.addText(tagText, {
644
+ x: 0.65, y: 0.78, w: 4.0, h: 0.25,
645
+ fontSize: 11, color: COLORS.accent, fontFace: FONTS.mono, bold: true,
646
+ });
647
+ }
648
+ if (props.topRightMeta) {
649
+ slide.addText(props.topRightMeta, {
650
+ x: 5.5, y: 0.78, w: 3.85, h: 0.25,
651
+ fontSize: 10, color: COLORS.secondary, align: 'right', fontFace: FONTS.mono,
652
+ });
653
+ }
654
+ slide.addText(props.title ?? '', {
655
+ x: 0.65, y: 1.15, w: 8.7, h: 0.7,
656
+ fontSize: 38, color: COLORS.primary, bold: true, valign: 'top', fontFace: FONTS.heading,
657
+ });
658
+ if (props.subtitle) {
659
+ slide.addText(props.subtitle, {
660
+ x: 0.65, y: 1.85, w: 8.7, h: 0.3,
661
+ fontSize: 13, color: COLORS.secondary, valign: 'top', fontFace: FONTS.body,
662
+ });
663
+ }
664
+ const steps = (props.steps || []).filter((s) => s != null).slice(0, 6);
665
+ if (steps.length > 0) {
666
+ const cardW = Math.min(2.0, (8.7 - (steps.length - 1) * 0.18) / steps.length);
667
+ const gap = 0.18;
668
+ const totalW = steps.length * cardW + (steps.length - 1) * gap;
669
+ const startX = (10 - totalW) / 2;
670
+ const cardY = 2.55;
671
+ const cardH = 2.2;
672
+ steps.forEach((step, idx) => {
673
+ const x = startX + idx * (cardW + gap);
674
+ addTheme05Card(slide, x, cardY, cardW, cardH);
675
+ slide.addShape('ellipse', {
676
+ x: x + 0.15, y: cardY + 0.18, w: 0.42, h: 0.42,
677
+ fill: { color: COLORS.accent },
678
+ });
679
+ slide.addText(String(idx + 1).padStart(2, '0'), {
680
+ x: x + 0.15, y: cardY + 0.18, w: 0.42, h: 0.42,
681
+ fontSize: 12, color: COLORS.white, bold: true, align: 'center', valign: 'middle', fontFace: FONTS.mono,
682
+ });
683
+ slide.addText(step.title ?? '', {
684
+ x: x + 0.65, y: cardY + 0.2, w: cardW - 0.8, h: 0.45,
685
+ fontSize: 14, color: COLORS.primary, bold: true, valign: 'top', fontFace: FONTS.heading,
686
+ });
687
+ if (step.description) {
688
+ slide.addText(step.description, {
689
+ x: x + 0.15, y: cardY + 0.75, w: cardW - 0.3, h: cardH - 0.95,
690
+ fontSize: 10, color: COLORS.secondary, valign: 'top', fontFace: FONTS.body,
691
+ });
692
+ }
693
+ if (idx < steps.length - 1) {
694
+ const arrowX = x + cardW + 0.02;
695
+ const arrowY = cardY + cardH / 2;
696
+ slide.addShape('rect', {
697
+ x: arrowX, y: arrowY - 0.01, w: gap - 0.04, h: 0.02,
698
+ fill: { color: COLORS.border },
699
+ });
700
+ slide.addShape('triangle', {
701
+ x: arrowX + gap - 0.1, y: arrowY - 0.05, w: 0.08, h: 0.1,
702
+ fill: { color: COLORS.border },
703
+ });
704
+ }
705
+ });
706
+ }
707
+ const footerText = [props.footnoteLeft, props.footnoteRight].filter(Boolean).join(' ');
708
+ if (footerText) {
709
+ slide.addText(footerText, {
710
+ x: 0.65, y: 5.32, w: 8.7, h: 0.2,
711
+ fontSize: 9, color: COLORS.secondary, align: 'center', fontFace: FONTS.mono,
712
+ });
713
+ }
714
+ addTheme05SpectrumBar(slide);
715
+ }
716
+ function renderTheme05TimelineV1(slide, props) {
717
+ const schemeColors = {
718
+ coral: 'E85D4E',
719
+ amber: 'F5A623',
720
+ teal: '0FA3B1',
721
+ indigo: '4A58D9',
722
+ violet: '7C3AED',
723
+ };
724
+ if (props.kicker) {
725
+ slide.addText(props.kicker, {
726
+ x: 0.65, y: 0.78, w: 5.0, h: 0.25,
727
+ fontSize: 11, color: COLORS.accent, fontFace: FONTS.mono, bold: true,
728
+ });
729
+ }
730
+ slide.addText(props.title ?? '', {
731
+ x: 0.65, y: 1.15, w: 8.7, h: 0.7,
732
+ fontSize: 38, color: COLORS.primary, bold: true, valign: 'top', fontFace: FONTS.heading,
733
+ });
734
+ if (props.subtitle) {
735
+ slide.addText(props.subtitle, {
736
+ x: 0.65, y: 1.85, w: 8.7, h: 0.3,
737
+ fontSize: 13, color: COLORS.secondary, valign: 'top', fontFace: FONTS.body,
738
+ });
739
+ }
740
+ const phases = (props.phases || []).slice(0, 3);
741
+ if (phases.length > 0) {
742
+ const cardW = 2.8;
743
+ const gap = 0.2;
744
+ const totalW = phases.length * cardW + (phases.length - 1) * gap;
745
+ const startX = (10 - totalW) / 2;
746
+ const trackY = 2.55;
747
+ const cardY = 3.15;
748
+ const cardH = 2.15;
749
+ const dotY = trackY + 0.05;
750
+ const lineY = trackY + 0.24;
751
+ // timeline line
752
+ slide.addShape('rect', {
753
+ x: startX + 0.4, y: lineY, w: totalW - 0.8, h: 0.02,
754
+ fill: { color: COLORS.border },
755
+ });
756
+ phases.forEach((phase, idx) => {
757
+ const x = startX + idx * (cardW + gap);
758
+ const scheme = schemeColors[phase.scheme] || COLORS.accent;
759
+ // node dot
760
+ slide.addShape('ellipse', {
761
+ x: x + cardW / 2 - 0.18, y: dotY, w: 0.36, h: 0.36,
762
+ fill: { color: scheme },
763
+ });
764
+ slide.addText(String(idx + 1), {
765
+ x: x + cardW / 2 - 0.18, y: dotY, w: 0.36, h: 0.36,
766
+ fontSize: 12, color: COLORS.white, bold: true, align: 'center', valign: 'middle', fontFace: FONTS.mono,
767
+ });
768
+ // period
769
+ slide.addText(phase.period ?? '', {
770
+ x, y: trackY + 0.5, w: cardW, h: 0.22,
771
+ fontSize: 10, color: COLORS.secondary, align: 'center', fontFace: FONTS.mono,
772
+ });
773
+ // card
774
+ addTheme05Card(slide, x, cardY, cardW, cardH);
775
+ slide.addText(String(idx + 1).padStart(2, '0'), {
776
+ x: x + 0.12, y: cardY + 0.12, w: 0.5, h: 0.35,
777
+ fontSize: 18, color: COLORS.secondary, bold: true, valign: 'top', fontFace: FONTS.mono,
778
+ });
779
+ if (phase.badge) {
780
+ slide.addText(phase.badge, {
781
+ x: x + 0.55, y: cardY + 0.14, w: cardW - 0.7, h: 0.22,
782
+ fontSize: 9, color: scheme, fontFace: FONTS.mono, bold: true,
783
+ });
784
+ }
785
+ slide.addText(phase.title ?? '', {
786
+ x: x + 0.12, y: cardY + 0.55, w: cardW - 0.24, h: 0.4,
787
+ fontSize: 14, color: COLORS.primary, bold: true, valign: 'top', fontFace: FONTS.heading,
788
+ });
789
+ if (phase.description) {
790
+ slide.addText(phase.description, {
791
+ x: x + 0.12, y: cardY + 0.95, w: cardW - 0.24, h: cardH - 1.05,
792
+ fontSize: 9, color: COLORS.secondary, valign: 'top', fontFace: FONTS.body,
793
+ });
794
+ }
795
+ });
796
+ }
797
+ if (props.footnote) {
798
+ slide.addText(props.footnote, {
799
+ x: 0.65, y: 5.32, w: 8.7, h: 0.2,
800
+ fontSize: 9, color: COLORS.secondary, align: 'center', fontFace: FONTS.mono,
801
+ });
802
+ }
803
+ addTheme05SpectrumBar(slide);
804
+ }
805
+ function renderTheme05MatrixV1(slide, props) {
806
+ const schemeColors = {
807
+ coral: 'E85D4E',
808
+ amber: 'F5A623',
809
+ teal: '0FA3B1',
810
+ indigo: '4A58D9',
811
+ violet: '7C3AED',
812
+ };
813
+ if (props.kicker) {
814
+ slide.addText(props.kicker, {
815
+ x: 0.65, y: 0.78, w: 4.0, h: 0.25,
816
+ fontSize: 11, color: COLORS.accent, fontFace: FONTS.mono, bold: true,
817
+ });
818
+ }
819
+ slide.addText(props.title ?? '', {
820
+ x: 0.65, y: 1.15, w: 8.7, h: 0.7,
821
+ fontSize: 38, color: COLORS.primary, bold: true, valign: 'top', fontFace: FONTS.heading,
822
+ });
823
+ if (props.subtitle) {
824
+ slide.addText(props.subtitle, {
825
+ x: 0.65, y: 1.85, w: 8.7, h: 0.3,
826
+ fontSize: 13, color: COLORS.secondary, valign: 'top', fontFace: FONTS.body,
827
+ });
828
+ }
829
+ const gridX = 1.1;
830
+ const gridY = 2.45;
831
+ const gridW = 7.4;
832
+ const gridH = 2.65;
833
+ // y-axis
834
+ const yAxis = props.yAxis || { low: '低', high: '高' };
835
+ slide.addText(yAxis.high ?? '高', {
836
+ x: 0.35, y: gridY, w: 0.5, h: 0.25,
837
+ fontSize: 10, color: COLORS.secondary, align: 'center', valign: 'top', fontFace: FONTS.body,
838
+ });
839
+ slide.addShape('rect', {
840
+ x: 0.57, y: gridY + 0.3, w: 0.02, h: gridH - 0.55,
841
+ fill: { color: COLORS.border },
842
+ });
843
+ slide.addText(yAxis.low ?? '低', {
844
+ x: 0.35, y: gridY + gridH - 0.5, w: 0.5, h: 0.25,
845
+ fontSize: 10, color: COLORS.secondary, align: 'center', valign: 'bottom', fontFace: FONTS.body,
846
+ });
847
+ const items = (props.items || []).slice(0, 4);
848
+ if (items.length > 0) {
849
+ const cols = 2;
850
+ const rows = 2;
851
+ const cardGap = 0.14;
852
+ const cardW = (gridW - cardGap) / cols;
853
+ const cardH = (gridH - cardGap) / rows;
854
+ items.forEach((item, idx) => {
855
+ const col = idx % cols;
856
+ const row = Math.floor(idx / cols);
857
+ const x = gridX + col * (cardW + cardGap);
858
+ const y = gridY + row * (cardH + cardGap);
859
+ const scheme = schemeColors[item.scheme] || COLORS.accent;
860
+ addTheme05Card(slide, x, y, cardW, cardH);
861
+ slide.addShape('rect', {
862
+ x, y, w: cardW, h: 0.04,
863
+ fill: { color: scheme },
864
+ });
865
+ slide.addText(item.title ?? '', {
866
+ x: x + 0.12, y: y + 0.18, w: cardW - 0.24, h: 0.35,
867
+ fontSize: 14, color: COLORS.primary, bold: true, valign: 'top', fontFace: FONTS.heading,
868
+ });
869
+ if (item.description) {
870
+ slide.addText(item.description, {
871
+ x: x + 0.12, y: y + 0.55, w: cardW - 0.24, h: cardH - 0.65,
872
+ fontSize: 9, color: COLORS.secondary, valign: 'top', fontFace: FONTS.body,
873
+ });
874
+ }
875
+ });
876
+ }
877
+ // x-axis
878
+ const xAxis = props.xAxis || { low: '低', high: '高' };
879
+ slide.addText(xAxis.low ?? '低', {
880
+ x: gridX, y: gridY + gridH + 0.12, w: 0.5, h: 0.22,
881
+ fontSize: 10, color: COLORS.secondary, align: 'left', fontFace: FONTS.body,
882
+ });
883
+ slide.addShape('rect', {
884
+ x: gridX + 0.55, y: gridY + gridH + 0.22, w: gridW - 1.1, h: 0.02,
885
+ fill: { color: COLORS.border },
886
+ });
887
+ slide.addText(xAxis.high ?? '高', {
888
+ x: gridX + gridW - 0.5, y: gridY + gridH + 0.12, w: 0.5, h: 0.22,
889
+ fontSize: 10, color: COLORS.secondary, align: 'right', fontFace: FONTS.body,
890
+ });
891
+ if (props.footnote) {
892
+ slide.addText(props.footnote, {
893
+ x: 0.65, y: 5.32, w: 8.7, h: 0.2,
894
+ fontSize: 9, color: COLORS.secondary, align: 'center', fontFace: FONTS.mono,
895
+ });
896
+ }
897
+ addTheme05SpectrumBar(slide);
898
+ }
899
+ function renderTheme05QuadrantV1(slide, props) {
900
+ const schemeColors = {
901
+ coral: 'E85D4E',
902
+ amber: 'F5A623',
903
+ teal: '0FA3B1',
904
+ indigo: '4A58D9',
905
+ violet: '7C3AED',
906
+ };
907
+ if (props.kicker) {
908
+ slide.addText(props.kicker, {
909
+ x: 0.65, y: 0.78, w: 5.0, h: 0.25,
910
+ fontSize: 11, color: COLORS.accent, fontFace: FONTS.mono, bold: true,
911
+ });
912
+ }
913
+ slide.addText(props.title ?? '', {
914
+ x: 0.65, y: 1.15, w: 8.7, h: 0.7,
915
+ fontSize: 38, color: COLORS.primary, bold: true, valign: 'top', fontFace: FONTS.heading,
916
+ });
917
+ if (props.subtitle) {
918
+ slide.addText(props.subtitle, {
919
+ x: 0.65, y: 1.85, w: 8.7, h: 0.3,
920
+ fontSize: 13, color: COLORS.secondary, valign: 'top', fontFace: FONTS.body,
921
+ });
922
+ }
923
+ const gridX = 1.1;
924
+ const gridY = 2.45;
925
+ const gridW = 7.4;
926
+ const gridH = 2.65;
927
+ // y-axis
928
+ const yAxisLabel = props.yAxisLabel ?? '资本热度';
929
+ const yLabels = props.yAxisLabels ?? ['低', '高'];
930
+ slide.addText(yAxisLabel, {
931
+ x: 0.05, y: gridY + gridH / 2 - 0.5, w: 0.9, h: 1.0,
932
+ fontSize: 10, color: COLORS.accent, align: 'center', valign: 'middle', fontFace: FONTS.mono, bold: true,
933
+ });
934
+ slide.addShape('rect', {
935
+ x: 0.57, y: gridY + 0.15, w: 0.02, h: gridH - 0.3,
936
+ fill: { color: COLORS.border },
937
+ });
938
+ slide.addText(yLabels[1] ?? '高', {
939
+ x: 0.35, y: gridY, w: 0.5, h: 0.25,
940
+ fontSize: 10, color: COLORS.secondary, align: 'center', valign: 'top', fontFace: FONTS.body,
941
+ });
942
+ slide.addText(yLabels[0] ?? '低', {
943
+ x: 0.35, y: gridY + gridH - 0.25, w: 0.5, h: 0.25,
944
+ fontSize: 10, color: COLORS.secondary, align: 'center', valign: 'bottom', fontFace: FONTS.body,
945
+ });
946
+ const quadrants = (props.quadrants || []).slice(0, 4);
947
+ if (quadrants.length > 0) {
948
+ const cols = 2;
949
+ const rows = 2;
950
+ const cardGap = 0.14;
951
+ const cardW = (gridW - cardGap) / cols;
952
+ const cardH = (gridH - cardGap) / rows;
953
+ quadrants.forEach((q, idx) => {
954
+ const col = idx % cols;
955
+ const row = Math.floor(idx / cols);
956
+ const x = gridX + col * (cardW + cardGap);
957
+ const y = gridY + row * (cardH + cardGap);
958
+ const scheme = schemeColors[q.scheme] || COLORS.accent;
959
+ addTheme05Card(slide, x, y, cardW, cardH);
960
+ slide.addShape('rect', {
961
+ x, y, w: cardW, h: 0.04,
962
+ fill: { color: scheme },
963
+ });
964
+ slide.addShape('ellipse', {
965
+ x: x + 0.12, y: y + 0.16, w: 0.1, h: 0.1,
966
+ fill: { color: scheme },
967
+ });
968
+ slide.addText(q.title ?? '', {
969
+ x: x + 0.28, y: y + 0.12, w: cardW - 0.8, h: 0.35,
970
+ fontSize: 14, color: COLORS.primary, bold: true, valign: 'top', fontFace: FONTS.heading,
971
+ });
972
+ slide.addText(String(idx + 1).padStart(2, '0'), {
973
+ x: x + cardW - 0.45, y: y + 0.12, w: 0.35, h: 0.3,
974
+ fontSize: 14, color: COLORS.secondary, bold: true, align: 'right', fontFace: FONTS.mono,
975
+ });
976
+ if (q.description) {
977
+ slide.addText(q.description, {
978
+ x: x + 0.12, y: y + 0.5, w: cardW - 0.24, h: cardH - 0.95,
979
+ fontSize: 9, color: COLORS.secondary, valign: 'top', fontFace: FONTS.body,
980
+ });
981
+ }
982
+ const tags = (q.tags || []).slice(0, 5);
983
+ if (tags.length > 0) {
984
+ slide.addText(tags.join(' · '), {
985
+ x: x + 0.12, y: y + cardH - 0.42, w: cardW - 0.24, h: 0.3,
986
+ fontSize: 8, color: COLORS.secondary, valign: 'bottom', fontFace: FONTS.mono,
987
+ });
988
+ }
989
+ });
990
+ }
991
+ // x-axis
992
+ const xAxisLabel = props.xAxisLabel ?? '商业兑现度';
993
+ const xLabels = props.xAxisLabels ?? ['低 / 待验证', '高'];
994
+ slide.addShape('rect', {
995
+ x: gridX + 0.2, y: gridY + gridH + 0.2, w: gridW - 0.4, h: 0.02,
996
+ fill: { color: COLORS.border },
997
+ });
998
+ slide.addText(xLabels[0] ?? '低', {
999
+ x: gridX, y: gridY + gridH + 0.28, w: 0.8, h: 0.22,
1000
+ fontSize: 10, color: COLORS.secondary, align: 'left', fontFace: FONTS.body,
1001
+ });
1002
+ slide.addText(xLabels[1] ?? '高', {
1003
+ x: gridX + gridW - 0.8, y: gridY + gridH + 0.28, w: 0.8, h: 0.22,
1004
+ fontSize: 10, color: COLORS.secondary, align: 'right', fontFace: FONTS.body,
1005
+ });
1006
+ slide.addText(xAxisLabel, {
1007
+ x: gridX + gridW / 2 - 1.0, y: gridY + gridH + 0.28, w: 2.0, h: 0.22,
1008
+ fontSize: 10, color: COLORS.accent, align: 'center', fontFace: FONTS.mono, bold: true,
1009
+ });
1010
+ if (props.footnote) {
1011
+ slide.addText(props.footnote, {
1012
+ x: 0.65, y: 5.32, w: 8.7, h: 0.2,
1013
+ fontSize: 9, color: COLORS.secondary, align: 'center', fontFace: FONTS.mono,
1014
+ });
1015
+ }
1016
+ addTheme05SpectrumBar(slide);
1017
+ }
1018
+ function renderTheme05RiskV1(slide, props) {
1019
+ const schemeColors = {
1020
+ coral: 'E85D4E',
1021
+ amber: 'F5A623',
1022
+ teal: '0FA3B1',
1023
+ indigo: '4A58D9',
1024
+ violet: '7C3AED',
1025
+ };
1026
+ if (props.kicker) {
1027
+ slide.addText(props.kicker, {
1028
+ x: 0.65, y: 0.78, w: 4.0, h: 0.25,
1029
+ fontSize: 11, color: COLORS.accent, fontFace: FONTS.mono, bold: true,
1030
+ });
1031
+ }
1032
+ slide.addText(props.title ?? '关键风险与应对', {
1033
+ x: 0.65, y: 1.15, w: 8.7, h: 0.7,
1034
+ fontSize: 38, color: COLORS.primary, bold: true, valign: 'top', fontFace: FONTS.heading,
1035
+ });
1036
+ if (props.subtitle) {
1037
+ slide.addText(props.subtitle, {
1038
+ x: 0.65, y: 1.85, w: 8.7, h: 0.3,
1039
+ fontSize: 13, color: COLORS.secondary, valign: 'top', fontFace: FONTS.body,
1040
+ });
1041
+ }
1042
+ const items = (props.items || []).slice(0, 4);
1043
+ if (items.length > 0) {
1044
+ const cols = 2;
1045
+ const rows = 2;
1046
+ const cardGap = 0.16;
1047
+ const cardW = (8.7 - cardGap) / cols;
1048
+ const cardH = (2.85 - cardGap) / rows;
1049
+ const startX = 0.65;
1050
+ const startY = 2.4;
1051
+ items.forEach((item, idx) => {
1052
+ const col = idx % cols;
1053
+ const row = Math.floor(idx / cols);
1054
+ const x = startX + col * (cardW + cardGap);
1055
+ const y = startY + row * (cardH + cardGap);
1056
+ const scheme = schemeColors[item.scheme] || COLORS.accent;
1057
+ addTheme05Card(slide, x, y, cardW, cardH);
1058
+ slide.addShape('rect', {
1059
+ x, y, w: cardW, h: 0.04,
1060
+ fill: { color: scheme },
1061
+ });
1062
+ slide.addText(item.risk ?? '', {
1063
+ x: x + 0.12, y: y + 0.14, w: cardW - 0.6, h: 0.4,
1064
+ fontSize: 15, color: COLORS.primary, bold: true, valign: 'top', fontFace: FONTS.heading,
1065
+ });
1066
+ slide.addText(String(idx + 1).padStart(2, '0'), {
1067
+ x: x + cardW - 0.45, y: y + 0.14, w: 0.35, h: 0.3,
1068
+ fontSize: 14, color: COLORS.secondary, bold: true, align: 'right', fontFace: FONTS.mono,
1069
+ });
1070
+ if (item.impact) {
1071
+ slide.addText('影响程度', {
1072
+ x: x + 0.12, y: y + 0.62, w: 0.9, h: 0.2,
1073
+ fontSize: 8, color: COLORS.secondary, fontFace: FONTS.mono, bold: true,
1074
+ });
1075
+ slide.addText(item.impact, {
1076
+ x: x + 1.05, y: y + 0.62, w: cardW - 1.2, h: 0.22,
1077
+ fontSize: 11, color: scheme, bold: true, valign: 'top', fontFace: FONTS.body,
1078
+ });
1079
+ }
1080
+ if (item.response) {
1081
+ slide.addText('应对策略', {
1082
+ x: x + 0.12, y: y + 0.92, w: 0.9, h: 0.2,
1083
+ fontSize: 8, color: COLORS.secondary, fontFace: FONTS.mono, bold: true,
1084
+ });
1085
+ slide.addText(item.response, {
1086
+ x: x + 1.05, y: y + 0.92, w: cardW - 1.2, h: cardH - 1.05,
1087
+ fontSize: 9, color: COLORS.secondary, valign: 'top', fontFace: FONTS.body,
1088
+ });
1089
+ }
1090
+ });
1091
+ }
1092
+ if (props.footnote) {
1093
+ slide.addText(props.footnote, {
1094
+ x: 0.65, y: 5.32, w: 8.7, h: 0.2,
1095
+ fontSize: 9, color: COLORS.secondary, align: 'center', fontFace: FONTS.mono,
1096
+ });
1097
+ }
1098
+ addTheme05SpectrumBar(slide);
1099
+ }
1100
+ function renderTheme05BubbleV1(slide, props) {
1101
+ const schemeColors = {
1102
+ coral: 'E85D4E',
1103
+ amber: 'F5A623',
1104
+ teal: '0FA3B1',
1105
+ indigo: '4A58D9',
1106
+ violet: '7C3AED',
1107
+ };
1108
+ if (props.kicker) {
1109
+ slide.addText(props.kicker, {
1110
+ x: 0.65, y: 0.78, w: 4.0, h: 0.25,
1111
+ fontSize: 11, color: COLORS.accent, fontFace: FONTS.mono, bold: true,
1112
+ });
1113
+ }
1114
+ slide.addText(props.title ?? '', {
1115
+ x: 0.65, y: 1.15, w: 8.7, h: 0.7,
1116
+ fontSize: 38, color: COLORS.primary, bold: true, valign: 'top', fontFace: FONTS.heading,
1117
+ });
1118
+ if (props.subtitle) {
1119
+ slide.addText(props.subtitle, {
1120
+ x: 0.65, y: 1.85, w: 8.7, h: 0.3,
1121
+ fontSize: 13, color: COLORS.secondary, valign: 'top', fontFace: FONTS.body,
1122
+ });
1123
+ }
1124
+ const items = (props.items || []).slice(0, 16);
1125
+ if (items.length === 0) {
1126
+ addTheme05SpectrumBar(slide);
1127
+ return;
1128
+ }
1129
+ const chartX = 0.65;
1130
+ const chartY = 2.45;
1131
+ const chartW = 8.7;
1132
+ const chartH = 2.85;
1133
+ addTheme05Card(slide, chartX, chartY, chartW, chartH);
1134
+ const plotMargin = { top: 0.3, right: 0.3, bottom: 0.5, left: 0.7 };
1135
+ const plotX = chartX + plotMargin.left;
1136
+ const plotY = chartY + plotMargin.top;
1137
+ const plotW = chartW - plotMargin.left - plotMargin.right;
1138
+ const plotH = chartH - plotMargin.top - plotMargin.bottom;
1139
+ const xs = items.map((item) => Number(item.x) || 0);
1140
+ const ys = items.map((item) => Number(item.y) || 0);
1141
+ const maxX = Math.max(1, ...xs);
1142
+ const maxY = Math.max(1, ...ys);
1143
+ for (let i = 0; i <= 4; i++) {
1144
+ const x = plotX + (i / 4) * plotW;
1145
+ const y = plotY + (i / 4) * plotH;
1146
+ slide.addShape('line', {
1147
+ x1: x, y1: plotY, x2: x, y2: plotY + plotH,
1148
+ line: { color: COLORS.border, width: 0.5, dash: 'dash' },
1149
+ });
1150
+ slide.addShape('line', {
1151
+ x1: plotX, y1: y, x2: plotX + plotW, y2: y,
1152
+ line: { color: COLORS.border, width: 0.5, dash: 'dash' },
1153
+ });
1154
+ }
1155
+ slide.addText(props.xAxisLabel || '', {
1156
+ x: plotX + plotW / 2 - 1, y: chartY + chartH - 0.38, w: 2, h: 0.2,
1157
+ fontSize: 9, color: COLORS.secondary, align: 'center', fontFace: FONTS.body,
1158
+ });
1159
+ slide.addText(props.yAxisLabel || '', {
1160
+ x: chartX + 0.05, y: plotY + plotH / 2 - 0.25, w: 0.5, h: 0.5,
1161
+ fontSize: 9, color: COLORS.secondary, align: 'center', valign: 'middle', fontFace: FONTS.body,
1162
+ });
1163
+ const maxValue = Math.max(1, ...items.map((item) => Number(item.value) || 0));
1164
+ items.forEach((item) => {
1165
+ const x = plotX + ((Number(item.x) || 0) / maxX) * plotW;
1166
+ const y = plotY + plotH - ((Number(item.y) || 0) / maxY) * plotH;
1167
+ const scheme = item.scheme || 'coral';
1168
+ const color = schemeColors[scheme] ?? COLORS.accent;
1169
+ const size = Math.max(0.12, Math.min(0.34, ((Number(item.value) || 0) / maxValue) * 0.28 + 0.1));
1170
+ slide.addShape('ellipse', {
1171
+ x: x - size / 2, y: y - size / 2, w: size, h: size,
1172
+ fill: { color },
1173
+ });
1174
+ slide.addText(item.name ?? '', {
1175
+ x: x - 0.6, y: y - size / 2 - 0.22, w: 1.2, h: 0.18,
1176
+ fontSize: 8, color: COLORS.primary, bold: true, align: 'center', fontFace: FONTS.body,
1177
+ });
1178
+ });
1179
+ addTheme05SpectrumBar(slide);
1180
+ }
1181
+ function renderTheme05MapV1(slide, props) {
1182
+ const schemeColors = {
1183
+ coral: 'E85D4E',
1184
+ amber: 'F5A623',
1185
+ teal: '0FA3B1',
1186
+ indigo: '4A58D9',
1187
+ violet: '7C3AED',
1188
+ };
1189
+ if (props.kicker) {
1190
+ slide.addText(props.kicker, {
1191
+ x: 0.65, y: 0.78, w: 4.0, h: 0.25,
1192
+ fontSize: 11, color: COLORS.accent, fontFace: FONTS.mono, bold: true,
1193
+ });
1194
+ }
1195
+ slide.addText(props.title ?? '', {
1196
+ x: 0.65, y: 1.15, w: 8.7, h: 0.7,
1197
+ fontSize: 38, color: COLORS.primary, bold: true, valign: 'top', fontFace: FONTS.heading,
1198
+ });
1199
+ if (props.subtitle) {
1200
+ slide.addText(props.subtitle, {
1201
+ x: 0.65, y: 1.85, w: 8.7, h: 0.3,
1202
+ fontSize: 13, color: COLORS.secondary, valign: 'top', fontFace: FONTS.body,
1203
+ });
1204
+ }
1205
+ const items = (props.items || []).slice(0, 10);
1206
+ if (items.length === 0) {
1207
+ addTheme05SpectrumBar(slide);
1208
+ return;
1209
+ }
1210
+ const chartX = 0.65;
1211
+ const chartY = 2.45;
1212
+ const chartW = 8.7;
1213
+ const chartH = 2.85;
1214
+ addTheme05Card(slide, chartX, chartY, chartW, chartH);
1215
+ const sorted = [...items].sort((a, b) => (Number(b.value) || 0) - (Number(a.value) || 0));
1216
+ const maxValue = Math.max(1, ...sorted.map((item) => Number(item.value) || 0));
1217
+ const rowH = chartH / sorted.length;
1218
+ const labelW = 1.4;
1219
+ const plotX = chartX + labelW + 0.2;
1220
+ const plotW = chartW - labelW - 1.2;
1221
+ sorted.forEach((item, idx) => {
1222
+ const y = chartY + idx * rowH;
1223
+ const value = Number(item.value) || 0;
1224
+ const barW = (value / maxValue) * plotW;
1225
+ const scheme = item.scheme || 'coral';
1226
+ const color = schemeColors[scheme] ?? COLORS.accent;
1227
+ slide.addText(item.name ?? '', {
1228
+ x: chartX + 0.15, y, w: labelW, h: rowH,
1229
+ fontSize: 11, color: COLORS.primary, bold: true, valign: 'middle', fontFace: FONTS.body,
1230
+ });
1231
+ slide.addShape('roundRect', {
1232
+ x: plotX, y: y + rowH * 0.22, w: Math.max(0.04, barW), h: rowH * 0.56,
1233
+ fill: { color }, rectRadius: 0.04,
1234
+ });
1235
+ slide.addText(`${value} ${props.unit ?? ''}`, {
1236
+ x: plotX + barW + 0.08, y, w: 1.0, h: rowH,
1237
+ fontSize: 10, color: COLORS.secondary, bold: true, valign: 'middle', fontFace: FONTS.body,
1238
+ });
1239
+ });
1240
+ addTheme05SpectrumBar(slide);
1241
+ }
1242
+ function renderTheme05TableOfContentsV1(slide, props) {
1243
+ if (props.subtitle) {
1244
+ slide.addText(props.subtitle, {
1245
+ x: 0.65, y: 0.78, w: 4.0, h: 0.25,
1246
+ fontSize: 11, color: COLORS.secondary, fontFace: FONTS.mono,
1247
+ });
1248
+ }
1249
+ slide.addText(props.title ?? '', {
1250
+ x: 0.65, y: 1.2, w: 4.2, h: 0.8,
1251
+ fontSize: 42, color: COLORS.primary, bold: true, valign: 'top', fontFace: FONTS.heading,
1252
+ });
1253
+ slide.addShape('rect', {
1254
+ x: 0.65, y: 2.05, w: 1.4, h: 0.05,
1255
+ fill: { color: COLORS.accent },
1256
+ });
1257
+ const items = (props.items || []).slice(0, 6);
1258
+ if (items.length > 0) {
1259
+ const cols = Math.min(items.length, 2);
1260
+ const cardW = 4.0;
1261
+ const cardH = 1.05;
1262
+ const gapX = 0.2;
1263
+ const gapY = 0.18;
1264
+ const startX = 5.05;
1265
+ const startY = 0.85;
1266
+ items.forEach((item, idx) => {
1267
+ const col = idx % cols;
1268
+ const row = Math.floor(idx / cols);
1269
+ const x = startX + col * (cardW + gapX);
1270
+ const y = startY + row * (cardH + gapY);
1271
+ addTheme05Card(slide, x, y, cardW, cardH);
1272
+ slide.addText(String(idx + 1).padStart(2, '0'), {
1273
+ x: x + 0.15, y: y + 0.18, w: 0.6, h: 0.6,
1274
+ fontSize: 28, color: COLORS.accent, bold: true, valign: 'top', fontFace: FONTS.heading,
1275
+ });
1276
+ slide.addText(item.title ?? '', {
1277
+ x: x + 0.85, y: y + 0.2, w: cardW - 1.1, h: 0.35,
1278
+ fontSize: 15, color: COLORS.primary, bold: true, valign: 'top', fontFace: FONTS.body,
1279
+ });
1280
+ if (item.page) {
1281
+ slide.addText(`P.${item.page}`, {
1282
+ x: x + 0.85, y: y + 0.55, w: cardW - 1.1, h: 0.25,
1283
+ fontSize: 11, color: COLORS.secondary, valign: 'top', fontFace: FONTS.mono,
1284
+ });
1285
+ }
1286
+ });
1287
+ }
1288
+ addTheme05SpectrumBar(slide);
1289
+ }
1290
+ function renderTheme05ClosingV1(slide, props) {
1291
+ if (props.kicker) {
1292
+ slide.addText(props.kicker, {
1293
+ x: 0.65, y: 0.78, w: 4.0, h: 0.25,
1294
+ fontSize: 11, color: COLORS.accent, fontFace: FONTS.mono, bold: true,
1295
+ });
1296
+ }
1297
+ slide.addText(props.claim ?? '', {
1298
+ x: 0.65, y: 1.6, w: 8.7, h: 1.4,
1299
+ fontSize: 42, color: COLORS.primary, bold: true, valign: 'top', fontFace: FONTS.heading,
1300
+ });
1301
+ const points = (props.points || []).slice(0, 4);
1302
+ if (points.length > 0) {
1303
+ const cardW = 2.05;
1304
+ const gap = 0.18;
1305
+ const startX = (10 - points.length * cardW - (points.length - 1) * gap) / 2;
1306
+ points.forEach((p, idx) => {
1307
+ const x = startX + idx * (cardW + gap);
1308
+ const y = 3.4;
1309
+ addTheme05Card(slide, x, y, cardW, 1.1, idx === 0);
1310
+ slide.addText(p.value ?? '', {
1311
+ x, y: y + 0.15, w: cardW, h: 0.45,
1312
+ fontSize: 28, color: idx === 0 ? COLORS.white : COLORS.primary, bold: true, align: 'center', fontFace: FONTS.heading,
1313
+ });
1314
+ slide.addText(p.label ?? '', {
1315
+ x, y: y + 0.62, w: cardW, h: 0.25,
1316
+ fontSize: 10, color: idx === 0 ? 'FFFFFF' : COLORS.secondary, align: 'center', fontFace: FONTS.body,
1317
+ });
1318
+ });
1319
+ }
1320
+ addTheme05SpectrumBar(slide);
1321
+ const footerText = [props.footnoteLeft, props.footnoteRight].filter(Boolean).join(' ');
1322
+ if (footerText) {
1323
+ slide.addText(footerText, {
1324
+ x: 0.65, y: 5.32, w: 8.7, h: 0.2,
1325
+ fontSize: 9, color: COLORS.secondary, align: 'center', fontFace: FONTS.mono,
1326
+ });
1327
+ }
1328
+ }
1329
+ function renderTheme05CoverExV1(slide, props) {
1330
+ if (props.tag) {
1331
+ slide.addText(props.tag, {
1332
+ x: 0.65, y: 0.78, w: 4.0, h: 0.25,
1333
+ fontSize: 12, color: COLORS.accent, fontFace: FONTS.mono, bold: true,
1334
+ });
1335
+ }
1336
+ slide.addText(props.title ?? '', {
1337
+ x: 0.65, y: 1.2, w: 5.0, h: 1.6,
1338
+ fontSize: 48, color: COLORS.primary, bold: true, valign: 'top', fontFace: FONTS.heading,
1339
+ });
1340
+ if (props.subtitle) {
1341
+ slide.addText(props.subtitle, {
1342
+ x: 0.65, y: 2.85, w: 5.0, h: 0.6,
1343
+ fontSize: 16, color: COLORS.secondary, valign: 'top', fontFace: FONTS.body,
1344
+ });
1345
+ }
1346
+ addTheme05SpectrumBar(slide);
1347
+ const stats = (props.stats || []).slice(0, 4);
1348
+ const cardW = 2.15;
1349
+ const cardH = 1.05;
1350
+ const gapX = 0.18;
1351
+ const gapY = 0.18;
1352
+ const startX = 5.85;
1353
+ const startY = 0.95;
1354
+ stats.forEach((s, idx) => {
1355
+ const col = idx % 2;
1356
+ const row = Math.floor(idx / 2);
1357
+ const x = startX + col * (cardW + gapX);
1358
+ const y = startY + row * (cardH + gapY);
1359
+ const color = theme05SchemeColor(s.scheme);
1360
+ slide.addShape('roundRect', {
1361
+ x, y, w: cardW, h: cardH,
1362
+ fill: { color: COLORS.surfaceElevated },
1363
+ line: { color: COLORS.border, width: 1 },
1364
+ rectRadius: 0.08,
1365
+ });
1366
+ slide.addShape('rect', {
1367
+ x, y, w: cardW, h: 0.06,
1368
+ fill: { color },
1369
+ });
1370
+ slide.addText(`${s.value ?? ''}${s.unit ?? ''}`, {
1371
+ x: x + 0.12, y: y + 0.18, w: cardW - 0.24, h: 0.42,
1372
+ fontSize: 26, color: COLORS.primary, bold: true, fontFace: FONTS.heading,
1373
+ });
1374
+ slide.addText(s.label ?? '', {
1375
+ x: x + 0.12, y: y + 0.62, w: cardW - 0.24, h: 0.28,
1376
+ fontSize: 10, color: COLORS.secondary, fontFace: FONTS.body,
1377
+ });
1378
+ });
1379
+ const footerText = [props.footnoteLeft, props.footnoteRight].filter(Boolean).join(' ');
1380
+ if (footerText) {
1381
+ slide.addText(footerText, {
1382
+ x: 0.65, y: 5.32, w: 8.7, h: 0.2,
1383
+ fontSize: 9, color: COLORS.secondary, align: 'center', fontFace: FONTS.mono,
1384
+ });
1385
+ }
1386
+ }
1387
+ function renderTheme05CoverExV2(slide, props) {
1388
+ if (props.backgroundNumber) {
1389
+ slide.addText(props.backgroundNumber, {
1390
+ x: 4.5, y: 0.6, w: 5.5, h: 2.0,
1391
+ fontSize: 160, color: COLORS.primary, bold: true, align: 'right', valign: 'top', fontFace: FONTS.heading,
1392
+ transparency: 92,
1393
+ });
1394
+ }
1395
+ if (props.tag) {
1396
+ slide.addText(props.tag, {
1397
+ x: 0.65, y: 0.78, w: 4.0, h: 0.25,
1398
+ fontSize: 12, color: COLORS.accent, fontFace: FONTS.mono, bold: true,
1399
+ });
1400
+ }
1401
+ const highlights = (props.highlights || []).slice(0, 5);
1402
+ if (highlights.length > 0) {
1403
+ let pillX = 0.65;
1404
+ const pillY = 1.2;
1405
+ highlights.forEach((h) => {
1406
+ const text = typeof h === 'string' ? h : h.text ?? '';
1407
+ if (!text)
1408
+ return;
1409
+ const color = theme05SchemeColor(typeof h === 'string' ? undefined : h.scheme);
1410
+ const approxW = Math.min(2.2, 0.24 + text.length * 0.11);
1411
+ addTheme05Pill(slide, pillX, pillY, approxW, 0.32, text, color);
1412
+ pillX += approxW + 0.12;
1413
+ });
1414
+ }
1415
+ slide.addText(props.title ?? '', {
1416
+ x: 0.65, y: 3.1, w: 8.7, h: 1.2,
1417
+ fontSize: 54, color: COLORS.primary, bold: true, valign: 'top', fontFace: FONTS.heading,
1418
+ });
1419
+ if (props.subtitle) {
1420
+ slide.addText(props.subtitle, {
1421
+ x: 0.65, y: 4.3, w: 8.7, h: 0.45,
1422
+ fontSize: 16, color: COLORS.secondary, valign: 'top', fontFace: FONTS.body,
1423
+ });
1424
+ }
1425
+ addTheme05SpectrumBar(slide);
1426
+ const footerText = [props.footnoteLeft, props.footnoteRight].filter(Boolean).join(' ');
1427
+ if (footerText) {
1428
+ slide.addText(footerText, {
1429
+ x: 0.65, y: 5.32, w: 8.7, h: 0.2,
1430
+ fontSize: 9, color: COLORS.secondary, align: 'center', fontFace: FONTS.mono,
1431
+ });
1432
+ }
1433
+ }
1434
+ function renderTheme05CoverHeroV1(slide, props) {
1435
+ const imageUrl = props.image;
1436
+ if (imageUrl) {
1437
+ addImageMaybe(slide, imageUrl, 0, 0, 10, 5.625);
1438
+ }
1439
+ else {
1440
+ slide.addShape('rect', {
1441
+ x: 0, y: 0, w: 10, h: 5.625,
1442
+ fill: { color: COLORS.light },
1443
+ });
1444
+ slide.addShape('rect', {
1445
+ x: 4.2, y: 2.0, w: 1.6, h: 1.6,
1446
+ fill: { color: COLORS.border },
1447
+ line: { color: COLORS.secondary, width: 1, dashType: 'dash' },
1448
+ });
1449
+ slide.addText('[图片]', {
1450
+ x: 4.2, y: 2.6, w: 1.6, h: 0.3,
1451
+ fontSize: 12, color: COLORS.secondary, align: 'center', fontFace: FONTS.body,
1452
+ });
1453
+ }
1454
+ // 左侧渐变遮罩:用半透明矩形从白到透明模拟
1455
+ slide.addShape('rect', {
1456
+ x: 0, y: 0, w: 5.2, h: 5.625,
1457
+ fill: {
1458
+ color: COLORS.white,
1459
+ gradient: {
1460
+ type: 'linear',
1461
+ angle: 0,
1462
+ stops: [
1463
+ { position: 0, color: COLORS.white },
1464
+ { position: 1, color: `${COLORS.white}00` },
1465
+ ],
1466
+ },
1467
+ },
1468
+ });
1469
+ if (props.tag) {
1470
+ slide.addText(props.tag, {
1471
+ x: 0.65, y: 0.78, w: 4.0, h: 0.25,
1472
+ fontSize: 12, color: COLORS.accent, fontFace: FONTS.mono, bold: true,
1473
+ });
1474
+ }
1475
+ slide.addText(props.title ?? '', {
1476
+ x: 0.65, y: 3.0, w: 6.5, h: 1.2,
1477
+ fontSize: 50, color: COLORS.primary, bold: true, valign: 'top', fontFace: FONTS.heading,
1478
+ });
1479
+ if (props.subtitle) {
1480
+ slide.addText(props.subtitle, {
1481
+ x: 0.65, y: 4.2, w: 6.5, h: 0.5,
1482
+ fontSize: 16, color: COLORS.secondary, valign: 'top', fontFace: FONTS.body,
1483
+ });
1484
+ }
1485
+ addTheme05SpectrumBar(slide);
1486
+ const footerText = [props.footnoteLeft, props.footnoteRight].filter(Boolean).join(' ');
1487
+ if (footerText) {
1488
+ slide.addText(footerText, {
1489
+ x: 0.65, y: 5.32, w: 8.7, h: 0.2,
1490
+ fontSize: 9, color: COLORS.secondary, align: 'center', fontFace: FONTS.mono,
1491
+ });
1492
+ }
1493
+ }
1494
+ function renderTheme05ChapterBigV1(slide, props) {
1495
+ if (props.tag) {
1496
+ slide.addText(props.tag, {
1497
+ x: 0.65, y: 0.78, w: 4.0, h: 0.25,
1498
+ fontSize: 12, color: COLORS.accent, fontFace: FONTS.mono, bold: true,
1499
+ });
1500
+ }
1501
+ slide.addText(props.title ?? '', {
1502
+ x: 0.65, y: 1.8, w: 8.7, h: 1.5,
1503
+ fontSize: 64, color: COLORS.primary, bold: true, align: 'center', valign: 'middle', fontFace: FONTS.heading,
1504
+ });
1505
+ if (props.subtitle) {
1506
+ slide.addText(props.subtitle, {
1507
+ x: 0.65, y: 3.4, w: 8.7, h: 0.6,
1508
+ fontSize: 18, color: COLORS.secondary, align: 'center', valign: 'top', fontFace: FONTS.body,
1509
+ });
1510
+ }
1511
+ addTheme05SpectrumBar(slide);
1512
+ }
1513
+ function renderTheme05ChapterSplitV1(slide, props) {
1514
+ slide.addShape('rect', {
1515
+ x: 0, y: 0, w: 3.0, h: 5.625,
1516
+ fill: { color: COLORS.accent },
1517
+ });
1518
+ slide.addText(props.number ?? '', {
1519
+ x: 0, y: 1.9, w: 3.0, h: 1.4,
1520
+ fontSize: 110, color: COLORS.white, bold: true, align: 'center', valign: 'middle', fontFace: FONTS.heading,
1521
+ });
1522
+ slide.addText(props.title ?? '', {
1523
+ x: 3.4, y: 1.8, w: 6.2, h: 1.2,
1524
+ fontSize: 48, color: COLORS.primary, bold: true, valign: 'top', fontFace: FONTS.heading,
1525
+ });
1526
+ if (props.subtitle) {
1527
+ slide.addText(props.subtitle, {
1528
+ x: 3.4, y: 3.05, w: 6.2, h: 0.6,
1529
+ fontSize: 16, color: COLORS.secondary, valign: 'top', fontFace: FONTS.body,
1530
+ });
1531
+ }
1532
+ addTheme05SpectrumBar(slide);
1533
+ }
1534
+ function renderTheme05ChapterNumberedV1(slide, props) {
1535
+ slide.addText(props.number ?? '', {
1536
+ x: 0.65, y: 0.9, w: 8.7, h: 0.9,
1537
+ fontSize: 80, color: COLORS.accent, bold: true, align: 'center', valign: 'top', fontFace: FONTS.heading,
1538
+ });
1539
+ slide.addText(props.title ?? '', {
1540
+ x: 0.65, y: 2.0, w: 8.7, h: 1.2,
1541
+ fontSize: 54, color: COLORS.primary, bold: true, align: 'center', valign: 'top', fontFace: FONTS.heading,
1542
+ });
1543
+ if (props.subtitle) {
1544
+ slide.addText(props.subtitle, {
1545
+ x: 0.65, y: 3.25, w: 8.7, h: 0.5,
1546
+ fontSize: 16, color: COLORS.secondary, align: 'center', valign: 'top', fontFace: FONTS.body,
1547
+ });
1548
+ }
1549
+ slide.addShape('rect', {
1550
+ x: 4.5, y: 3.9, w: 1.0, h: 0.05,
1551
+ fill: { color: COLORS.accent },
1552
+ });
1553
+ addTheme05SpectrumBar(slide);
1554
+ }
1555
+ function renderTheme05ChapterImageV1(slide, props) {
1556
+ const imageUrl = props.image;
1557
+ if (imageUrl) {
1558
+ addImageMaybe(slide, imageUrl, 0, 0, 10, 5.625);
1559
+ }
1560
+ else {
1561
+ slide.addShape('rect', {
1562
+ x: 0, y: 0, w: 10, h: 5.625,
1563
+ fill: { color: COLORS.light },
1564
+ });
1565
+ slide.addShape('rect', {
1566
+ x: 4.2, y: 2.0, w: 1.6, h: 1.6,
1567
+ fill: { color: COLORS.border },
1568
+ line: { color: COLORS.secondary, width: 1, dashType: 'dash' },
1569
+ });
1570
+ slide.addText('[图片]', {
1571
+ x: 4.2, y: 2.6, w: 1.6, h: 0.3,
1572
+ fontSize: 12, color: COLORS.secondary, align: 'center', fontFace: FONTS.body,
1573
+ });
1574
+ }
1575
+ // 底部渐变遮罩
1576
+ slide.addShape('rect', {
1577
+ x: 0, y: 2.8, w: 10, h: 2.825,
1578
+ fill: {
1579
+ color: COLORS.white,
1580
+ gradient: {
1581
+ type: 'linear',
1582
+ angle: 270,
1583
+ stops: [
1584
+ { position: 0, color: `${COLORS.white}00` },
1585
+ { position: 1, color: COLORS.white },
1586
+ ],
1587
+ },
1588
+ },
1589
+ });
1590
+ slide.addText(props.number ?? '', {
1591
+ x: 0.65, y: 3.2, w: 8.7, h: 0.6,
1592
+ fontSize: 48, color: COLORS.accent, bold: true, fontFace: FONTS.heading,
1593
+ });
1594
+ slide.addText(props.title ?? '', {
1595
+ x: 0.65, y: 3.85, w: 8.7, h: 0.9,
1596
+ fontSize: 42, color: COLORS.primary, bold: true, valign: 'top', fontFace: FONTS.heading,
1597
+ });
1598
+ if (props.subtitle) {
1599
+ slide.addText(props.subtitle, {
1600
+ x: 0.65, y: 4.75, w: 8.7, h: 0.35,
1601
+ fontSize: 14, color: COLORS.secondary, valign: 'top', fontFace: FONTS.body,
1602
+ });
1603
+ }
1604
+ addTheme05SpectrumBar(slide);
1605
+ }
1606
+ function renderTheme05MetricHeroV1(slide, props) {
1607
+ if (props.kicker) {
1608
+ slide.addText(props.kicker, {
1609
+ x: 0.65, y: 0.78, w: 4.0, h: 0.25,
1610
+ fontSize: 11, color: COLORS.accent, fontFace: FONTS.mono, bold: true,
1611
+ });
1612
+ }
1613
+ slide.addText(props.title ?? '', {
1614
+ x: 0.65, y: 1.15, w: 8.7, h: 0.8,
1615
+ fontSize: 38, color: COLORS.primary, bold: true, align: 'center', valign: 'top', fontFace: FONTS.heading,
1616
+ });
1617
+ if (props.subtitle) {
1618
+ slide.addText(props.subtitle, {
1619
+ x: 0.65, y: 1.95, w: 8.7, h: 0.35,
1620
+ fontSize: 13, color: COLORS.secondary, align: 'center', valign: 'top', fontFace: FONTS.body,
1621
+ });
1622
+ }
1623
+ slide.addText(props.value ?? '', {
1624
+ x: 0.65, y: 2.6, w: 8.7, h: 1.2,
1625
+ fontSize: 84, color: COLORS.accent, bold: true, align: 'center', valign: 'top', fontFace: FONTS.heading,
1626
+ });
1627
+ if (props.unit) {
1628
+ slide.addText(props.unit, {
1629
+ x: 0.65, y: 3.6, w: 8.7, h: 0.35,
1630
+ fontSize: 22, color: COLORS.secondary, align: 'center', valign: 'top', fontFace: FONTS.heading,
1631
+ });
1632
+ }
1633
+ if (props.change || props.changeLabel) {
1634
+ const badgeW = 2.4;
1635
+ const badgeX = (10 - badgeW) / 2;
1636
+ slide.addShape('roundRect', {
1637
+ x: badgeX, y: 4.05, w: badgeW, h: 0.42,
1638
+ fill: { color: COLORS.accent },
1639
+ rectRadius: 0.21,
1640
+ });
1641
+ const text = [props.change, props.changeLabel].filter(Boolean).join(' ');
1642
+ slide.addText(text, {
1643
+ x: badgeX, y: 4.07, w: badgeW, h: 0.38,
1644
+ fontSize: 12, color: COLORS.white, bold: true, align: 'center', valign: 'middle', fontFace: FONTS.body,
1645
+ });
1646
+ }
1647
+ addTheme05SpectrumBar(slide);
1648
+ }
1649
+ function renderTheme05MetricCapacityV1(slide, props) {
1650
+ if (props.kicker) {
1651
+ slide.addText(props.kicker, {
1652
+ x: 0.65, y: 0.78, w: 4.0, h: 0.25,
1653
+ fontSize: 11, color: COLORS.accent, fontFace: FONTS.mono, bold: true,
1654
+ });
1655
+ }
1656
+ slide.addText(props.title ?? '', {
1657
+ x: 0.65, y: 1.15, w: 8.7, h: 0.8,
1658
+ fontSize: 38, color: COLORS.primary, bold: true, valign: 'top', fontFace: FONTS.heading,
1659
+ });
1660
+ if (props.subtitle) {
1661
+ slide.addText(props.subtitle, {
1662
+ x: 0.65, y: 1.95, w: 8.7, h: 0.35,
1663
+ fontSize: 13, color: COLORS.secondary, valign: 'top', fontFace: FONTS.body,
1664
+ });
1665
+ }
1666
+ const items = (props.items || []).slice(0, 6);
1667
+ const startY = 2.55;
1668
+ const rowH = 0.55;
1669
+ items.forEach((item, idx) => {
1670
+ const y = startY + idx * rowH;
1671
+ const name = item.name ?? '';
1672
+ const value = Number(item.value) || 0;
1673
+ const max = Math.max(Number(item.max) || 100, value);
1674
+ const unit = item.unit ?? '';
1675
+ const color = theme05SchemeColor(item.scheme);
1676
+ slide.addText(name, {
1677
+ x: 0.65, y, w: 2.0, h: 0.3,
1678
+ fontSize: 12, color: COLORS.primary, bold: true, valign: 'middle', fontFace: FONTS.body,
1679
+ });
1680
+ slide.addText(`${value}${unit}`, {
1681
+ x: 8.05, y, w: 1.3, h: 0.3,
1682
+ fontSize: 12, color: COLORS.primary, bold: true, align: 'right', valign: 'middle', fontFace: FONTS.heading,
1683
+ });
1684
+ const trackX = 2.75;
1685
+ const trackW = 5.15;
1686
+ slide.addShape('roundRect', {
1687
+ x: trackX, y: y + 0.08, w: trackW, h: 0.14,
1688
+ fill: { color: COLORS.border },
1689
+ rectRadius: 0.07,
1690
+ });
1691
+ const fillW = max > 0 ? (value / max) * trackW : 0;
1692
+ if (fillW > 0) {
1693
+ slide.addShape('roundRect', {
1694
+ x: trackX, y: y + 0.08, w: fillW, h: 0.14,
1695
+ fill: { color },
1696
+ rectRadius: 0.07,
1697
+ });
1698
+ }
1699
+ });
1700
+ addTheme05SpectrumBar(slide);
1701
+ }
1702
+ function renderTheme05MetricDeltaV1(slide, props) {
1703
+ if (props.kicker) {
1704
+ slide.addText(props.kicker, {
1705
+ x: 0.65, y: 0.78, w: 4.0, h: 0.25,
1706
+ fontSize: 11, color: COLORS.accent, fontFace: FONTS.mono, bold: true,
1707
+ });
1708
+ }
1709
+ slide.addText(props.title ?? '', {
1710
+ x: 0.65, y: 1.15, w: 8.7, h: 0.7,
1711
+ fontSize: 38, color: COLORS.primary, bold: true, valign: 'top', fontFace: FONTS.heading,
1712
+ });
1713
+ if (props.subtitle) {
1714
+ slide.addText(props.subtitle, {
1715
+ x: 0.65, y: 1.85, w: 8.7, h: 0.35,
1716
+ fontSize: 13, color: COLORS.secondary, valign: 'top', fontFace: FONTS.body,
1717
+ });
1718
+ }
1719
+ const colW = 2.6;
1720
+ const startX = (10 - colW * 3 - 0.6) / 2;
1721
+ // 当前值
1722
+ slide.addShape('roundRect', {
1723
+ x: startX, y: 2.4, w: colW, h: 1.4,
1724
+ fill: { color: COLORS.surfaceElevated },
1725
+ line: { color: COLORS.border, width: 1 },
1726
+ rectRadius: 0.1,
1727
+ });
1728
+ slide.addText(`${props.currentValue ?? ''}${props.currentUnit ?? ''}`, {
1729
+ x: startX, y: 2.55, w: colW, h: 0.55,
1730
+ fontSize: 34, color: COLORS.accent, bold: true, align: 'center', fontFace: FONTS.heading,
1731
+ });
1732
+ slide.addText(props.currentLabel ?? '当前值', {
1733
+ x: startX, y: 3.15, w: colW, h: 0.25,
1734
+ fontSize: 10, color: COLORS.secondary, align: 'center', fontFace: FONTS.body,
1735
+ });
1736
+ // Delta 徽章
1737
+ const deltaX = startX + colW + 0.3;
1738
+ slide.addShape('roundRect', {
1739
+ x: deltaX, y: 2.65, w: colW, h: 0.9,
1740
+ fill: { color: COLORS.accent },
1741
+ rectRadius: 0.1,
1742
+ });
1743
+ slide.addText(props.delta ?? '', {
1744
+ x: deltaX, y: 2.72, w: colW, h: 0.4,
1745
+ fontSize: 28, color: COLORS.white, bold: true, align: 'center', fontFace: FONTS.heading,
1746
+ });
1747
+ slide.addText(props.deltaLabel ?? '变化', {
1748
+ x: deltaX, y: 3.15, w: colW, h: 0.25,
1749
+ fontSize: 10, color: COLORS.white, align: 'center', fontFace: FONTS.body,
1750
+ });
1751
+ // 对比值
1752
+ const prevX = deltaX + colW + 0.3;
1753
+ slide.addShape('roundRect', {
1754
+ x: prevX, y: 2.4, w: colW, h: 1.4,
1755
+ fill: { color: COLORS.surfaceElevated },
1756
+ line: { color: COLORS.border, width: 1 },
1757
+ rectRadius: 0.1,
1758
+ });
1759
+ slide.addText(`${props.previousValue ?? ''}${props.previousUnit ?? ''}`, {
1760
+ x: prevX, y: 2.55, w: colW, h: 0.55,
1761
+ fontSize: 34, color: COLORS.primary, bold: true, align: 'center', fontFace: FONTS.heading,
1762
+ });
1763
+ slide.addText(props.previousLabel ?? '对比值', {
1764
+ x: prevX, y: 3.15, w: colW, h: 0.25,
1765
+ fontSize: 10, color: COLORS.secondary, align: 'center', fontFace: FONTS.body,
1766
+ });
1767
+ // 迷你折线
1768
+ const labels = (props.labels || []).slice(0, 12);
1769
+ const data = (props.data || []).slice(0, 12).map((d) => Number(d) || 0);
1770
+ if (labels.length > 1 && data.length > 1) {
1771
+ slide.addChart('line', [{ name: props.title || '', labels, values: data }], {
1772
+ x: 0.85, y: 4.05, w: 8.3, h: 1.25,
1773
+ chartColors: [COLORS.accent],
1774
+ lineDataSymbol: 'none',
1775
+ lineSmooth: true,
1776
+ showLegend: false,
1777
+ showValue: false,
1778
+ catAxisHidden: true,
1779
+ valAxisHidden: true,
1780
+ gridLineColor: COLORS.surface,
1781
+ });
1782
+ }
1783
+ addTheme05SpectrumBar(slide);
1784
+ }
1785
+ function renderTheme05ChartShareV1(slide, props) {
1786
+ if (props.kicker) {
1787
+ slide.addText(props.kicker, {
1788
+ x: 0.65, y: 0.78, w: 4.0, h: 0.25,
1789
+ fontSize: 11, color: COLORS.accent, fontFace: FONTS.mono, bold: true,
1790
+ });
1791
+ }
1792
+ slide.addText(props.title ?? '', {
1793
+ x: 0.65, y: 1.15, w: 8.7, h: 0.7,
1794
+ fontSize: 38, color: COLORS.primary, bold: true, valign: 'top', fontFace: FONTS.heading,
1795
+ });
1796
+ if (props.subtitle) {
1797
+ slide.addText(props.subtitle, {
1798
+ x: 0.65, y: 1.85, w: 8.7, h: 0.35,
1799
+ fontSize: 13, color: COLORS.secondary, valign: 'top', fontFace: FONTS.body,
1800
+ });
1801
+ }
1802
+ const items = (props.items || []).slice(0, 8);
1803
+ const hasConclusion = props.showConclusion !== false && !!props.conclusion && (!!props.conclusion.value || !!props.conclusion.label || !!props.conclusion.description);
1804
+ const chartW = hasConclusion ? 5.7 : 8.7;
1805
+ if (items.length > 0) {
1806
+ const data = items.map((item) => ({
1807
+ name: item.name ?? '',
1808
+ labels: [item.name ?? ''],
1809
+ values: [Number(item.value) || 0],
1810
+ }));
1811
+ slide.addChart('pie', data, {
1812
+ x: 0.65, y: 2.05, w: chartW, h: 3.0,
1813
+ chartColors: items.map((item) => theme05SchemeColor(item.scheme)),
1814
+ showValue: false,
1815
+ showLegend: false,
1816
+ dataLabelColor: COLORS.primary,
1817
+ dataLabelFontSize: 10,
1818
+ });
1819
+ }
1820
+ else {
1821
+ slide.addText('(暂无图表数据)', {
1822
+ x: 0.65, y: 3.0, w: chartW, h: 0.6,
1823
+ fontSize: 16, color: COLORS.secondary, align: 'center', valign: 'middle', fontFace: FONTS.body,
1824
+ });
1825
+ }
1826
+ if (hasConclusion) {
1827
+ addTheme05ConclusionCard(slide, 6.55, 2.05, 2.8, 3.0, props.conclusion);
1828
+ }
1829
+ addTheme05SpectrumBar(slide);
1830
+ }
1831
+ function renderTheme05ChartStackedV1(slide, props) {
1832
+ if (props.kicker) {
1833
+ slide.addText(props.kicker, {
1834
+ x: 0.65, y: 0.78, w: 4.0, h: 0.25,
1835
+ fontSize: 11, color: COLORS.accent, fontFace: FONTS.mono, bold: true,
1836
+ });
1837
+ }
1838
+ slide.addText(props.title ?? '', {
1839
+ x: 0.65, y: 1.15, w: 8.7, h: 0.7,
1840
+ fontSize: 38, color: COLORS.primary, bold: true, valign: 'top', fontFace: FONTS.heading,
1841
+ });
1842
+ if (props.subtitle) {
1843
+ slide.addText(props.subtitle, {
1844
+ x: 0.65, y: 1.85, w: 8.7, h: 0.35,
1845
+ fontSize: 13, color: COLORS.secondary, valign: 'top', fontFace: FONTS.body,
1846
+ });
1847
+ }
1848
+ const labels = (props.labels || []).slice(0, 12);
1849
+ const rawSeries = (props.series || []).slice(0, 5);
1850
+ const series = rawSeries.map((s) => ({
1851
+ name: s.name ?? '',
1852
+ labels,
1853
+ values: (s.data || []).slice(0, labels.length).map((d) => Number(d) || 0),
1854
+ }));
1855
+ const hasConclusion = props.showConclusion !== false && !!props.conclusion && (!!props.conclusion.value || !!props.conclusion.label || !!props.conclusion.description);
1856
+ const chartW = hasConclusion ? 5.7 : 8.7;
1857
+ if (labels.length > 0 && series.length > 0) {
1858
+ slide.addChart('bar', series, {
1859
+ x: 0.65, y: 2.05, w: chartW, h: 3.0,
1860
+ chartColors: rawSeries.map((s) => theme05SchemeColor(s.scheme)),
1861
+ barGrouping: 'stacked',
1862
+ barDir: 'col',
1863
+ showValue: false,
1864
+ });
1865
+ }
1866
+ else {
1867
+ slide.addText('(暂无图表数据)', {
1868
+ x: 0.65, y: 3.0, w: chartW, h: 0.6,
1869
+ fontSize: 16, color: COLORS.secondary, align: 'center', valign: 'middle', fontFace: FONTS.body,
1870
+ });
1871
+ }
1872
+ if (hasConclusion) {
1873
+ addTheme05ConclusionCard(slide, 6.55, 2.05, 2.8, 3.0, props.conclusion);
1874
+ }
1875
+ addTheme05SpectrumBar(slide);
1876
+ }
1877
+ function renderTheme05ChartCurveV1(slide, props) {
1878
+ if (props.kicker) {
1879
+ slide.addText(props.kicker, {
1880
+ x: 0.65, y: 0.78, w: 4.0, h: 0.25,
1881
+ fontSize: 11, color: COLORS.accent, fontFace: FONTS.mono, bold: true,
1882
+ });
1883
+ }
1884
+ slide.addText(props.title ?? '', {
1885
+ x: 0.65, y: 1.15, w: 8.7, h: 0.7,
1886
+ fontSize: 38, color: COLORS.primary, bold: true, valign: 'top', fontFace: FONTS.heading,
1887
+ });
1888
+ if (props.subtitle) {
1889
+ slide.addText(props.subtitle, {
1890
+ x: 0.65, y: 1.85, w: 8.7, h: 0.35,
1891
+ fontSize: 13, color: COLORS.secondary, valign: 'top', fontFace: FONTS.body,
1892
+ });
1893
+ }
1894
+ const labels = (props.labels || []).slice(0, 12);
1895
+ const values = (props.data || []).slice(0, 12).map((d) => Number(d) || 0);
1896
+ const hasConclusion = props.showConclusion !== false && !!props.conclusion && (!!props.conclusion.value || !!props.conclusion.label || !!props.conclusion.description);
1897
+ const chartW = hasConclusion ? 5.7 : 8.7;
1898
+ if (labels.length > 0 && values.length > 0) {
1899
+ slide.addChart('line', [{ name: props.title || '', labels, values }], {
1900
+ x: 0.65, y: 2.05, w: chartW, h: 3.0,
1901
+ chartColors: [COLORS.accent],
1902
+ lineDataSymbol: 'circle',
1903
+ lineSmooth: props.smooth !== false,
1904
+ showLegend: false,
1905
+ showValue: false,
1906
+ });
1907
+ }
1908
+ else {
1909
+ slide.addText('(暂无图表数据)', {
1910
+ x: 0.65, y: 3.0, w: chartW, h: 0.6,
1911
+ fontSize: 16, color: COLORS.secondary, align: 'center', valign: 'middle', fontFace: FONTS.body,
1912
+ });
1913
+ }
1914
+ if (hasConclusion) {
1915
+ addTheme05ConclusionCard(slide, 6.55, 2.05, 2.8, 3.0, props.conclusion);
1916
+ }
1917
+ addTheme05SpectrumBar(slide);
1918
+ }
1919
+ function renderTheme05ChartPeakV1(slide, props) {
1920
+ if (props.kicker) {
1921
+ slide.addText(props.kicker, {
1922
+ x: 0.65, y: 0.78, w: 4.0, h: 0.25,
1923
+ fontSize: 11, color: COLORS.accent, fontFace: FONTS.mono, bold: true,
1924
+ });
1925
+ }
1926
+ slide.addText(props.title ?? '', {
1927
+ x: 0.65, y: 1.15, w: 8.7, h: 0.7,
1928
+ fontSize: 38, color: COLORS.primary, bold: true, valign: 'top', fontFace: FONTS.heading,
1929
+ });
1930
+ if (props.subtitle) {
1931
+ slide.addText(props.subtitle, {
1932
+ x: 0.65, y: 1.85, w: 8.7, h: 0.35,
1933
+ fontSize: 13, color: COLORS.secondary, valign: 'top', fontFace: FONTS.body,
1934
+ });
1935
+ }
1936
+ const labels = (props.labels || []).slice(0, 12);
1937
+ const values = (props.data || []).slice(0, 12).map((d) => Number(d) || 0);
1938
+ const peakIndex = Number(props.peakIndex) || 0;
1939
+ const hasConclusion = props.showConclusion !== false && !!props.conclusion && (!!props.conclusion.value || !!props.conclusion.label || !!props.conclusion.description);
1940
+ const chartW = hasConclusion ? 5.7 : 8.7;
1941
+ if (labels.length > 0 && values.length > 0) {
1942
+ const baseColor = CHART_COLORS[2] || COLORS.secondary;
1943
+ const peakColor = COLORS.accent;
1944
+ const chartColors = values.map((_, idx) => (idx === peakIndex ? peakColor : baseColor));
1945
+ slide.addChart('bar', [{ name: props.title || '', labels, values }], {
1946
+ x: 0.65, y: 2.05, w: chartW, h: 3.0,
1947
+ chartColors,
1948
+ barGrouping: 'clustered',
1949
+ barDir: 'col',
1950
+ showValue: false,
1951
+ });
1952
+ // PEAK 标注
1953
+ const barW = chartW / Math.max(labels.length, 1);
1954
+ const maxVal = Math.max(...values, 1);
1955
+ const peakVal = values[peakIndex] ?? 0;
1956
+ const peakX = 0.65 + (peakIndex + 0.5) * barW;
1957
+ const peakY = 2.05 + 3.0 * (1 - peakVal / maxVal) - 0.35;
1958
+ slide.addText('PEAK', {
1959
+ x: peakX - 0.5, y: Math.max(2.05, peakY), w: 1.0, h: 0.22,
1960
+ fontSize: 10, color: COLORS.accent, bold: true, align: 'center', fontFace: FONTS.mono,
1961
+ });
1962
+ }
1963
+ else {
1964
+ slide.addText('(暂无图表数据)', {
1965
+ x: 0.65, y: 3.0, w: chartW, h: 0.6,
1966
+ fontSize: 16, color: COLORS.secondary, align: 'center', valign: 'middle', fontFace: FONTS.body,
1967
+ });
1968
+ }
1969
+ if (hasConclusion) {
1970
+ addTheme05ConclusionCard(slide, 6.55, 2.05, 2.8, 3.0, props.conclusion);
1971
+ }
1972
+ addTheme05SpectrumBar(slide);
1973
+ }
1974
+ function renderTheme05ChartPeaktroughV1(slide, props) {
1975
+ if (props.kicker) {
1976
+ slide.addText(props.kicker, {
1977
+ x: 0.65, y: 0.78, w: 4.0, h: 0.25,
1978
+ fontSize: 11, color: COLORS.accent, fontFace: FONTS.mono, bold: true,
1979
+ });
1980
+ }
1981
+ slide.addText(props.title ?? '', {
1982
+ x: 0.65, y: 1.15, w: 8.7, h: 0.7,
1983
+ fontSize: 38, color: COLORS.primary, bold: true, valign: 'top', fontFace: FONTS.heading,
1984
+ });
1985
+ if (props.subtitle) {
1986
+ slide.addText(props.subtitle, {
1987
+ x: 0.65, y: 1.85, w: 8.7, h: 0.35,
1988
+ fontSize: 13, color: COLORS.secondary, valign: 'top', fontFace: FONTS.body,
1989
+ });
1990
+ }
1991
+ const labels = (props.labels || []).slice(0, 12);
1992
+ const values = (props.data || []).slice(0, 12).map((d) => Number(d) || 0);
1993
+ const peakIndex = Number(props.peakIndex) || 0;
1994
+ const troughIndex = Number(props.troughIndex) || 0;
1995
+ const hasConclusion = props.showConclusion !== false && !!props.conclusion && (!!props.conclusion.value || !!props.conclusion.label || !!props.conclusion.description);
1996
+ const chartW = hasConclusion ? 5.7 : 8.7;
1997
+ if (labels.length > 0 && values.length > 0) {
1998
+ slide.addChart('line', [{ name: props.title || '', labels, values }], {
1999
+ x: 0.65, y: 2.05, w: chartW, h: 3.0,
2000
+ chartColors: [COLORS.accent],
2001
+ lineDataSymbol: 'circle',
2002
+ lineSmooth: true,
2003
+ showLegend: false,
2004
+ showValue: false,
2005
+ });
2006
+ // 峰值/谷值椭圆标注
2007
+ const maxVal = Math.max(...values, 1);
2008
+ const minVal = Math.min(...values);
2009
+ const plotH = 3.0;
2010
+ const plotY = 2.05;
2011
+ const pointX = (idx) => 0.65 + (chartW / Math.max(labels.length - 1, 1)) * idx;
2012
+ const pointY = (idx) => plotY + plotH * (1 - (values[idx] - minVal) / Math.max(maxVal - minVal, 1));
2013
+ if (values[peakIndex] !== undefined) {
2014
+ const px = pointX(peakIndex);
2015
+ const py = pointY(peakIndex);
2016
+ slide.addShape('ellipse', {
2017
+ x: px - 0.2, y: py - 0.12, w: 0.4, h: 0.24,
2018
+ fill: { color: COLORS.surface },
2019
+ line: { color: COLORS.accent, width: 2 },
2020
+ });
2021
+ slide.addText('PEAK', {
2022
+ x: px - 0.4, y: py - 0.4, w: 0.8, h: 0.2,
2023
+ fontSize: 9, color: COLORS.accent, bold: true, align: 'center', fontFace: FONTS.mono,
2024
+ });
2025
+ }
2026
+ if (values[troughIndex] !== undefined) {
2027
+ const tx = pointX(troughIndex);
2028
+ const ty = pointY(troughIndex);
2029
+ slide.addShape('ellipse', {
2030
+ x: tx - 0.2, y: ty - 0.12, w: 0.4, h: 0.24,
2031
+ fill: { color: COLORS.surface },
2032
+ line: { color: COLORS.secondary, width: 2 },
2033
+ });
2034
+ slide.addText('TROUGH', {
2035
+ x: tx - 0.45, y: ty + 0.2, w: 0.9, h: 0.2,
2036
+ fontSize: 9, color: COLORS.secondary, bold: true, align: 'center', fontFace: FONTS.mono,
2037
+ });
2038
+ }
2039
+ }
2040
+ else {
2041
+ slide.addText('(暂无图表数据)', {
2042
+ x: 0.65, y: 3.0, w: chartW, h: 0.6,
2043
+ fontSize: 16, color: COLORS.secondary, align: 'center', valign: 'middle', fontFace: FONTS.body,
2044
+ });
2045
+ }
2046
+ if (hasConclusion) {
2047
+ addTheme05ConclusionCard(slide, 6.55, 2.05, 2.8, 3.0, props.conclusion);
2048
+ }
2049
+ addTheme05SpectrumBar(slide);
2050
+ }
2051
+ function renderTheme05ChartCumulativeV1(slide, props) {
2052
+ if (props.kicker) {
2053
+ slide.addText(props.kicker, {
2054
+ x: 0.65, y: 0.78, w: 4.0, h: 0.25,
2055
+ fontSize: 11, color: COLORS.accent, fontFace: FONTS.mono, bold: true,
2056
+ });
2057
+ }
2058
+ slide.addText(props.title ?? '', {
2059
+ x: 0.65, y: 1.15, w: 8.7, h: 0.7,
2060
+ fontSize: 38, color: COLORS.primary, bold: true, valign: 'top', fontFace: FONTS.heading,
2061
+ });
2062
+ if (props.subtitle) {
2063
+ slide.addText(props.subtitle, {
2064
+ x: 0.65, y: 1.85, w: 8.7, h: 0.35,
2065
+ fontSize: 13, color: COLORS.secondary, valign: 'top', fontFace: FONTS.body,
2066
+ });
2067
+ }
2068
+ const labels = (props.labels || []).slice(0, 12);
2069
+ const rawValues = (props.data || []).slice(0, 12).map((d) => Number(d) || 0);
2070
+ const cumulative = [];
2071
+ rawValues.reduce((sum, v) => {
2072
+ const next = sum + v;
2073
+ cumulative.push(next);
2074
+ return next;
2075
+ }, 0);
2076
+ const hasConclusion = props.showConclusion !== false && !!props.conclusion && (!!props.conclusion.value || !!props.conclusion.label || !!props.conclusion.description);
2077
+ const chartW = hasConclusion ? 5.7 : 8.7;
2078
+ if (labels.length > 0 && cumulative.length > 0) {
2079
+ slide.addChart('line', [{ name: props.title || '', labels, values: cumulative }], {
2080
+ x: 0.65, y: 2.05, w: chartW, h: 3.0,
2081
+ chartColors: [COLORS.accent],
2082
+ lineDataSymbol: 'circle',
2083
+ lineSmooth: false,
2084
+ showLegend: false,
2085
+ showValue: false,
2086
+ });
2087
+ }
2088
+ else {
2089
+ slide.addText('(暂无图表数据)', {
2090
+ x: 0.65, y: 3.0, w: chartW, h: 0.6,
2091
+ fontSize: 16, color: COLORS.secondary, align: 'center', valign: 'middle', fontFace: FONTS.body,
2092
+ });
2093
+ }
2094
+ if (hasConclusion) {
2095
+ addTheme05ConclusionCard(slide, 6.55, 2.05, 2.8, 3.0, props.conclusion);
2096
+ }
2097
+ addTheme05SpectrumBar(slide);
2098
+ }
2099
+ function renderTheme05TableOfContentsV2(slide, props) {
2100
+ slide.addText(props.title ?? '', {
2101
+ x: 0.65, y: 1.2, w: 4.0, h: 0.9,
2102
+ fontSize: 42, color: COLORS.primary, bold: true, valign: 'top', fontFace: FONTS.heading,
2103
+ });
2104
+ if (props.subtitle) {
2105
+ slide.addText(props.subtitle, {
2106
+ x: 0.65, y: 2.1, w: 4.0, h: 0.4,
2107
+ fontSize: 14, color: COLORS.secondary, valign: 'top', fontFace: FONTS.body,
2108
+ });
2109
+ }
2110
+ slide.addShape('rect', {
2111
+ x: 0.65, y: 2.6, w: 1.2, h: 0.05,
2112
+ fill: { color: COLORS.accent },
2113
+ });
2114
+ const items = (props.items || []).slice(0, 6);
2115
+ const startX = 5.2;
2116
+ const startY = 1.05;
2117
+ const rowH = 0.72;
2118
+ items.forEach((item, idx) => {
2119
+ const y = startY + idx * rowH;
2120
+ const num = String(idx + 1).padStart(2, '0');
2121
+ slide.addText(num, {
2122
+ x: startX, y, w: 0.7, h: 0.5,
2123
+ fontSize: 28, color: COLORS.accent, bold: true, valign: 'top', fontFace: FONTS.heading,
2124
+ });
2125
+ slide.addText(item.title ?? '', {
2126
+ x: startX + 0.9, y, w: 3.2, h: 0.32,
2127
+ fontSize: 16, color: COLORS.primary, bold: true, valign: 'top', fontFace: FONTS.body,
2128
+ });
2129
+ if (item.page) {
2130
+ slide.addText(`P.${item.page}`, {
2131
+ x: startX + 0.9, y: y + 0.28, w: 3.2, h: 0.22,
2132
+ fontSize: 10, color: COLORS.secondary, valign: 'top', fontFace: FONTS.mono,
2133
+ });
2134
+ }
2135
+ });
2136
+ addTheme05SpectrumBar(slide);
2137
+ }
2138
+ function renderTheme05ProcessV2(slide, props) {
2139
+ if (props.kicker) {
2140
+ slide.addText(props.kicker, {
2141
+ x: 0.65, y: 0.78, w: 4.0, h: 0.25,
2142
+ fontSize: 11, color: COLORS.accent, fontFace: FONTS.mono, bold: true,
2143
+ });
2144
+ }
2145
+ slide.addText(props.title ?? '', {
2146
+ x: 0.65, y: 1.15, w: 8.7, h: 0.8,
2147
+ fontSize: 38, color: COLORS.primary, bold: true, valign: 'top', fontFace: FONTS.heading,
2148
+ });
2149
+ if (props.subtitle) {
2150
+ slide.addText(props.subtitle, {
2151
+ x: 0.65, y: 1.95, w: 8.7, h: 0.35,
2152
+ fontSize: 13, color: COLORS.secondary, valign: 'top', fontFace: FONTS.body,
2153
+ });
2154
+ }
2155
+ const steps = (props.steps || []).slice(0, 6);
2156
+ const cardW = 8.7;
2157
+ const cardH = 0.62;
2158
+ const gapY = 0.18;
2159
+ const startX = 0.65;
2160
+ const startY = 2.5;
2161
+ steps.forEach((step, idx) => {
2162
+ const y = startY + idx * (cardH + gapY);
2163
+ const color = theme05SchemeColor(step.scheme);
2164
+ slide.addShape('roundRect', {
2165
+ x: startX, y, w: cardW, h: cardH,
2166
+ fill: { color: COLORS.surfaceElevated },
2167
+ line: { color: COLORS.border, width: 1 },
2168
+ rectRadius: 0.08,
2169
+ });
2170
+ slide.addShape('roundRect', {
2171
+ x: startX + 0.12, y: y + 0.11, w: 0.6, h: 0.4,
2172
+ fill: { color },
2173
+ rectRadius: 0.06,
2174
+ });
2175
+ slide.addText(String(idx + 1).padStart(2, '0'), {
2176
+ x: startX + 0.12, y: y + 0.13, w: 0.6, h: 0.36,
2177
+ fontSize: 14, color: COLORS.white, bold: true, align: 'center', valign: 'middle', fontFace: FONTS.mono,
2178
+ });
2179
+ slide.addText(step.title ?? '', {
2180
+ x: startX + 0.9, y: y + 0.1, w: 3.5, h: 0.25,
2181
+ fontSize: 14, color: COLORS.primary, bold: true, valign: 'middle', fontFace: FONTS.body,
2182
+ });
2183
+ if (step.description) {
2184
+ slide.addText(step.description, {
2185
+ x: startX + 0.9, y: y + 0.32, w: 7.4, h: 0.25,
2186
+ fontSize: 10, color: COLORS.secondary, valign: 'top', fontFace: FONTS.body,
2187
+ });
2188
+ }
2189
+ if (idx < steps.length - 1) {
2190
+ const arrowY = y + cardH + gapY / 2;
2191
+ slide.addShape('triangle', {
2192
+ x: 4.9, y: arrowY - 0.08, w: 0.16, h: 0.16,
2193
+ fill: { color: COLORS.border },
2194
+ });
2195
+ }
2196
+ });
2197
+ addTheme05SpectrumBar(slide);
2198
+ }
2199
+ function renderTheme05ComparisonV1(slide, props) {
2200
+ if (props.kicker) {
2201
+ slide.addText(props.kicker, {
2202
+ x: 0.65, y: 0.78, w: 4.0, h: 0.25,
2203
+ fontSize: 11, color: COLORS.accent, fontFace: FONTS.mono, bold: true,
2204
+ });
2205
+ }
2206
+ slide.addText(props.title ?? '', {
2207
+ x: 0.65, y: 1.15, w: 8.7, h: 0.8,
2208
+ fontSize: 38, color: COLORS.primary, bold: true, valign: 'top', fontFace: FONTS.heading,
2209
+ });
2210
+ if (props.subtitle) {
2211
+ slide.addText(props.subtitle, {
2212
+ x: 0.65, y: 1.95, w: 8.7, h: 0.35,
2213
+ fontSize: 13, color: COLORS.secondary, valign: 'top', fontFace: FONTS.body,
2214
+ });
2215
+ }
2216
+ const leftColor = theme05SchemeColor(props.leftScheme);
2217
+ const rightColor = theme05SchemeColor(props.rightScheme);
2218
+ const cardW = 3.6;
2219
+ const cardH = 3.0;
2220
+ const cardY = 2.25;
2221
+ const gap = 0.9;
2222
+ const startX = (10 - cardW * 2 - gap) / 2;
2223
+ // 左栏
2224
+ slide.addShape('roundRect', {
2225
+ x: startX, y: cardY, w: cardW, h: cardH,
2226
+ fill: { color: COLORS.surfaceElevated },
2227
+ line: { color: COLORS.border, width: 1 },
2228
+ rectRadius: 0.12,
2229
+ });
2230
+ slide.addShape('rect', {
2231
+ x: startX, y: cardY, w: cardW, h: 0.08,
2232
+ fill: { color: leftColor },
2233
+ });
2234
+ slide.addText(props.leftTitle ?? '', {
2235
+ x: startX + 0.2, y: cardY + 0.25, w: cardW - 0.4, h: 0.35,
2236
+ fontSize: 18, color: leftColor, bold: true, valign: 'top', fontFace: FONTS.heading,
2237
+ });
2238
+ const leftItems = (props.leftItems || []).slice(0, 6);
2239
+ leftItems.forEach((item, idx) => {
2240
+ slide.addText(item, {
2241
+ x: startX + 0.3, y: cardY + 0.75 + idx * 0.38, w: cardW - 0.5, h: 0.34,
2242
+ fontSize: 12, color: COLORS.primary, valign: 'top', fontFace: FONTS.body,
2243
+ });
2244
+ });
2245
+ // VS 徽章
2246
+ const vsX = startX + cardW + gap / 2;
2247
+ slide.addShape('ellipse', {
2248
+ x: vsX - 0.35, y: cardY + cardH / 2 - 0.35, w: 0.7, h: 0.7,
2249
+ fill: { color: COLORS.surfaceElevated },
2250
+ line: { color: COLORS.border, width: 1 },
2251
+ });
2252
+ slide.addText(props.vsLabel ?? 'VS', {
2253
+ x: vsX - 0.35, y: cardY + cardH / 2 - 0.15, w: 0.7, h: 0.3,
2254
+ fontSize: 16, color: COLORS.secondary, bold: true, align: 'center', valign: 'middle', fontFace: FONTS.mono,
2255
+ });
2256
+ // 右栏
2257
+ const rightX = startX + cardW + gap;
2258
+ slide.addShape('roundRect', {
2259
+ x: rightX, y: cardY, w: cardW, h: cardH,
2260
+ fill: { color: COLORS.surfaceElevated },
2261
+ line: { color: COLORS.border, width: 1 },
2262
+ rectRadius: 0.12,
2263
+ });
2264
+ slide.addShape('rect', {
2265
+ x: rightX, y: cardY, w: cardW, h: 0.08,
2266
+ fill: { color: rightColor },
2267
+ });
2268
+ slide.addText(props.rightTitle ?? '', {
2269
+ x: rightX + 0.2, y: cardY + 0.25, w: cardW - 0.4, h: 0.35,
2270
+ fontSize: 18, color: rightColor, bold: true, valign: 'top', fontFace: FONTS.heading,
2271
+ });
2272
+ const rightItems = (props.rightItems || []).slice(0, 6);
2273
+ rightItems.forEach((item, idx) => {
2274
+ slide.addText(item, {
2275
+ x: rightX + 0.3, y: cardY + 0.75 + idx * 0.38, w: cardW - 0.5, h: 0.34,
2276
+ fontSize: 12, color: COLORS.primary, valign: 'top', fontFace: FONTS.body,
2277
+ });
2278
+ });
2279
+ addTheme05SpectrumBar(slide);
2280
+ }
2281
+ function renderTheme05ChartFunnelV1(slide, props) {
2282
+ if (props.kicker) {
2283
+ slide.addText(props.kicker, {
2284
+ x: 0.65, y: 0.78, w: 4.0, h: 0.25,
2285
+ fontSize: 11, color: COLORS.accent, fontFace: FONTS.mono, bold: true,
2286
+ });
2287
+ }
2288
+ slide.addText(props.title ?? '', {
2289
+ x: 0.65, y: 1.15, w: 8.7, h: 0.7,
2290
+ fontSize: 38, color: COLORS.primary, bold: true, valign: 'top', fontFace: FONTS.heading,
2291
+ });
2292
+ if (props.subtitle) {
2293
+ slide.addText(props.subtitle, {
2294
+ x: 0.65, y: 1.85, w: 8.7, h: 0.35,
2295
+ fontSize: 13, color: COLORS.secondary, valign: 'top', fontFace: FONTS.body,
2296
+ });
2297
+ }
2298
+ const stages = (props.stages || []).slice(0, 8);
2299
+ const hasConclusion = props.showConclusion !== false && !!props.conclusion && (!!props.conclusion.value || !!props.conclusion.label || !!props.conclusion.description);
2300
+ const funnelW = hasConclusion ? 5.7 : 8.7;
2301
+ const startX = 0.65;
2302
+ const startY = 2.15;
2303
+ const maxH = 3.0;
2304
+ if (stages.length > 0) {
2305
+ const maxValue = Math.max(...stages.map((s) => Number(s.value) || 0), 1);
2306
+ const layerH = maxH / stages.length;
2307
+ const topW = funnelW;
2308
+ stages.forEach((stage, idx) => {
2309
+ const value = Number(stage.value) || 0;
2310
+ const ratio = value / maxValue;
2311
+ const upperRatio = idx === 0 ? 1 : (Number(stages[idx - 1].value) || 0) / maxValue;
2312
+ const y = startY + idx * layerH;
2313
+ const topLayerW = topW * upperRatio;
2314
+ const bottomLayerW = topW * ratio;
2315
+ const xTop = startX + (funnelW - topLayerW) / 2;
2316
+ const xBottom = startX + (funnelW - bottomLayerW) / 2;
2317
+ const color = theme05SchemeColor(stage.scheme);
2318
+ // 用梯形路径模拟漏斗层
2319
+ const path = [
2320
+ `M ${xTop} ${y}`,
2321
+ `L ${xTop + topLayerW} ${y}`,
2322
+ `L ${xBottom + bottomLayerW} ${y + layerH - 0.04}`,
2323
+ `L ${xBottom} ${y + layerH - 0.04}`,
2324
+ 'Z',
2325
+ ].join(' ');
2326
+ slide.addShape('custGeom', {
2327
+ x: startX, y, w: funnelW, h: layerH,
2328
+ path,
2329
+ fill: { color },
2330
+ });
2331
+ slide.addText(stage.name ?? '', {
2332
+ x: startX, y: y + 0.05, w: funnelW, h: 0.22,
2333
+ fontSize: 10, color: COLORS.white, bold: true, align: 'center', valign: 'top', fontFace: FONTS.body,
2334
+ });
2335
+ slide.addText(String(value), {
2336
+ x: startX, y: y + layerH - 0.28, w: funnelW, h: 0.2,
2337
+ fontSize: 10, color: COLORS.white, align: 'center', valign: 'bottom', fontFace: FONTS.heading,
2338
+ });
2339
+ });
2340
+ }
2341
+ else {
2342
+ slide.addText('(暂无漏斗数据)', {
2343
+ x: startX, y: 3.0, w: funnelW, h: 0.6,
2344
+ fontSize: 16, color: COLORS.secondary, align: 'center', valign: 'middle', fontFace: FONTS.body,
2345
+ });
2346
+ }
2347
+ if (hasConclusion) {
2348
+ addTheme05ConclusionCard(slide, 6.55, 2.05, 2.8, 3.0, props.conclusion);
2349
+ }
2350
+ addTheme05SpectrumBar(slide);
2351
+ }
2352
+ function renderTheme05QuoteV2(slide, props) {
2353
+ const color = theme05SchemeColor(props.accentScheme);
2354
+ slide.addShape('roundRect', {
2355
+ x: 0.65, y: 1.8, w: 0.1, h: 2.0,
2356
+ fill: { color },
2357
+ rectRadius: 0.05,
2358
+ });
2359
+ slide.addText('“', {
2360
+ x: 1.0, y: 1.2, w: 1.0, h: 0.6,
2361
+ fontSize: 80, color: COLORS.border, bold: true, valign: 'top', fontFace: FONTS.heading,
2362
+ });
2363
+ slide.addText(props.quote ?? '', {
2364
+ x: 1.0, y: 2.0, w: 8.35, h: 1.6,
2365
+ fontSize: 32, color: COLORS.primary, bold: true, valign: 'top', fontFace: FONTS.heading,
2366
+ });
2367
+ const source = [props.author, props.source].filter(Boolean).join(' · ');
2368
+ if (source) {
2369
+ slide.addText(source, {
2370
+ x: 1.0, y: 3.75, w: 8.35, h: 0.25,
2371
+ fontSize: 12, color: COLORS.secondary, valign: 'top', fontFace: FONTS.body,
2372
+ });
2373
+ }
2374
+ addTheme05SpectrumBar(slide);
2375
+ }
2376
+ function renderTheme05ChartGaugeV1(slide, props) {
2377
+ if (props.kicker) {
2378
+ slide.addText(props.kicker, {
2379
+ x: 0.65, y: 0.78, w: 4.0, h: 0.25,
2380
+ fontSize: 11, color: COLORS.accent, fontFace: FONTS.mono, bold: true,
2381
+ });
2382
+ }
2383
+ slide.addText(props.title ?? '', {
2384
+ x: 0.65, y: 1.15, w: 8.7, h: 0.7,
2385
+ fontSize: 38, color: COLORS.primary, bold: true, align: 'center', valign: 'top', fontFace: FONTS.heading,
2386
+ });
2387
+ if (props.subtitle) {
2388
+ slide.addText(props.subtitle, {
2389
+ x: 0.65, y: 1.85, w: 8.7, h: 0.35,
2390
+ fontSize: 13, color: COLORS.secondary, align: 'center', valign: 'top', fontFace: FONTS.body,
2391
+ });
2392
+ }
2393
+ const value = Number(props.value) || 0;
2394
+ const min = Number(props.min) || 0;
2395
+ const max = Math.max(Number(props.max) || 100, value, min + 1);
2396
+ const chartX = 2.5;
2397
+ const chartY = 2.25;
2398
+ const chartW = 5.0;
2399
+ const chartH = 2.8;
2400
+ const color = theme05SchemeColor(props.scheme);
2401
+ slide.addChart('doughnut', [
2402
+ { name: props.label || '已完成', labels: ['已完成'], values: [value - min] },
2403
+ { name: '剩余', labels: ['剩余'], values: [max - value] },
2404
+ ], {
2405
+ x: chartX, y: chartY, w: chartW, h: chartH,
2406
+ chartColors: [color, COLORS.border],
2407
+ holeSize: 65,
2408
+ showValue: false,
2409
+ showLegend: false,
2410
+ });
2411
+ slide.addText(`${value}${props.unit ?? ''}`, {
2412
+ x: chartX, y: chartY + 1.05, w: chartW, h: 0.55,
2413
+ fontSize: 36, color: COLORS.primary, bold: true, align: 'center', valign: 'middle', fontFace: FONTS.heading,
2414
+ });
2415
+ slide.addText(props.label ?? '完成度', {
2416
+ x: chartX, y: chartY + 1.55, w: chartW, h: 0.25,
2417
+ fontSize: 11, color: COLORS.secondary, align: 'center', valign: 'top', fontFace: FONTS.body,
2418
+ });
2419
+ addTheme05SpectrumBar(slide);
2420
+ }
2421
+ function renderTheme05BentoV1(slide, props) {
2422
+ if (props.kicker) {
2423
+ slide.addText(props.kicker, {
2424
+ x: 0.65, y: 0.78, w: 4.0, h: 0.25,
2425
+ fontSize: 11, color: COLORS.accent, fontFace: FONTS.mono, bold: true,
2426
+ });
2427
+ }
2428
+ slide.addText(props.title ?? '', {
2429
+ x: 0.65, y: 1.15, w: 8.7, h: 0.7,
2430
+ fontSize: 38, color: COLORS.primary, bold: true, valign: 'top', fontFace: FONTS.heading,
2431
+ });
2432
+ if (props.subtitle) {
2433
+ slide.addText(props.subtitle, {
2434
+ x: 0.65, y: 1.85, w: 8.7, h: 0.35,
2435
+ fontSize: 13, color: COLORS.secondary, valign: 'top', fontFace: FONTS.body,
2436
+ });
2437
+ }
2438
+ const items = (props.items || []).slice(0, 6);
2439
+ if (items.length > 0) {
2440
+ const startY = 2.35;
2441
+ const areaH = 2.85;
2442
+ const gap = 0.16;
2443
+ if (items.length === 1) {
2444
+ renderTheme05BentoCard(slide, items[0], 0.65, startY, 8.7, areaH);
2445
+ }
2446
+ else if (items.length === 2) {
2447
+ const cardW = (8.7 - gap) / 2;
2448
+ items.forEach((item, idx) => {
2449
+ renderTheme05BentoCard(slide, item, 0.65 + idx * (cardW + gap), startY, cardW, areaH);
2450
+ });
2451
+ }
2452
+ else if (items.length === 3) {
2453
+ renderTheme05BentoCard(slide, items[0], 0.65, startY, 4.25, areaH);
2454
+ const h2 = (areaH - gap) / 2;
2455
+ renderTheme05BentoCard(slide, items[1], 5.08, startY, 4.25, h2);
2456
+ renderTheme05BentoCard(slide, items[2], 5.08, startY + h2 + gap, 4.25, h2);
2457
+ }
2458
+ else if (items.length === 4) {
2459
+ const cardW = (8.7 - gap) / 2;
2460
+ const cardH = (areaH - gap) / 2;
2461
+ items.forEach((item, idx) => {
2462
+ const col = idx % 2;
2463
+ const row = Math.floor(idx / 2);
2464
+ renderTheme05BentoCard(slide, item, 0.65 + col * (cardW + gap), startY + row * (cardH + gap), cardW, cardH);
2465
+ });
2466
+ }
2467
+ else {
2468
+ const cols = 3;
2469
+ const cardW = (8.7 - gap * (cols - 1)) / cols;
2470
+ const cardH = (areaH - gap) / 2;
2471
+ items.forEach((item, idx) => {
2472
+ const col = idx % cols;
2473
+ const row = Math.floor(idx / cols);
2474
+ renderTheme05BentoCard(slide, item, 0.65 + col * (cardW + gap), startY + row * (cardH + gap), cardW, cardH);
2475
+ });
2476
+ }
2477
+ }
2478
+ addTheme05SpectrumBar(slide);
2479
+ }
2480
+ function renderTheme05BentoCard(slide, item, x, y, w, h) {
2481
+ const color = theme05SchemeColor(item.scheme);
2482
+ addTheme05Card(slide, x, y, w, h);
2483
+ slide.addShape('rect', {
2484
+ x, y, w, h: 0.06,
2485
+ fill: { color },
2486
+ });
2487
+ slide.addText(item.value ?? '', {
2488
+ x: x + 0.14, y: y + 0.18, w: w - 0.28, h: 0.55,
2489
+ fontSize: 30, color: COLORS.primary, bold: true, valign: 'top', fontFace: FONTS.heading,
2490
+ });
2491
+ slide.addText(item.title ?? '', {
2492
+ x: x + 0.14, y: y + h - 0.55, w: w - 0.28, h: 0.25,
2493
+ fontSize: 11, color: COLORS.secondary, valign: 'bottom', fontFace: FONTS.body,
2494
+ });
2495
+ if (item.description) {
2496
+ slide.addText(item.description, {
2497
+ x: x + 0.14, y: y + 0.78, w: w - 0.28, h: Math.max(0.2, h - 1.05),
2498
+ fontSize: 10, color: COLORS.secondary, valign: 'top', fontFace: FONTS.body,
2499
+ });
2500
+ }
2501
+ }
2502
+ function renderTheme05CaseV1(slide, props) {
2503
+ if (props.kicker) {
2504
+ slide.addText(props.kicker, {
2505
+ x: 0.65, y: 0.78, w: 4.0, h: 0.25,
2506
+ fontSize: 11, color: COLORS.accent, fontFace: FONTS.mono, bold: true,
2507
+ });
2508
+ }
2509
+ slide.addText(props.title ?? '', {
2510
+ x: 0.65, y: 1.15, w: 8.7, h: 0.7,
2511
+ fontSize: 38, color: COLORS.primary, bold: true, valign: 'top', fontFace: FONTS.heading,
2512
+ });
2513
+ if (props.subtitle) {
2514
+ slide.addText(props.subtitle, {
2515
+ x: 0.65, y: 1.85, w: 8.7, h: 0.35,
2516
+ fontSize: 13, color: COLORS.secondary, valign: 'top', fontFace: FONTS.body,
2517
+ });
2518
+ }
2519
+ const metric = props.metric || {};
2520
+ const leftX = 0.65;
2521
+ const leftW = 3.6;
2522
+ const rightX = 4.55;
2523
+ const rightW = 4.8;
2524
+ const startY = 2.35;
2525
+ slide.addText(props.company ?? '', {
2526
+ x: leftX, y: startY, w: leftW, h: 0.55,
2527
+ fontSize: 26, color: COLORS.primary, bold: true, valign: 'top', fontFace: FONTS.heading,
2528
+ });
2529
+ slide.addText(`${metric.value ?? ''}`, {
2530
+ x: leftX, y: startY + 0.7, w: leftW, h: 0.8,
2531
+ fontSize: 56, color: COLORS.accent, bold: true, valign: 'top', fontFace: FONTS.heading,
2532
+ });
2533
+ if (metric.label) {
2534
+ slide.addText(metric.label, {
2535
+ x: leftX, y: startY + 1.45, w: leftW, h: 0.3,
2536
+ fontSize: 13, color: COLORS.secondary, valign: 'top', fontFace: FONTS.body,
2537
+ });
2538
+ }
2539
+ const sections = [
2540
+ { key: 'challenge', label: '挑战' },
2541
+ { key: 'solution', label: '方案' },
2542
+ { key: 'result', label: '成果' },
2543
+ ].filter((s) => props[s.key]);
2544
+ const cardH = sections.length > 0 ? (2.8 - 0.16 * (sections.length - 1)) / sections.length : 0;
2545
+ sections.forEach((s, idx) => {
2546
+ const y = startY + idx * (cardH + 0.16);
2547
+ addTheme05Card(slide, rightX, y, rightW, cardH, true);
2548
+ slide.addText(s.label, {
2549
+ x: rightX + 0.14, y: y + 0.1, w: 0.8, h: 0.22,
2550
+ fontSize: 10, color: COLORS.white, bold: true, fontFace: FONTS.mono,
2551
+ });
2552
+ slide.addText(props[s.key], {
2553
+ x: rightX + 0.14, y: y + 0.34, w: rightW - 0.28, h: cardH - 0.48,
2554
+ fontSize: 12, color: COLORS.white, valign: 'top', fontFace: FONTS.body,
2555
+ });
2556
+ });
2557
+ addTheme05SpectrumBar(slide);
2558
+ }
2559
+ function renderTheme05EditorialV1(slide, props) {
2560
+ if (props.kicker) {
2561
+ slide.addText(props.kicker, {
2562
+ x: 0.65, y: 0.78, w: 4.0, h: 0.25,
2563
+ fontSize: 11, color: COLORS.accent, fontFace: FONTS.mono, bold: true,
2564
+ });
2565
+ }
2566
+ slide.addText(props.title ?? '', {
2567
+ x: 0.65, y: 1.15, w: 8.7, h: 0.7,
2568
+ fontSize: 38, color: COLORS.primary, bold: true, valign: 'top', fontFace: FONTS.heading,
2569
+ });
2570
+ if (props.subtitle) {
2571
+ slide.addText(props.subtitle, {
2572
+ x: 0.65, y: 1.85, w: 8.7, h: 0.35,
2573
+ fontSize: 13, color: COLORS.secondary, valign: 'top', fontFace: FONTS.body,
2574
+ });
2575
+ }
2576
+ const isRight = props.imageSide === 'right';
2577
+ const imgX = isRight ? 5.05 : 0.65;
2578
+ const textX = isRight ? 0.65 : 5.05;
2579
+ const imgW = 4.3;
2580
+ const textW = 4.3;
2581
+ const bodyY = 2.35;
2582
+ const imgH = 2.85;
2583
+ if (props.imageUrl) {
2584
+ addImageMaybe(slide, props.imageUrl, imgX, bodyY, imgW, imgH);
2585
+ }
2586
+ else {
2587
+ slide.addShape('roundRect', {
2588
+ x: imgX, y: bodyY, w: imgW, h: imgH,
2589
+ fill: { color: COLORS.surfaceElevated },
2590
+ line: { color: COLORS.border, width: 1 },
2591
+ rectRadius: 0.12,
2592
+ });
2593
+ slide.addText('点击上传大图', {
2594
+ x: imgX, y: bodyY + 1.2, w: imgW, h: 0.4,
2595
+ fontSize: 14, color: COLORS.secondary, align: 'center', valign: 'middle', fontFace: FONTS.body,
2596
+ });
2597
+ }
2598
+ if (props.body) {
2599
+ slide.addText(props.body, {
2600
+ x: textX, y: bodyY, w: textW, h: props.pullQuote ? 1.75 : 2.75,
2601
+ fontSize: 13, color: COLORS.primary, valign: 'top', fontFace: FONTS.body,
2602
+ });
2603
+ }
2604
+ if (props.pullQuote) {
2605
+ const quoteY = props.body ? bodyY + 1.9 : bodyY;
2606
+ slide.addShape('roundRect', {
2607
+ x: textX, y: quoteY, w: textW, h: 1.3,
2608
+ fill: { color: COLORS.surfaceElevated },
2609
+ line: { color: COLORS.border, width: 1 },
2610
+ rectRadius: 0.12,
2611
+ });
2612
+ slide.addShape('rect', {
2613
+ x: textX, y: quoteY, w: 0.08, h: 1.3,
2614
+ fill: { color: COLORS.accent },
2615
+ });
2616
+ slide.addText(props.pullQuote, {
2617
+ x: textX + 0.18, y: quoteY + 0.16, w: textW - 0.3, h: 0.95,
2618
+ fontSize: 13, color: COLORS.primary, bold: true, valign: 'top', fontFace: FONTS.body,
2619
+ });
2620
+ }
2621
+ addTheme05SpectrumBar(slide);
2622
+ }
2623
+ function renderTheme05GalleryV1(slide, props) {
2624
+ if (props.kicker) {
2625
+ slide.addText(props.kicker, {
2626
+ x: 0.65, y: 0.78, w: 4.0, h: 0.25,
2627
+ fontSize: 11, color: COLORS.accent, fontFace: FONTS.mono, bold: true,
2628
+ });
2629
+ }
2630
+ slide.addText(props.title ?? '', {
2631
+ x: 0.65, y: 1.15, w: 8.7, h: 0.7,
2632
+ fontSize: 38, color: COLORS.primary, bold: true, valign: 'top', fontFace: FONTS.heading,
2633
+ });
2634
+ if (props.subtitle) {
2635
+ slide.addText(props.subtitle, {
2636
+ x: 0.65, y: 1.85, w: 8.7, h: 0.35,
2637
+ fontSize: 13, color: COLORS.secondary, valign: 'top', fontFace: FONTS.body,
2638
+ });
2639
+ }
2640
+ const items = (props.items || []).slice(0, 4);
2641
+ if (items.length > 0) {
2642
+ const startY = 2.35;
2643
+ const areaH = 2.6;
2644
+ const gap = 0.16;
2645
+ if (items.length === 1) {
2646
+ addImageMaybe(slide, items[0].imageUrl, 0.65, startY, 8.7, areaH);
2647
+ if (items[0].caption) {
2648
+ slide.addText(items[0].caption, {
2649
+ x: 0.65, y: startY + areaH + 0.08, w: 8.7, h: 0.25,
2650
+ fontSize: 12, color: COLORS.secondary, align: 'center', fontFace: FONTS.body,
2651
+ });
2652
+ }
2653
+ }
2654
+ else if (items.length === 2) {
2655
+ const w = (8.7 - gap) / 2;
2656
+ items.forEach((item, idx) => {
2657
+ addImageMaybe(slide, item.imageUrl, 0.65 + idx * (w + gap), startY, w, areaH);
2658
+ if (item.caption) {
2659
+ slide.addText(item.caption, {
2660
+ x: 0.65 + idx * (w + gap), y: startY + areaH + 0.08, w, h: 0.25,
2661
+ fontSize: 12, color: COLORS.secondary, align: 'center', fontFace: FONTS.body,
2662
+ });
2663
+ }
2664
+ });
2665
+ }
2666
+ else if (items.length === 3) {
2667
+ const w = (8.7 - gap) / 2;
2668
+ const h = (areaH - gap) / 2;
2669
+ addImageMaybe(slide, items[0].imageUrl, 0.65, startY, w, areaH);
2670
+ addImageMaybe(slide, items[1].imageUrl, 0.65 + w + gap, startY, w, h);
2671
+ addImageMaybe(slide, items[2].imageUrl, 0.65 + w + gap, startY + h + gap, w, h);
2672
+ [0, 1, 2].forEach((idx) => {
2673
+ if (items[idx].caption) {
2674
+ const cx = idx === 0 ? 0.65 : 0.65 + w + gap;
2675
+ const cy = idx === 0 ? startY + areaH + 0.08 : startY + h + 0.08;
2676
+ slide.addText(items[idx].caption, {
2677
+ x: cx, y: cy, w, h: 0.25,
2678
+ fontSize: 12, color: COLORS.secondary, align: 'center', fontFace: FONTS.body,
2679
+ });
2680
+ }
2681
+ });
2682
+ }
2683
+ else {
2684
+ const w = (8.7 - gap) / 2;
2685
+ const h = (areaH - gap) / 2;
2686
+ items.forEach((item, idx) => {
2687
+ const col = idx % 2;
2688
+ const row = Math.floor(idx / 2);
2689
+ addImageMaybe(slide, item.imageUrl, 0.65 + col * (w + gap), startY + row * (h + gap), w, h);
2690
+ if (item.caption) {
2691
+ slide.addText(item.caption, {
2692
+ x: 0.65 + col * (w + gap), y: startY + row * (h + gap) + h + 0.08, w, h: 0.25,
2693
+ fontSize: 12, color: COLORS.secondary, align: 'center', fontFace: FONTS.body,
2694
+ });
2695
+ }
2696
+ });
2697
+ }
2698
+ }
2699
+ addTheme05SpectrumBar(slide);
2700
+ }
2701
+ function renderTheme05ProfileV1(slide, props) {
2702
+ if (props.kicker) {
2703
+ slide.addText(props.kicker, {
2704
+ x: 0.65, y: 0.78, w: 4.0, h: 0.25,
2705
+ fontSize: 11, color: COLORS.accent, fontFace: FONTS.mono, bold: true,
2706
+ });
2707
+ }
2708
+ slide.addText(props.title ?? '', {
2709
+ x: 0.65, y: 1.15, w: 8.7, h: 0.7,
2710
+ fontSize: 38, color: COLORS.primary, bold: true, valign: 'top', fontFace: FONTS.heading,
2711
+ });
2712
+ if (props.subtitle) {
2713
+ slide.addText(props.subtitle, {
2714
+ x: 0.65, y: 1.85, w: 8.7, h: 0.35,
2715
+ fontSize: 13, color: COLORS.secondary, valign: 'top', fontFace: FONTS.body,
2716
+ });
2717
+ }
2718
+ const avatarX = 0.85;
2719
+ const avatarY = 2.45;
2720
+ const avatarSize = 2.2;
2721
+ addTheme05Card(slide, avatarX - 0.2, avatarY - 0.2, avatarSize + 0.4, 2.9);
2722
+ if (props.avatarUrl) {
2723
+ addImageMaybe(slide, props.avatarUrl, avatarX, avatarY, avatarSize, avatarSize);
2724
+ }
2725
+ else {
2726
+ slide.addShape('ellipse', {
2727
+ x: avatarX, y: avatarY, w: avatarSize, h: avatarSize,
2728
+ fill: { color: COLORS.border },
2729
+ });
2730
+ slide.addText('+ 头像', {
2731
+ x: avatarX, y: avatarY + 0.85, w: avatarSize, h: 0.4,
2732
+ fontSize: 14, color: COLORS.secondary, align: 'center', valign: 'middle', fontFace: FONTS.body,
2733
+ });
2734
+ }
2735
+ const mainX = 3.55;
2736
+ const mainW = 5.8;
2737
+ let cursorY = 2.45;
2738
+ slide.addText(props.name ?? '', {
2739
+ x: mainX, y: cursorY, w: mainW, h: 0.5,
2740
+ fontSize: 28, color: COLORS.primary, bold: true, fontFace: FONTS.heading,
2741
+ });
2742
+ cursorY += 0.55;
2743
+ slide.addText(`${props.role ?? ''} · ${props.company ?? ''}`, {
2744
+ x: mainX, y: cursorY, w: mainW, h: 0.28,
2745
+ fontSize: 12, color: COLORS.secondary, fontFace: FONTS.body,
2746
+ });
2747
+ cursorY += 0.45;
2748
+ if (props.quote) {
2749
+ slide.addShape('roundRect', {
2750
+ x: mainX, y: cursorY, w: mainW, h: 0.9,
2751
+ fill: { color: COLORS.surfaceElevated },
2752
+ line: { color: COLORS.border, width: 1 },
2753
+ rectRadius: 0.12,
2754
+ });
2755
+ slide.addText(`"${props.quote}"`, {
2756
+ x: mainX + 0.14, y: cursorY + 0.12, w: mainW - 0.28, h: 0.65,
2757
+ fontSize: 13, color: COLORS.primary, bold: true, valign: 'top', fontFace: FONTS.body,
2758
+ });
2759
+ cursorY += 1.05;
2760
+ }
2761
+ const facts = (props.facts || []).slice(0, 4);
2762
+ if (facts.length > 0) {
2763
+ const cols = facts.length <= 2 ? 2 : 2;
2764
+ const rows = Math.ceil(facts.length / cols);
2765
+ const gap = 0.12;
2766
+ const cardW = (mainW - gap * (cols - 1)) / cols;
2767
+ const cardH = rows === 1 ? 0.7 : 0.55;
2768
+ facts.forEach((fact, idx) => {
2769
+ const col = idx % cols;
2770
+ const row = Math.floor(idx / cols);
2771
+ const x = mainX + col * (cardW + gap);
2772
+ const y = cursorY + row * (cardH + gap);
2773
+ slide.addText(fact.label ?? '', {
2774
+ x, y, w: cardW, h: 0.18,
2775
+ fontSize: 9, color: COLORS.secondary, fontFace: FONTS.body,
2776
+ });
2777
+ slide.addText(fact.value ?? '', {
2778
+ x, y: y + 0.2, w: cardW, h: 0.28,
2779
+ fontSize: 13, color: COLORS.primary, bold: true, fontFace: FONTS.heading,
2780
+ });
2781
+ });
2782
+ }
2783
+ addTheme05SpectrumBar(slide);
2784
+ }
2785
+ function renderTheme05RoadmapV1(slide, props) {
2786
+ if (props.kicker) {
2787
+ slide.addText(props.kicker, {
2788
+ x: 0.65, y: 0.78, w: 4.0, h: 0.25,
2789
+ fontSize: 11, color: COLORS.accent, fontFace: FONTS.mono, bold: true,
2790
+ });
2791
+ }
2792
+ slide.addText(props.title ?? '', {
2793
+ x: 0.65, y: 1.15, w: 8.7, h: 0.7,
2794
+ fontSize: 38, color: COLORS.primary, bold: true, valign: 'top', fontFace: FONTS.heading,
2795
+ });
2796
+ if (props.subtitle) {
2797
+ slide.addText(props.subtitle, {
2798
+ x: 0.65, y: 1.85, w: 8.7, h: 0.35,
2799
+ fontSize: 13, color: COLORS.secondary, valign: 'top', fontFace: FONTS.body,
2800
+ });
2801
+ }
2802
+ const phases = (props.phases || []).slice(0, 4);
2803
+ if (phases.length > 0) {
2804
+ const startY = 2.45;
2805
+ const cardH = 2.5;
2806
+ const gap = 0.2;
2807
+ const cardW = (8.7 - gap * (phases.length - 1)) / phases.length;
2808
+ const lineY = startY + 0.35;
2809
+ slide.addShape('rect', {
2810
+ x: 0.65, y: lineY, w: 8.7, h: 0.04,
2811
+ fill: { color: COLORS.border },
2812
+ });
2813
+ phases.forEach((phase, idx) => {
2814
+ const x = 0.65 + idx * (cardW + gap);
2815
+ const color = theme05SchemeColor(phase.scheme);
2816
+ const nodeSize = 0.36;
2817
+ const nodeX = x + (cardW - nodeSize) / 2;
2818
+ slide.addShape('ellipse', {
2819
+ x: nodeX, y: lineY - nodeSize / 2 + 0.02, w: nodeSize, h: nodeSize,
2820
+ fill: { color },
2821
+ });
2822
+ slide.addText(String(idx + 1).padStart(2, '0'), {
2823
+ x: nodeX, y: lineY - nodeSize / 2 + 0.02, w: nodeSize, h: nodeSize,
2824
+ fontSize: 11, color: 'FFFFFF', bold: true, align: 'center', valign: 'middle', fontFace: FONTS.heading,
2825
+ });
2826
+ addTheme05Card(slide, x, startY + 0.7, cardW, cardH - 0.7);
2827
+ slide.addShape('rect', {
2828
+ x, y: startY + 0.7, w: cardW, h: 0.06,
2829
+ fill: { color },
2830
+ });
2831
+ slide.addText(phase.period ?? '', {
2832
+ x: x + 0.12, y: startY + 0.88, w: cardW - 0.24, h: 0.2,
2833
+ fontSize: 10, color: COLORS.secondary, fontFace: FONTS.mono,
2834
+ });
2835
+ slide.addText(phase.title ?? '', {
2836
+ x: x + 0.12, y: startY + 1.15, w: cardW - 0.24, h: 0.35,
2837
+ fontSize: 15, color: COLORS.primary, bold: true, valign: 'top', fontFace: FONTS.heading,
2838
+ });
2839
+ if (phase.description) {
2840
+ slide.addText(phase.description, {
2841
+ x: x + 0.12, y: startY + 1.55, w: cardW - 0.24, h: 1.1,
2842
+ fontSize: 11, color: COLORS.secondary, valign: 'top', fontFace: FONTS.body,
2843
+ });
2844
+ }
2845
+ });
2846
+ }
2847
+ addTheme05SpectrumBar(slide);
2848
+ }
2849
+ function renderTheme05ScorecardsV1(slide, props) {
2850
+ if (props.kicker) {
2851
+ slide.addText(props.kicker, {
2852
+ x: 0.65, y: 0.78, w: 4.0, h: 0.25,
2853
+ fontSize: 11, color: COLORS.accent, fontFace: FONTS.mono, bold: true,
2854
+ });
2855
+ }
2856
+ slide.addText(props.title ?? '', {
2857
+ x: 0.65, y: 1.15, w: 8.7, h: 0.7,
2858
+ fontSize: 38, color: COLORS.primary, bold: true, valign: 'top', fontFace: FONTS.heading,
2859
+ });
2860
+ if (props.subtitle) {
2861
+ slide.addText(props.subtitle, {
2862
+ x: 0.65, y: 1.85, w: 8.7, h: 0.35,
2863
+ fontSize: 13, color: COLORS.secondary, valign: 'top', fontFace: FONTS.body,
2864
+ });
2865
+ }
2866
+ const cards = (props.cards || []).slice(0, 4);
2867
+ if (cards.length > 0) {
2868
+ const startY = 2.45;
2869
+ const gap = 0.14;
2870
+ const cardH = (2.85 - gap * (cards.length - 1)) / cards.length;
2871
+ cards.forEach((card, idx) => {
2872
+ const y = startY + idx * (cardH + gap);
2873
+ const color = theme05SchemeColor(card.scheme);
2874
+ addTheme05Card(slide, 0.65, y, 8.7, cardH);
2875
+ slide.addShape('rect', {
2876
+ x: 0.65, y, w: 0.1, h: cardH,
2877
+ fill: { color },
2878
+ });
2879
+ slide.addText(card.label ?? '', {
2880
+ x: 0.9, y: y + 0.12, w: 3.8, h: cardH - 0.24,
2881
+ fontSize: 15, color: COLORS.primary, bold: true, valign: 'middle', fontFace: FONTS.heading,
2882
+ });
2883
+ const valueText = `${card.value ?? ''}${card.unit ?? ''}`;
2884
+ slide.addText(valueText, {
2885
+ x: 4.9, y: y + 0.1, w: 2.2, h: cardH - 0.2,
2886
+ fontSize: 28, color: COLORS.primary, bold: true, align: 'right', valign: 'middle', fontFace: FONTS.heading,
2887
+ });
2888
+ if (card.change) {
2889
+ const changeColor = card.positive === false ? 'E85D4E' : '22C55E';
2890
+ slide.addShape('roundRect', {
2891
+ x: 7.25, y: y + (cardH - 0.42) / 2, w: 1.0, h: 0.42,
2892
+ fill: { color: changeColor }, rectRadius: 0.21,
2893
+ });
2894
+ slide.addText(card.change, {
2895
+ x: 7.25, y: y + (cardH - 0.42) / 2 + 0.02, w: 1.0, h: 0.38,
2896
+ fontSize: 12, color: 'FFFFFF', bold: true, align: 'center', valign: 'middle', fontFace: FONTS.body,
2897
+ });
2898
+ }
2899
+ });
2900
+ }
2901
+ addTheme05SpectrumBar(slide);
2902
+ }
2903
+ function renderTheme05DonutV1(slide, props) {
2904
+ if (props.kicker) {
2905
+ slide.addText(props.kicker, {
2906
+ x: 0.65, y: 0.78, w: 4.0, h: 0.25,
2907
+ fontSize: 11, color: COLORS.accent, fontFace: FONTS.mono, bold: true,
2908
+ });
2909
+ }
2910
+ slide.addText(props.title ?? '', {
2911
+ x: 0.65, y: 1.15, w: 8.7, h: 0.7,
2912
+ fontSize: 38, color: COLORS.primary, bold: true, valign: 'top', fontFace: FONTS.heading,
2913
+ });
2914
+ if (props.subtitle) {
2915
+ slide.addText(props.subtitle, {
2916
+ x: 0.65, y: 1.85, w: 8.7, h: 0.35,
2917
+ fontSize: 13, color: COLORS.secondary, valign: 'top', fontFace: FONTS.body,
2918
+ });
2919
+ }
2920
+ const items = (props.items || []).slice(0, 8).filter((it) => it && it.name);
2921
+ const hasConclusion = props.showConclusion !== false && !!props.conclusion && (!!props.conclusion.value || !!props.conclusion.label || !!props.conclusion.description);
2922
+ const chartW = hasConclusion ? 5.4 : 8.0;
2923
+ const chartX = 0.65;
2924
+ const chartY = 2.15;
2925
+ if (items.length > 0) {
2926
+ const total = items.reduce((sum, it) => sum + (Number(it.value) || 0), 0);
2927
+ const data = items.map((it) => ({
2928
+ name: it.name,
2929
+ labels: [it.name],
2930
+ values: [Number(it.value) || 0],
2931
+ }));
2932
+ slide.addChart('doughnut', data, {
2933
+ x: chartX, y: chartY, w: chartW, h: 3.1,
2934
+ chartColors: items.map((it) => theme05SchemeColor(it.scheme)),
2935
+ holeSize: 55,
2936
+ showValue: false,
2937
+ showLegend: false,
2938
+ });
2939
+ if (total > 0) {
2940
+ slide.addText(String(total), {
2941
+ x: chartX, y: chartY + 1.2, w: chartW, h: 0.55,
2942
+ fontSize: 34, color: COLORS.primary, bold: true, align: 'center', valign: 'middle', fontFace: FONTS.heading,
2943
+ });
2944
+ if (props.unit) {
2945
+ slide.addText(props.unit, {
2946
+ x: chartX, y: chartY + 1.7, w: chartW, h: 0.25,
2947
+ fontSize: 11, color: COLORS.secondary, align: 'center', valign: 'top', fontFace: FONTS.body,
2948
+ });
2949
+ }
2950
+ }
2951
+ const legendX = chartX + chartW + 0.25;
2952
+ const legendW = hasConclusion ? 2.7 : 3.0;
2953
+ items.slice(0, 6).forEach((it, idx) => {
2954
+ const y = chartY + idx * 0.48;
2955
+ const color = theme05SchemeColor(it.scheme);
2956
+ slide.addShape('ellipse', {
2957
+ x: legendX, y: y + 0.1, w: 0.16, h: 0.16,
2958
+ fill: { color },
2959
+ });
2960
+ slide.addText(it.name, {
2961
+ x: legendX + 0.24, y, w: legendW - 0.24, h: 0.22,
2962
+ fontSize: 11, color: COLORS.primary, bold: true, fontFace: FONTS.body,
2963
+ });
2964
+ const pct = total > 0 ? Math.round(((Number(it.value) || 0) / total) * 100) : 0;
2965
+ slide.addText(`${pct}%`, {
2966
+ x: legendX + 0.24, y: y + 0.22, w: legendW - 0.24, h: 0.18,
2967
+ fontSize: 10, color: COLORS.secondary, fontFace: FONTS.body,
2968
+ });
2969
+ });
2970
+ }
2971
+ else {
2972
+ slide.addText('(暂无图表数据)', {
2973
+ x: chartX, y: chartY + 1.2, w: chartW, h: 0.6,
2974
+ fontSize: 16, color: COLORS.secondary, align: 'center', valign: 'middle', fontFace: FONTS.body,
2975
+ });
2976
+ }
2977
+ if (hasConclusion) {
2978
+ addTheme05ConclusionCard(slide, 6.55, chartY, 2.8, 3.1, props.conclusion);
2979
+ }
2980
+ addTheme05SpectrumBar(slide);
2981
+ }
2982
+ function renderTheme05RadarV1(slide, props) {
2983
+ if (props.kicker) {
2984
+ slide.addText(props.kicker, {
2985
+ x: 0.65, y: 0.78, w: 4.0, h: 0.25,
2986
+ fontSize: 11, color: COLORS.accent, fontFace: FONTS.mono, bold: true,
2987
+ });
2988
+ }
2989
+ slide.addText(props.title ?? '', {
2990
+ x: 0.65, y: 1.15, w: 8.7, h: 0.7,
2991
+ fontSize: 38, color: COLORS.primary, bold: true, valign: 'top', fontFace: FONTS.heading,
2992
+ });
2993
+ if (props.subtitle) {
2994
+ slide.addText(props.subtitle, {
2995
+ x: 0.65, y: 1.85, w: 8.7, h: 0.35,
2996
+ fontSize: 13, color: COLORS.secondary, valign: 'top', fontFace: FONTS.body,
2997
+ });
2998
+ }
2999
+ const indicators = (props.indicators || []).slice(0, 10).filter((it) => it && it.name);
3000
+ const rawSeries = (props.series || []).slice(0, 5).filter((it) => it && it.name);
3001
+ const chartX = 0.65;
3002
+ const chartY = 2.15;
3003
+ const chartW = 6.0;
3004
+ const chartH = 3.15;
3005
+ if (indicators.length > 0 && rawSeries.length > 0) {
3006
+ const labels = indicators.map((it) => it.name);
3007
+ const data = rawSeries.map((s) => ({
3008
+ name: s.name,
3009
+ labels,
3010
+ values: (Array.isArray(s.values) ? s.values : String(s.values || '').split(/[,,]/).map((v) => parseFloat(v.trim())).filter((v) => !Number.isNaN(v))).slice(0, labels.length),
3011
+ })).filter((s) => s.values.length > 0);
3012
+ if (data.length > 0) {
3013
+ slide.addChart('radar', data, {
3014
+ x: chartX, y: chartY, w: chartW, h: chartH,
3015
+ chartColors: rawSeries.map((s) => theme05SchemeColor(s.scheme)),
3016
+ showValue: false,
3017
+ showLegend: false,
3018
+ });
3019
+ }
3020
+ const legendX = chartX + chartW + 0.3;
3021
+ const legendW = 2.8;
3022
+ rawSeries.forEach((s, idx) => {
3023
+ const y = chartY + idx * 0.32;
3024
+ const color = theme05SchemeColor(s.scheme);
3025
+ slide.addShape('ellipse', {
3026
+ x: legendX, y, w: 0.14, h: 0.14,
3027
+ fill: { color },
3028
+ });
3029
+ slide.addText(s.name, {
3030
+ x: legendX + 0.22, y: y - 0.02, w: legendW - 0.22, h: 0.2,
3031
+ fontSize: 12, color: COLORS.primary, bold: true, fontFace: FONTS.body,
3032
+ });
3033
+ });
3034
+ }
3035
+ else {
3036
+ slide.addText('(暂无图表数据)', {
3037
+ x: chartX, y: chartY + 1.2, w: chartW, h: 0.6,
3038
+ fontSize: 16, color: COLORS.secondary, align: 'center', valign: 'middle', fontFace: FONTS.body,
3039
+ });
3040
+ }
3041
+ addTheme05SpectrumBar(slide);
3042
+ }
3043
+ function renderTheme05TreemapV1(slide, props) {
3044
+ if (props.kicker) {
3045
+ slide.addText(props.kicker, {
3046
+ x: 0.65, y: 0.78, w: 4.0, h: 0.25,
3047
+ fontSize: 11, color: COLORS.accent, fontFace: FONTS.mono, bold: true,
3048
+ });
3049
+ }
3050
+ slide.addText(props.title ?? '', {
3051
+ x: 0.65, y: 1.15, w: 8.7, h: 0.7,
3052
+ fontSize: 38, color: COLORS.primary, bold: true, valign: 'top', fontFace: FONTS.heading,
3053
+ });
3054
+ if (props.subtitle) {
3055
+ slide.addText(props.subtitle, {
3056
+ x: 0.65, y: 1.85, w: 8.7, h: 0.35,
3057
+ fontSize: 13, color: COLORS.secondary, valign: 'top', fontFace: FONTS.body,
3058
+ });
3059
+ }
3060
+ const items = (props.items || []).slice(0, 16).filter((it) => it && it.name);
3061
+ const chartX = 0.65;
3062
+ const chartY = 2.35;
3063
+ const chartW = 8.7;
3064
+ const chartH = 2.85;
3065
+ if (items.length > 0) {
3066
+ slide.addShape('roundRect', {
3067
+ x: chartX, y: chartY, w: chartW, h: chartH,
3068
+ fill: { color: COLORS.surfaceElevated },
3069
+ line: { color: COLORS.border, width: 1 },
3070
+ rectRadius: 0.12,
3071
+ });
3072
+ const rects = layoutTreemap(items, chartX + 0.06, chartY + 0.06, chartW - 0.12, chartH - 0.12);
3073
+ rects.forEach(({ x, y, w, h, item }) => {
3074
+ const color = theme05SchemeColor(item.scheme);
3075
+ if (w < 0.15 || h < 0.15)
3076
+ return;
3077
+ slide.addShape('roundRect', {
3078
+ x, y, w, h,
3079
+ fill: { color },
3080
+ rectRadius: 0.06,
3081
+ });
3082
+ const label = `${item.name ?? ''}\n${item.value ?? ''}`;
3083
+ const fontSize = Math.min(13, Math.max(8, Math.min(w, h) * 6));
3084
+ slide.addText(label, {
3085
+ x: x + 0.04, y: y + 0.04, w: w - 0.08, h: h - 0.08,
3086
+ fontSize, color: 'FFFFFF', bold: true, align: 'center', valign: 'middle', fontFace: FONTS.body,
3087
+ });
3088
+ });
3089
+ }
3090
+ else {
3091
+ slide.addText('(暂无图表数据)', {
3092
+ x: chartX, y: chartY + 1.1, w: chartW, h: 0.6,
3093
+ fontSize: 16, color: COLORS.secondary, align: 'center', valign: 'middle', fontFace: FONTS.body,
3094
+ });
3095
+ }
3096
+ addTheme05SpectrumBar(slide);
3097
+ }
3098
+ export function registerTheme05Renderers(registerPptxLayoutRenderer) {
3099
+ registerPptxLayoutRenderer('theme05_cover_v1', renderTheme05CoverV1);
3100
+ registerPptxLayoutRenderer('theme05_table_of_contents_v1', renderTheme05TableOfContentsV1);
3101
+ registerPptxLayoutRenderer('theme05_chapter_v1', renderTheme05ChapterV1);
3102
+ registerPptxLayoutRenderer('theme05_content_v1', renderTheme05ContentV1);
3103
+ registerPptxLayoutRenderer('theme05_metric_v1', renderTheme05MetricV1);
3104
+ registerPptxLayoutRenderer('theme05_chart_v1', renderTheme05ChartV1);
3105
+ registerPptxLayoutRenderer('theme05_bubble_v1', renderTheme05BubbleV1);
3106
+ registerPptxLayoutRenderer('theme05_map_v1', renderTheme05MapV1);
3107
+ registerPptxLayoutRenderer('theme05_rank_v1', renderTheme05RankV1);
3108
+ registerPptxLayoutRenderer('theme05_heatmap_v1', renderTheme05HeatmapV1);
3109
+ registerPptxLayoutRenderer('theme05_waterfall_v1', renderTheme05WaterfallV1);
3110
+ registerPptxLayoutRenderer('theme05_quote_v1', renderTheme05QuoteV1);
3111
+ registerPptxLayoutRenderer('theme05_image_v1', renderTheme05ImageV1);
3112
+ registerPptxLayoutRenderer('theme05_versus_v1', renderTheme05VersusV1);
3113
+ registerPptxLayoutRenderer('theme05_process_v1', renderTheme05ProcessV1);
3114
+ registerPptxLayoutRenderer('theme05_timeline_v1', renderTheme05TimelineV1);
3115
+ registerPptxLayoutRenderer('theme05_matrix_v1', renderTheme05MatrixV1);
3116
+ registerPptxLayoutRenderer('theme05_quadrant_v1', renderTheme05QuadrantV1);
3117
+ registerPptxLayoutRenderer('theme05_risk_v1', renderTheme05RiskV1);
3118
+ registerPptxLayoutRenderer('theme05_closing_v1', renderTheme05ClosingV1);
3119
+ registerPptxLayoutRenderer('theme05_cover_ex_v1', renderTheme05CoverExV1);
3120
+ registerPptxLayoutRenderer('theme05_cover_ex_v2', renderTheme05CoverExV2);
3121
+ registerPptxLayoutRenderer('theme05_cover_hero_v1', renderTheme05CoverHeroV1);
3122
+ registerPptxLayoutRenderer('theme05_chapter_big_v1', renderTheme05ChapterBigV1);
3123
+ registerPptxLayoutRenderer('theme05_chapter_split_v1', renderTheme05ChapterSplitV1);
3124
+ registerPptxLayoutRenderer('theme05_chapter_numbered_v1', renderTheme05ChapterNumberedV1);
3125
+ registerPptxLayoutRenderer('theme05_chapter_image_v1', renderTheme05ChapterImageV1);
3126
+ registerPptxLayoutRenderer('theme05_metric_hero_v1', renderTheme05MetricHeroV1);
3127
+ registerPptxLayoutRenderer('theme05_metric_capacity_v1', renderTheme05MetricCapacityV1);
3128
+ registerPptxLayoutRenderer('theme05_metric_delta_v1', renderTheme05MetricDeltaV1);
3129
+ registerPptxLayoutRenderer('theme05_chart_share_v1', renderTheme05ChartShareV1);
3130
+ registerPptxLayoutRenderer('theme05_chart_stacked_v1', renderTheme05ChartStackedV1);
3131
+ registerPptxLayoutRenderer('theme05_chart_curve_v1', renderTheme05ChartCurveV1);
3132
+ registerPptxLayoutRenderer('theme05_chart_peak_v1', renderTheme05ChartPeakV1);
3133
+ registerPptxLayoutRenderer('theme05_chart_peaktrough_v1', renderTheme05ChartPeaktroughV1);
3134
+ registerPptxLayoutRenderer('theme05_chart_cumulative_v1', renderTheme05ChartCumulativeV1);
3135
+ registerPptxLayoutRenderer('theme05_table_of_contents_v2', renderTheme05TableOfContentsV2);
3136
+ registerPptxLayoutRenderer('theme05_process_v2', renderTheme05ProcessV2);
3137
+ registerPptxLayoutRenderer('theme05_comparison_v1', renderTheme05ComparisonV1);
3138
+ registerPptxLayoutRenderer('theme05_chart_funnel_v1', renderTheme05ChartFunnelV1);
3139
+ registerPptxLayoutRenderer('theme05_quote_v2', renderTheme05QuoteV2);
3140
+ registerPptxLayoutRenderer('theme05_chart_gauge_v1', renderTheme05ChartGaugeV1);
3141
+ registerPptxLayoutRenderer('theme05_bento_v1', renderTheme05BentoV1);
3142
+ registerPptxLayoutRenderer('theme05_case_v1', renderTheme05CaseV1);
3143
+ registerPptxLayoutRenderer('theme05_donut_v1', renderTheme05DonutV1);
3144
+ registerPptxLayoutRenderer('theme05_editorial_v1', renderTheme05EditorialV1);
3145
+ registerPptxLayoutRenderer('theme05_gallery_v1', renderTheme05GalleryV1);
3146
+ registerPptxLayoutRenderer('theme05_profile_v1', renderTheme05ProfileV1);
3147
+ registerPptxLayoutRenderer('theme05_radar_v1', renderTheme05RadarV1);
3148
+ registerPptxLayoutRenderer('theme05_roadmap_v1', renderTheme05RoadmapV1);
3149
+ registerPptxLayoutRenderer('theme05_scorecards_v1', renderTheme05ScorecardsV1);
3150
+ registerPptxLayoutRenderer('theme05_treemap_v1', renderTheme05TreemapV1);
3151
+ }
3152
+ //# sourceMappingURL=theme05-pptx.js.map