@lemonppt/renderer 1.0.7 → 1.0.8

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 (84) hide show
  1. package/dist/browser-export-driver.d.ts +28 -0
  2. package/dist/browser-export-driver.d.ts.map +1 -0
  3. package/dist/browser-export-driver.js +115 -0
  4. package/dist/browser-export-driver.js.map +1 -0
  5. package/dist/browser-export.d.ts +66 -0
  6. package/dist/browser-export.d.ts.map +1 -0
  7. package/dist/browser-export.js +176 -0
  8. package/dist/browser-export.js.map +1 -0
  9. package/dist/client/browser-export.js +1 -0
  10. package/dist/client/client-render.js +21 -21
  11. package/dist/client/theme-echarts.js +27 -27
  12. package/dist/export-pdf.d.ts.map +1 -1
  13. package/dist/export-pdf.js +6 -3
  14. package/dist/export-pdf.js.map +1 -1
  15. package/dist/export-pptx-screenshot.d.ts +36 -0
  16. package/dist/export-pptx-screenshot.d.ts.map +1 -0
  17. package/dist/export-pptx-screenshot.js +74 -0
  18. package/dist/export-pptx-screenshot.js.map +1 -0
  19. package/dist/index.d.ts +3 -1
  20. package/dist/index.d.ts.map +1 -1
  21. package/dist/index.js +3 -1
  22. package/dist/index.js.map +1 -1
  23. package/dist/normalize-goal.d.ts +6 -0
  24. package/dist/normalize-goal.d.ts.map +1 -0
  25. package/dist/normalize-goal.js +12 -0
  26. package/dist/normalize-goal.js.map +1 -0
  27. package/dist/preview-server.d.ts +9 -0
  28. package/dist/preview-server.d.ts.map +1 -0
  29. package/dist/preview-server.js +81 -0
  30. package/dist/preview-server.js.map +1 -0
  31. package/dist/render-editor-data.js +2 -2
  32. package/dist/render-editor-data.js.map +1 -1
  33. package/dist/render.d.ts.map +1 -1
  34. package/dist/render.js +14 -4
  35. package/dist/render.js.map +1 -1
  36. package/package.json +8 -5
  37. package/dist/export-pptx.d.ts +0 -1167
  38. package/dist/export-pptx.d.ts.map +0 -1
  39. package/dist/export-pptx.js +0 -3007
  40. package/dist/export-pptx.js.map +0 -1
  41. package/dist/theme01-pptx.d.ts +0 -12
  42. package/dist/theme01-pptx.d.ts.map +0 -1
  43. package/dist/theme01-pptx.js +0 -2957
  44. package/dist/theme01-pptx.js.map +0 -1
  45. package/dist/theme02-pptx.d.ts +0 -5
  46. package/dist/theme02-pptx.d.ts.map +0 -1
  47. package/dist/theme02-pptx.js +0 -1063
  48. package/dist/theme02-pptx.js.map +0 -1
  49. package/dist/theme03-pptx.d.ts +0 -5
  50. package/dist/theme03-pptx.d.ts.map +0 -1
  51. package/dist/theme03-pptx.js +0 -3279
  52. package/dist/theme03-pptx.js.map +0 -1
  53. package/dist/theme04-pptx.d.ts +0 -6
  54. package/dist/theme04-pptx.d.ts.map +0 -1
  55. package/dist/theme04-pptx.js +0 -5496
  56. package/dist/theme04-pptx.js.map +0 -1
  57. package/dist/theme05-pptx.d.ts +0 -5
  58. package/dist/theme05-pptx.d.ts.map +0 -1
  59. package/dist/theme05-pptx.js +0 -3152
  60. package/dist/theme05-pptx.js.map +0 -1
  61. package/dist/theme06-pptx.d.ts +0 -124
  62. package/dist/theme06-pptx.d.ts.map +0 -1
  63. package/dist/theme06-pptx.js +0 -8154
  64. package/dist/theme06-pptx.js.map +0 -1
  65. package/dist/theme07-pptx.d.ts +0 -49
  66. package/dist/theme07-pptx.d.ts.map +0 -1
  67. package/dist/theme07-pptx.js +0 -2579
  68. package/dist/theme07-pptx.js.map +0 -1
  69. package/dist/theme08-pptx.d.ts +0 -19
  70. package/dist/theme08-pptx.d.ts.map +0 -1
  71. package/dist/theme08-pptx.js +0 -1411
  72. package/dist/theme08-pptx.js.map +0 -1
  73. package/dist/theme09-pptx.d.ts +0 -18
  74. package/dist/theme09-pptx.d.ts.map +0 -1
  75. package/dist/theme09-pptx.js +0 -1513
  76. package/dist/theme09-pptx.js.map +0 -1
  77. package/dist/theme10-pptx.d.ts +0 -18
  78. package/dist/theme10-pptx.d.ts.map +0 -1
  79. package/dist/theme10-pptx.js +0 -2889
  80. package/dist/theme10-pptx.js.map +0 -1
  81. package/dist/theme11-pptx.d.ts +0 -17
  82. package/dist/theme11-pptx.d.ts.map +0 -1
  83. package/dist/theme11-pptx.js +0 -908
  84. package/dist/theme11-pptx.js.map +0 -1
@@ -1,1063 +0,0 @@
1
- // lemonPPT - AI-powered presentation generation
2
- // Copyright (c) 2026 lemonforme
3
- // SPDX-License-Identifier: AGPL-3.0-or-later
4
- //
5
- // theme02 深色霓虹科技风 — PPTX 版式专属渲染器(从 export-pptx.ts 抽出)。
6
- // 共享基础设施(全局 helper + 通用渲染器 + 类型 + theme02 局部 helper/const)从 export-pptx.ts 导入;
7
- // theme01 通用渲染器从 theme01-pptx.ts 复用。注册通过 registerTheme02Renderers 回调注入。
8
- import { addKicker, addTitle, COLORS, FONTS, addTheme02Card, renderInsightPanel, renderTableOfContentsV1, renderImageV1, renderTestimonialV1, renderProcessV1, T2_CHART, addImageMaybe, renderTheme01MetricBig, renderChartFunnel, renderTheme01ChartDonut, renderChartHeatmap, renderChartRadar, renderChartGauge, renderStatsV1, renderTeamV1, renderTimelineV1, renderFeatureV1, renderTheme01GalleryV1, renderPricingV1, renderTheme01RoadmapV1, renderSwotV1, renderFaqV1, renderTheme01FilmstripV1, renderPartnersV1, renderPestV1, renderTheme01TableV1, renderTheme01TagsV1 } from './export-pptx.js';
9
- import { renderChartV1, renderContentV1, renderCoverV1, renderClosing, renderComparisonV1 } from './export-pptx.js';
10
- function renderTheme02ChapterV1(slide, props) {
11
- if (props.kicker)
12
- addKicker(slide, props.kicker);
13
- if (props.title)
14
- addTitle(slide, props.title);
15
- if (props.number) {
16
- slide.addText(props.number, {
17
- x: 0.8, y: 2.4, w: 8.4, h: 1.0,
18
- fontSize: 72, color: COLORS.accent, bold: true, align: 'left',
19
- fontFace: FONTS.mono,
20
- });
21
- }
22
- if (props.subtitle) {
23
- slide.addText(props.subtitle, {
24
- x: 0.8, y: props.number ? 3.6 : 2.4, w: 8.4, h: 0.8,
25
- fontSize: 22, color: COLORS.secondary, align: 'left', valign: 'top',
26
- fontFace: FONTS.body,
27
- });
28
- }
29
- }
30
- function renderTheme02ChapterV2(slide, props) {
31
- addTheme02Card(slide, 0.8, 1.0, 8.4, 3.8, 0.2);
32
- if (props.kicker)
33
- addKicker(slide, props.kicker, 0.8, 1.2);
34
- if (props.number) {
35
- slide.addText(props.number, {
36
- x: 0.8, y: 1.8, w: 8.4, h: 1.4,
37
- fontSize: 96, color: COLORS.accent, bold: true, align: 'center',
38
- fontFace: FONTS.mono,
39
- });
40
- }
41
- if (props.title) {
42
- slide.addText(props.title, {
43
- x: 0.8, y: props.number ? 3.3 : 1.8, w: 8.4, h: 0.9,
44
- fontSize: 48, color: COLORS.primary, bold: true, align: 'center', valign: 'top',
45
- fontFace: FONTS.heading,
46
- });
47
- }
48
- if (props.subtitle) {
49
- slide.addText(props.subtitle, {
50
- x: 1.2, y: props.title ? 4.3 : 3.0, w: 7.6, h: 0.6,
51
- fontSize: 20, color: COLORS.secondary, align: 'center', valign: 'top',
52
- fontFace: FONTS.body,
53
- });
54
- }
55
- }
56
- function renderTheme02QuoteV2(slide, props) {
57
- addTheme02Card(slide, 0.9, 1.4, 8.2, 3.2, 0.15);
58
- slide.addText(`“${props.quote}”`, {
59
- x: 1.1, y: 1.7, w: 7.8, h: 2.0,
60
- fontSize: 32, color: COLORS.primary, bold: true, align: 'center', valign: 'middle',
61
- fontFace: FONTS.heading,
62
- });
63
- const attribution = [props.author, props.role, props.source].filter(Boolean).join(' · ');
64
- if (attribution) {
65
- slide.addText(attribution, {
66
- x: 1.1, y: 3.85, w: 7.8, h: 0.4,
67
- fontSize: 16, color: COLORS.secondary, align: 'center',
68
- fontFace: FONTS.mono,
69
- });
70
- }
71
- }
72
- function renderTheme02NumberShowcaseV1(slide, props) {
73
- addTheme02Card(slide, 1.2, 1.1, 7.6, 3.8, 0.2);
74
- if (props.kicker)
75
- addKicker(slide, props.kicker, 0.8, 1.0);
76
- if (props.title) {
77
- slide.addText(props.title, {
78
- x: 0.8, y: 1.5, w: 8.4, h: 0.7,
79
- fontSize: 36, color: COLORS.primary, bold: true, align: 'center', valign: 'top',
80
- fontFace: FONTS.heading,
81
- });
82
- }
83
- const valueY = props.title ? 2.35 : 1.7;
84
- slide.addText(props.value ?? '', {
85
- x: 0.8, y: valueY, w: 8.4, h: 1.5,
86
- fontSize: 80, color: COLORS.accent, bold: true, align: 'center', valign: 'top',
87
- fontFace: FONTS.mono,
88
- });
89
- if (props.unit) {
90
- slide.addText(props.unit, {
91
- x: 0.8, y: valueY + 1.35, w: 8.4, h: 0.35,
92
- fontSize: 22, color: COLORS.secondary, align: 'center',
93
- fontFace: FONTS.mono,
94
- });
95
- }
96
- if (props.description) {
97
- slide.addText(props.description, {
98
- x: 1.2, y: props.unit ? valueY + 1.85 : valueY + 1.55, w: 7.6, h: 0.7,
99
- fontSize: 18, color: COLORS.secondary, align: 'center', valign: 'top',
100
- fontFace: FONTS.body,
101
- });
102
- }
103
- if (props.footnote) {
104
- slide.addText(props.footnote, {
105
- x: 0.8, y: 5.35, w: 8.4, h: 0.25,
106
- fontSize: 11, color: COLORS.secondary, align: 'center',
107
- fontFace: FONTS.mono,
108
- });
109
- }
110
- }
111
- function renderTheme02ChartV1(slide, props) {
112
- const adapted = {
113
- ...props,
114
- type: props.type === 'area' ? 'line' : props.type,
115
- };
116
- renderChartV1(slide, adapted);
117
- }
118
- function renderTheme02ContentV1(slide, props) {
119
- renderContentV1(slide, {
120
- kicker: props.kicker,
121
- title: props.title ?? '',
122
- points: props.bullets || [],
123
- });
124
- }
125
- function renderTheme02DeltaV1(slide, props) {
126
- const hasInsight = props.showInsight !== false && !!props.insight && (!!props.insight.value || !!props.insight.label || !!props.insight.description);
127
- const mainW = hasInsight ? 6.8 : 8.4;
128
- const startX = 0.8;
129
- if (props.kicker)
130
- addKicker(slide, props.kicker, startX);
131
- if (props.title)
132
- addTitle(slide, props.title, startX, 1.3, mainW);
133
- if (props.subtitle) {
134
- slide.addText(props.subtitle, {
135
- x: startX, y: 2.05, w: mainW, h: 0.35,
136
- fontSize: 16, color: COLORS.secondary, align: 'left', valign: 'top',
137
- fontFace: FONTS.body,
138
- });
139
- }
140
- const items = (props.items || []).slice(0, 6);
141
- if (items.length === 0) {
142
- slide.addText('(暂无对照数据)', {
143
- x: 2, y: 3.2, w: 6, h: 0.8,
144
- fontSize: 18, color: COLORS.secondary, align: 'center', valign: 'middle',
145
- fontFace: FONTS.body,
146
- });
147
- return;
148
- }
149
- const cols = Math.min(items.length, 3);
150
- const rows = Math.ceil(items.length / cols);
151
- const cardW = (mainW - 0.25 * (cols - 1)) / cols;
152
- const cardH = rows === 1 ? 2.6 : 1.25;
153
- const startY = 2.5;
154
- items.forEach((item, index) => {
155
- const col = index % cols;
156
- const row = Math.floor(index / cols);
157
- const x = startX + col * (cardW + 0.25);
158
- const y = startY + row * (cardH + 0.25);
159
- const previous = Number(item.previous) || 0;
160
- const current = Number(item.current) || 0;
161
- const delta = previous > 0 ? ((current - previous) / previous) * 100 : 0;
162
- const positive = delta >= 0;
163
- addTheme02Card(slide, x, y, cardW, cardH, 0.1);
164
- slide.addText(item.label || '', {
165
- x: x + 0.12, y: y + 0.1, w: cardW - 0.24, h: 0.35,
166
- fontSize: 14, color: COLORS.secondary, align: 'left', valign: 'top',
167
- fontFace: FONTS.body,
168
- });
169
- slide.addText(`${current}${item.unit ? ` ${item.unit}` : ''}`, {
170
- x: x + 0.12, y: y + 0.5, w: cardW - 0.24, h: 0.45,
171
- fontSize: 24, color: positive ? COLORS.accent : 'FF6B6B', bold: true, align: 'left', valign: 'top',
172
- fontFace: FONTS.heading,
173
- });
174
- slide.addText(`${positive ? '+' : ''}${delta.toFixed(1)}%`, {
175
- x: x + 0.12, y: y + 1.0, w: cardW - 0.24, h: 0.3,
176
- fontSize: 12, color: positive ? COLORS.accent : 'FF6B6B', align: 'left', valign: 'top',
177
- fontFace: FONTS.mono,
178
- });
179
- });
180
- if (props.footnote) {
181
- slide.addText(props.footnote, {
182
- x: startX, y: 5.75, w: mainW, h: 0.25,
183
- fontSize: 11, color: COLORS.secondary, align: 'left',
184
- fontFace: FONTS.mono,
185
- });
186
- }
187
- if (hasInsight) {
188
- renderInsightPanel(slide, props.insight, 8.0, 2.3, 2.4, 3.2);
189
- }
190
- }
191
- function renderTheme02TableOfContentsV1(slide, props) {
192
- renderTableOfContentsV1(slide, {
193
- title: props.title ?? '目录',
194
- kicker: props.subtitle,
195
- items: (props.items || []).map((item) => item.title),
196
- });
197
- }
198
- function renderTheme02ImageV1(slide, props) {
199
- renderImageV1(slide, {
200
- title: props.title ?? '',
201
- subtitle: props.subtitle,
202
- imageUrl: props.image,
203
- });
204
- if (props.caption) {
205
- slide.addText(props.caption, {
206
- x: 0.8, y: 5.25, w: 8.4, h: 0.3,
207
- fontSize: 11, color: COLORS.secondary, align: 'left',
208
- fontFace: FONTS.mono,
209
- });
210
- }
211
- }
212
- function renderTheme02QuoteV1(slide, props) {
213
- renderTestimonialV1(slide, {
214
- quote: props.quote,
215
- author: props.author,
216
- role: props.role,
217
- avatarUrl: props.avatar,
218
- });
219
- }
220
- function renderTheme02BentoV1(slide, props) {
221
- const hasInsight = props.showInsight !== false && !!props.insight && (!!props.insight.value || !!props.insight.label || !!props.insight.description);
222
- const mainW = hasInsight ? 6.8 : 8.4;
223
- const startX = 0.8;
224
- if (props.kicker)
225
- addKicker(slide, props.kicker, startX);
226
- if (props.title)
227
- addTitle(slide, props.title, startX, 1.3, mainW);
228
- if (props.subtitle) {
229
- slide.addText(props.subtitle, {
230
- x: startX, y: 2.05, w: mainW, h: 0.35,
231
- fontSize: 16, color: COLORS.secondary, align: 'left', valign: 'top',
232
- fontFace: FONTS.body,
233
- });
234
- }
235
- const items = (props.items || []).slice(0, 6);
236
- if (items.length === 0) {
237
- slide.addText('(暂无数据)', {
238
- x: 2, y: 3.2, w: 6, h: 0.8,
239
- fontSize: 18, color: COLORS.secondary, align: 'center', valign: 'middle',
240
- fontFace: FONTS.body,
241
- });
242
- return;
243
- }
244
- const cardW = (mainW - 0.4) / 2;
245
- const positions = [
246
- { x: startX, y: 2.5, w: cardW, h: 1.3 },
247
- { x: startX + cardW + 0.2, y: 2.5, w: cardW, h: 1.3 },
248
- { x: startX, y: 4.0, w: cardW, h: 1.3 },
249
- { x: startX + cardW + 0.2, y: 4.0, w: cardW, h: 1.3 },
250
- { x: startX, y: 5.5, w: cardW, h: 1.3 },
251
- { x: startX + cardW + 0.2, y: 5.5, w: cardW, h: 1.3 },
252
- ];
253
- items.forEach((item, index) => {
254
- const pos = positions[index];
255
- if (!pos)
256
- return;
257
- addTheme02Card(slide, pos.x, pos.y, pos.w, pos.h, 0.1);
258
- const valueText = `${item.value}${item.unit ? ` ${item.unit}` : ''}`;
259
- slide.addText(valueText, {
260
- x: pos.x + 0.12, y: pos.y + 0.12, w: pos.w - 0.24, h: 0.45,
261
- fontSize: 24, color: COLORS.primary, bold: true, align: 'left', valign: 'top',
262
- fontFace: FONTS.heading,
263
- });
264
- slide.addText(item.label, {
265
- x: pos.x + 0.12, y: pos.y + 0.62, w: pos.w - 0.24, h: 0.35,
266
- fontSize: 12, color: COLORS.secondary, align: 'left', valign: 'top',
267
- fontFace: FONTS.body,
268
- });
269
- });
270
- if (hasInsight) {
271
- renderInsightPanel(slide, props.insight, 8.0, 2.3, 2.4, 3.2);
272
- }
273
- }
274
- function renderTheme02ProcessV1(slide, props) {
275
- const steps = (props.steps || []).map((s) => s.title || '').filter(Boolean);
276
- renderProcessV1(slide, {
277
- kicker: props.kicker,
278
- title: props.title,
279
- steps,
280
- });
281
- }
282
- function renderTheme02ProgressV1(slide, props) {
283
- if (props.kicker)
284
- addKicker(slide, props.kicker);
285
- if (props.title)
286
- addTitle(slide, props.title);
287
- if (props.subtitle) {
288
- slide.addText(props.subtitle, {
289
- x: 0.8, y: 2.05, w: 8.4, h: 0.35,
290
- fontSize: 16, color: COLORS.secondary, align: 'left', valign: 'top',
291
- fontFace: FONTS.body,
292
- });
293
- }
294
- const items = (props.items || []).slice(0, 6);
295
- if (items.length === 0) {
296
- slide.addText('(暂无进度数据)', {
297
- x: 2, y: 3.2, w: 6, h: 0.8,
298
- fontSize: 18, color: COLORS.secondary, align: 'center', valign: 'middle',
299
- fontFace: FONTS.body,
300
- });
301
- return;
302
- }
303
- let y = 2.5;
304
- items.forEach((item) => {
305
- const value = Number(item.value) || 0;
306
- const max = Number(item.max) || 100;
307
- const pct = max > 0 ? Math.min(100, (value / max) * 100) : 0;
308
- slide.addText(item.label || '', {
309
- x: 0.8, y, w: 5.0, h: 0.3,
310
- fontSize: 14, color: COLORS.primary, align: 'left', valign: 'top',
311
- fontFace: FONTS.body,
312
- });
313
- slide.addText(`${value}${item.unit ? ` ${item.unit}` : ''} (${pct.toFixed(1)}%)`, {
314
- x: 5.8, y, w: 3.4, h: 0.3,
315
- fontSize: 14, color: COLORS.accent, bold: true, align: 'right', valign: 'top',
316
- fontFace: FONTS.mono,
317
- });
318
- slide.addShape('rect', {
319
- x: 0.8, y: y + 0.35, w: 8.4, h: 0.12,
320
- fill: { color: COLORS.light },
321
- });
322
- slide.addShape('rect', {
323
- x: 0.8, y: y + 0.35, w: 8.4 * (pct / 100), h: 0.12,
324
- fill: { color: COLORS.accent },
325
- });
326
- y += 0.65;
327
- });
328
- }
329
- function renderTheme02FeatureV2(slide, props) {
330
- const x = 0.8;
331
- const w = 8.4;
332
- if (props.kicker)
333
- addKicker(slide, props.kicker, x);
334
- if (props.title)
335
- addTitle(slide, props.title, x, 1.25, w);
336
- if (props.subtitle)
337
- slide.addText(props.subtitle, { x, y: 2.05, w, h: 0.4, fontSize: 16, color: COLORS.secondary, align: 'left', valign: 'top', fontFace: FONTS.body });
338
- const features = (props.features || []).slice(0, 6);
339
- if (features.length === 0)
340
- return;
341
- const cols = 3;
342
- const rows = Math.ceil(features.length / cols);
343
- const cardW = (w - 0.4 * (cols - 1)) / cols;
344
- const cardH = rows >= 2 ? 1.25 : 2.2;
345
- const startY = 2.55;
346
- features.forEach((f, i) => {
347
- const c = i % cols;
348
- const r = Math.floor(i / cols);
349
- const cx = x + c * (cardW + 0.4);
350
- const cy = startY + r * (cardH + 0.28);
351
- addTheme02Card(slide, cx, cy, cardW, cardH, 0.12);
352
- let ty = cy + 0.16;
353
- if (f.icon) {
354
- slide.addText(f.icon, { x: cx + 0.22, y: ty, w: cardW - 0.44, h: 0.38, fontSize: 22, align: 'left', valign: 'top', fontFace: FONTS.body });
355
- ty += 0.44;
356
- }
357
- slide.addText(f.title || '', { x: cx + 0.22, y: ty, w: cardW - 0.44, h: 0.36, fontSize: 15, bold: true, color: COLORS.primary, align: 'left', valign: 'top', fontFace: FONTS.heading });
358
- ty += 0.38;
359
- if (f.desc)
360
- slide.addText(f.desc, { x: cx + 0.22, y: ty, w: cardW - 0.44, h: cardH - (ty - cy) - 0.12, fontSize: 11, color: COLORS.secondary, align: 'left', valign: 'top', fontFace: FONTS.body });
361
- });
362
- }
363
- function renderTheme02ChecklistV1(slide, props) {
364
- const x = 0.8;
365
- const w = 8.4;
366
- if (props.kicker)
367
- addKicker(slide, props.kicker, x);
368
- if (props.title)
369
- addTitle(slide, props.title, x, 1.25, w);
370
- if (props.subtitle)
371
- slide.addText(props.subtitle, { x, y: 2.05, w, h: 0.4, fontSize: 16, color: COLORS.secondary, align: 'left', valign: 'top', fontFace: FONTS.body });
372
- const items = (props.items || []).slice(0, 8);
373
- if (items.length === 0)
374
- return;
375
- let y = 2.65;
376
- const rowH = Math.min(0.5, (5.05 - 2.65) / items.length);
377
- items.forEach((it) => {
378
- slide.addText('✓', { x, y, w: 0.42, h: rowH, fontSize: 18, bold: true, color: COLORS.accent, align: 'center', valign: 'middle', fontFace: FONTS.body });
379
- slide.addText(it.text || '', { x: x + 0.55, y, w: w - 0.55, h: rowH, fontSize: 16, bold: true, color: COLORS.primary, align: 'left', valign: 'middle', fontFace: FONTS.heading });
380
- if (it.note && items.length <= 5)
381
- slide.addText(it.note, { x: x + 0.55, y: y + rowH - 0.22, w: w - 0.55, h: 0.2, fontSize: 11, color: COLORS.secondary, align: 'left', valign: 'top', fontFace: FONTS.body });
382
- y += rowH + 0.05;
383
- });
384
- }
385
- function renderTheme02StepsV1(slide, props) {
386
- const x = 0.8;
387
- const w = 8.4;
388
- if (props.kicker)
389
- addKicker(slide, props.kicker, x);
390
- if (props.title)
391
- addTitle(slide, props.title, x, 1.25, w);
392
- if (props.subtitle)
393
- slide.addText(props.subtitle, { x, y: 2.05, w, h: 0.4, fontSize: 16, color: COLORS.secondary, align: 'left', valign: 'top', fontFace: FONTS.body });
394
- const steps = (props.steps || []).slice(0, 6);
395
- if (steps.length === 0)
396
- return;
397
- const cols = 3;
398
- const rows = Math.ceil(steps.length / cols);
399
- const cardW = (w - 0.4 * (cols - 1)) / cols;
400
- const cardH = rows >= 2 ? 1.4 : 2.4;
401
- const startY = 2.6;
402
- steps.forEach((s, i) => {
403
- const c = i % cols;
404
- const r = Math.floor(i / cols);
405
- const cx = x + c * (cardW + 0.4);
406
- const cy = startY + r * (cardH + 0.28);
407
- addTheme02Card(slide, cx, cy, cardW, cardH, 0.12);
408
- slide.addText(String(i + 1), { x: cx + 0.2, y: cy + 0.16, w: 0.6, h: 0.5, fontSize: 22, bold: true, color: COLORS.accent, align: 'left', valign: 'top', fontFace: FONTS.heading });
409
- slide.addText(s.title || '', { x: cx + 0.2, y: cy + 0.72, w: cardW - 0.4, h: 0.4, fontSize: 15, bold: true, color: COLORS.primary, align: 'left', valign: 'top', fontFace: FONTS.heading });
410
- if (s.desc)
411
- slide.addText(s.desc, { x: cx + 0.2, y: cy + 1.12, w: cardW - 0.4, h: cardH - 1.2, fontSize: 11, color: COLORS.secondary, align: 'left', valign: 'top', fontFace: FONTS.body });
412
- });
413
- }
414
- function renderTheme02CardGridV1(slide, props) {
415
- const x = 0.8;
416
- const w = 8.4;
417
- if (props.kicker)
418
- addKicker(slide, props.kicker, x);
419
- if (props.title)
420
- addTitle(slide, props.title, x, 1.25, w);
421
- if (props.subtitle)
422
- slide.addText(props.subtitle, { x, y: 2.05, w, h: 0.4, fontSize: 16, color: COLORS.secondary, align: 'left', valign: 'top', fontFace: FONTS.body });
423
- const cards = (props.cards || []).slice(0, 9);
424
- if (cards.length === 0)
425
- return;
426
- const cols = 3;
427
- const rows = Math.ceil(cards.length / cols);
428
- const cardW = (w - 0.36 * (cols - 1)) / cols;
429
- const cardH = (5.05 - 2.55) / rows - 0.28;
430
- const startY = 2.55;
431
- cards.forEach((cd, i) => {
432
- const c = i % cols;
433
- const r = Math.floor(i / cols);
434
- const cx = x + c * (cardW + 0.36);
435
- const cy = startY + r * (cardH + 0.28);
436
- addTheme02Card(slide, cx, cy, cardW, cardH, 0.12);
437
- let ty = cy + 0.16;
438
- if (cd.tag) {
439
- slide.addText(cd.tag, { x: cx + 0.2, y: ty, w: cardW - 0.4, h: 0.26, fontSize: 11, bold: true, color: COLORS.accent, align: 'left', valign: 'top', fontFace: FONTS.mono });
440
- ty += 0.32;
441
- }
442
- slide.addText(cd.title || '', { x: cx + 0.2, y: ty, w: cardW - 0.4, h: 0.4, fontSize: 15, bold: true, color: COLORS.primary, align: 'left', valign: 'top', fontFace: FONTS.heading });
443
- ty += 0.42;
444
- if (cd.desc)
445
- slide.addText(cd.desc, { x: cx + 0.2, y: ty, w: cardW - 0.4, h: cardH - (ty - cy) - 0.12, fontSize: 11, color: COLORS.secondary, align: 'left', valign: 'top', fontFace: FONTS.body });
446
- });
447
- }
448
- function renderTheme02HighlightV1(slide, props) {
449
- const x = 0.8;
450
- const w = 8.4;
451
- if (props.kicker)
452
- slide.addText(props.kicker, { x, y: 1.5, w, h: 0.4, fontSize: 14, bold: true, color: COLORS.accent, align: 'center', valign: 'top', fontFace: FONTS.mono });
453
- if (props.title)
454
- slide.addText(props.title, { x, y: 2.0, w, h: 0.4, fontSize: 18, bold: true, color: COLORS.secondary, align: 'center', valign: 'top', fontFace: FONTS.heading });
455
- if (props.statement)
456
- slide.addText(props.statement, { x: x + 0.4, y: 2.5, w: w - 0.8, h: 1.8, fontSize: 30, bold: true, color: COLORS.primary, align: 'center', valign: 'middle', fontFace: FONTS.heading });
457
- if (props.footnote)
458
- slide.addText(props.footnote, { x, y: 4.5, w, h: 0.4, fontSize: 14, color: COLORS.secondary, align: 'center', valign: 'top', fontFace: FONTS.body });
459
- }
460
- function renderTheme02ComparisonV2(slide, props) {
461
- const x = 0.8;
462
- const w = 8.4;
463
- if (props.kicker)
464
- addKicker(slide, props.kicker, x);
465
- if (props.title)
466
- addTitle(slide, props.title, x, 1.2, w);
467
- if (props.subtitle)
468
- slide.addText(props.subtitle, { x, y: 1.95, w, h: 0.4, fontSize: 16, color: COLORS.secondary, align: 'left', valign: 'top', fontFace: FONTS.body });
469
- const cols = (props.columns || []).slice(0, 2);
470
- if (cols.length === 0)
471
- return;
472
- const colW = (w - 0.4) / 2;
473
- const startY = 2.5;
474
- const colH = 2.4;
475
- cols.forEach((col, i) => {
476
- const cx = x + i * (colW + 0.4);
477
- addTheme02Card(slide, cx, startY, colW, colH, 0.12);
478
- slide.addText(col.title || '', { x: cx + 0.25, y: startY + 0.2, w: colW - 0.5, h: 0.4, fontSize: 18, bold: true, color: i === 1 ? COLORS.accent : COLORS.primary, align: 'left', valign: 'top', fontFace: FONTS.heading });
479
- const points = (col.points || []).slice(0, 6);
480
- let py = startY + 0.75;
481
- const ph = (colH - 0.95) / Math.max(points.length, 1);
482
- points.forEach((p) => {
483
- slide.addText('• ' + p, { x: cx + 0.25, y: py, w: colW - 0.5, h: ph, fontSize: 13, color: COLORS.primary, align: 'left', valign: 'top', fontFace: FONTS.body });
484
- py += ph;
485
- });
486
- });
487
- }
488
- function renderTheme02MatrixV1(slide, props) {
489
- const x = 0.8;
490
- const w = 8.4;
491
- if (props.kicker)
492
- addKicker(slide, props.kicker, x);
493
- if (props.title)
494
- addTitle(slide, props.title, x, 1.2, w);
495
- if (props.subtitle)
496
- slide.addText(props.subtitle, { x, y: 1.95, w, h: 0.35, fontSize: 15, color: COLORS.secondary, align: 'left', valign: 'top', fontFace: FONTS.body });
497
- const startY = 2.5;
498
- const gridH = 2.4;
499
- const gridW = w - 0.5;
500
- const qColors = ['00E5B0', 'FFD166', '00B4FF', 'FF6B6B'];
501
- const quads = props.quadrants || [];
502
- for (let qi = 0; qi < 4; qi++) {
503
- const c = qi % 2;
504
- const r = Math.floor(qi / 2);
505
- const qx = x + 0.5 + c * (gridW / 2 + 0.1);
506
- const qy = startY + r * (gridH / 2 + 0.12);
507
- addTheme02Card(slide, qx, qy, gridW / 2 - 0.05, gridH / 2 - 0.06, 0.1);
508
- const q = quads[qi];
509
- if (q) {
510
- slide.addText(q.title || '', { x: qx + 0.18, y: qy + 0.12, w: gridW / 2 - 0.36, h: 0.35, fontSize: 15, bold: true, color: qColors[qi] || COLORS.accent, align: 'left', valign: 'top', fontFace: FONTS.heading });
511
- if (q.desc)
512
- slide.addText(q.desc, { x: qx + 0.18, y: qy + 0.5, w: gridW / 2 - 0.36, h: gridH / 2 - 0.6, fontSize: 11, color: COLORS.secondary, align: 'left', valign: 'top', fontFace: FONTS.body });
513
- }
514
- }
515
- if (props.axisY)
516
- slide.addText(props.axisY, { x: x - 0.05, y: startY + gridH / 2 - 0.2, w: 0.5, h: 0.4, fontSize: 12, color: COLORS.secondary, align: 'center', valign: 'middle', fontFace: FONTS.mono });
517
- if (props.axisX)
518
- slide.addText(props.axisX, { x: x + 0.5, y: startY + gridH + 0.02, w: gridW, h: 0.3, fontSize: 12, color: COLORS.secondary, align: 'center', valign: 'top', fontFace: FONTS.mono });
519
- }
520
- function renderTheme02StatGridV1(slide, props) {
521
- const x = 0.8;
522
- const w = 8.4;
523
- if (props.kicker)
524
- addKicker(slide, props.kicker, x);
525
- if (props.title)
526
- addTitle(slide, props.title, x, 1.25, w);
527
- if (props.subtitle)
528
- slide.addText(props.subtitle, { x, y: 2.05, w, h: 0.4, fontSize: 16, color: COLORS.secondary, align: 'left', valign: 'top', fontFace: FONTS.body });
529
- const stats = (props.stats || []).slice(0, 8);
530
- if (stats.length === 0)
531
- return;
532
- const cols = 4;
533
- const rows = Math.ceil(stats.length / cols);
534
- const cardW = (w - 0.36 * (cols - 1)) / cols;
535
- const cardH = (5.05 - 2.55) / rows - 0.26;
536
- const startY = 2.55;
537
- stats.forEach((s, i) => {
538
- const c = i % cols;
539
- const r = Math.floor(i / cols);
540
- const cx = x + c * (cardW + 0.36);
541
- const cy = startY + r * (cardH + 0.26);
542
- addTheme02Card(slide, cx, cy, cardW, cardH, 0.1);
543
- let ty = cy + 0.16;
544
- const valStr = (s.value || '') + (s.unit ? ' ' + s.unit : '');
545
- slide.addText(valStr, { x: cx + 0.2, y: ty, w: cardW - 0.4, h: 0.5, fontSize: 26, bold: true, color: COLORS.accent, align: 'left', valign: 'top', fontFace: FONTS.heading });
546
- ty += 0.55;
547
- if (s.label)
548
- slide.addText(s.label, { x: cx + 0.2, y: ty, w: cardW - 0.4, h: 0.3, fontSize: 12, color: COLORS.primary, align: 'left', valign: 'top', fontFace: FONTS.body });
549
- ty += 0.32;
550
- if (s.delta)
551
- slide.addText(s.delta, { x: cx + 0.2, y: ty, w: cardW - 0.4, h: 0.3, fontSize: 12, bold: true, color: COLORS.secondary, align: 'left', valign: 'top', fontFace: FONTS.mono });
552
- });
553
- }
554
- function renderTheme02CoverV3(slide, props) {
555
- const x = 0.8;
556
- const w = 8.4;
557
- if (props.kicker)
558
- slide.addText(props.kicker, { x, y: 1.7, w, h: 0.4, fontSize: 14, bold: true, color: COLORS.accent, align: 'center', valign: 'top', fontFace: FONTS.mono });
559
- if (props.title)
560
- slide.addText(props.title, { x, y: 2.2, w, h: 1.6, fontSize: 44, bold: true, color: COLORS.primary, align: 'center', valign: 'middle', fontFace: FONTS.heading });
561
- if (props.subtitle)
562
- slide.addText(props.subtitle, { x: x + 0.5, y: 3.9, w: w - 1, h: 0.8, fontSize: 18, color: COLORS.secondary, align: 'center', valign: 'top', fontFace: FONTS.body });
563
- if (props.date)
564
- slide.addText(props.date, { x, y: 4.8, w, h: 0.4, fontSize: 13, color: COLORS.secondary, align: 'center', valign: 'top', fontFace: FONTS.mono });
565
- }
566
- function renderTheme02ClosingV2(slide, props) {
567
- const x = 0.8;
568
- const w = 8.4;
569
- if (props.kicker)
570
- slide.addText(props.kicker, { x, y: 1.9, w, h: 0.4, fontSize: 14, bold: true, color: COLORS.accent, align: 'center', valign: 'top', fontFace: FONTS.mono });
571
- if (props.title)
572
- slide.addText(props.title, { x, y: 2.4, w, h: 1.4, fontSize: 40, bold: true, color: COLORS.primary, align: 'center', valign: 'middle', fontFace: FONTS.heading });
573
- if (props.subtitle)
574
- slide.addText(props.subtitle, { x: x + 0.5, y: 3.9, w: w - 1, h: 0.8, fontSize: 18, color: COLORS.secondary, align: 'center', valign: 'top', fontFace: FONTS.body });
575
- if (props.cta) {
576
- const ctaW = 3.2;
577
- slide.addText(props.cta, { x: (10 - ctaW) / 2, y: 4.85, w: ctaW, h: 0.5, fontSize: 16, bold: true, color: COLORS.white, fill: { color: COLORS.accent }, align: 'center', valign: 'middle', fontFace: FONTS.heading });
578
- }
579
- }
580
- function renderTheme02ChartBarV1(slide, props) {
581
- const x = 0.8;
582
- const w = 8.4;
583
- if (props.kicker)
584
- addKicker(slide, props.kicker, x);
585
- if (props.title)
586
- addTitle(slide, props.title, x, 1.25, w);
587
- if (props.subtitle)
588
- slide.addText(props.subtitle, { x, y: 2.05, w, h: 0.35, fontSize: 16, color: COLORS.secondary, align: 'left', fontFace: FONTS.body });
589
- const labels = props.labels || [];
590
- const series = (props.series || []).slice(0, 3);
591
- if (labels.length === 0 || series.length === 0)
592
- return;
593
- slide.addChart('bar', series.map((s) => ({ name: s.name || '', labels, values: (s.values || []) })), {
594
- x, y: 2.5, w, h: 2.9, chartColors: T2_CHART,
595
- showValue: true, dataLabelColor: COLORS.primary, dataLabelFontSize: 9,
596
- catAxisLabelColor: COLORS.secondary, valAxisLabelColor: COLORS.secondary,
597
- catAxisLabelFontFace: FONTS.body, valAxisLabelFontFace: FONTS.body,
598
- });
599
- }
600
- function renderTheme02ChartLineV1(slide, props) {
601
- const x = 0.8;
602
- const w = 8.4;
603
- if (props.kicker)
604
- addKicker(slide, props.kicker, x);
605
- if (props.title)
606
- addTitle(slide, props.title, x, 1.25, w);
607
- if (props.subtitle)
608
- slide.addText(props.subtitle, { x, y: 2.05, w, h: 0.35, fontSize: 16, color: COLORS.secondary, align: 'left', fontFace: FONTS.body });
609
- const labels = props.labels || [];
610
- const series = (props.series || []).slice(0, 4);
611
- if (labels.length === 0 || series.length === 0)
612
- return;
613
- slide.addChart('line', series.map((s) => ({ name: s.name || '', labels, values: (s.values || []) })), {
614
- x, y: 2.5, w, h: 2.9, chartColors: T2_CHART,
615
- lineDataSymbol: 'circle', lineDataSymbolSize: 6,
616
- catAxisLabelColor: COLORS.secondary, valAxisLabelColor: COLORS.secondary,
617
- catAxisLabelFontFace: FONTS.body, valAxisLabelFontFace: FONTS.body,
618
- });
619
- }
620
- function renderTheme02ChartAreaV1(slide, props) {
621
- const x = 0.8;
622
- const w = 8.4;
623
- if (props.kicker)
624
- addKicker(slide, props.kicker, x);
625
- if (props.title)
626
- addTitle(slide, props.title, x, 1.25, w);
627
- if (props.subtitle)
628
- slide.addText(props.subtitle, { x, y: 2.05, w, h: 0.35, fontSize: 16, color: COLORS.secondary, align: 'left', fontFace: FONTS.body });
629
- const labels = props.labels || [];
630
- const series = (props.series || []).slice(0, 3);
631
- if (labels.length === 0 || series.length === 0)
632
- return;
633
- slide.addChart('area', series.map((s) => ({ name: s.name || '', labels, values: (s.values || []) })), {
634
- x, y: 2.5, w, h: 2.9, chartColors: T2_CHART,
635
- catAxisLabelColor: COLORS.secondary, valAxisLabelColor: COLORS.secondary,
636
- catAxisLabelFontFace: FONTS.body, valAxisLabelFontFace: FONTS.body,
637
- });
638
- }
639
- function renderTheme02ChartStackV1(slide, props) {
640
- const x = 0.8;
641
- const w = 8.4;
642
- if (props.kicker)
643
- addKicker(slide, props.kicker, x);
644
- if (props.title)
645
- addTitle(slide, props.title, x, 1.25, w);
646
- if (props.subtitle)
647
- slide.addText(props.subtitle, { x, y: 2.05, w, h: 0.35, fontSize: 16, color: COLORS.secondary, align: 'left', fontFace: FONTS.body });
648
- const labels = props.labels || [];
649
- const series = (props.series || []).slice(0, 4);
650
- if (labels.length === 0 || series.length === 0)
651
- return;
652
- slide.addChart('bar', series.map((s) => ({ name: s.name || '', labels, values: (s.values || []) })), {
653
- x, y: 2.5, w, h: 2.9, chartColors: T2_CHART, barDir: 'bar', barGrouping: 'percentStacked',
654
- showValue: true, dataLabelColor: COLORS.white, dataLabelFontSize: 9,
655
- catAxisLabelColor: COLORS.secondary, valAxisLabelColor: COLORS.secondary,
656
- catAxisLabelFontFace: FONTS.body, valAxisLabelFontFace: FONTS.body,
657
- });
658
- }
659
- function renderTheme02KpiStripV1(slide, props) {
660
- const x = 0.8;
661
- const w = 8.4;
662
- if (props.kicker)
663
- addKicker(slide, props.kicker, x);
664
- if (props.title)
665
- addTitle(slide, props.title, x, 1.25, w);
666
- if (props.subtitle)
667
- slide.addText(props.subtitle, { x, y: 2.05, w, h: 0.4, fontSize: 16, color: COLORS.secondary, align: 'left', fontFace: FONTS.body });
668
- const items = (props.items || []).slice(0, 5);
669
- if (items.length === 0)
670
- return;
671
- const cols = items.length;
672
- const cardW = (w - 0.3 * (cols - 1)) / cols;
673
- const startY = 2.6;
674
- const cardH = 2.2;
675
- items.forEach((it, i) => {
676
- const cx = x + i * (cardW + 0.3);
677
- addTheme02Card(slide, cx, startY, cardW, cardH, 0.12);
678
- let ty = startY + 0.2;
679
- const valStr = (it.value || '') + (it.unit ? ' ' + it.unit : '');
680
- slide.addText(valStr, { x: cx + 0.2, y: ty, w: cardW - 0.4, h: 0.6, fontSize: 28, bold: true, color: COLORS.accent, align: 'left', fontFace: FONTS.heading });
681
- ty += 0.62;
682
- if (it.label)
683
- slide.addText(it.label || '', { x: cx + 0.2, y: ty, w: cardW - 0.4, h: 0.3, fontSize: 12, color: COLORS.primary, align: 'left', fontFace: FONTS.body });
684
- ty += 0.32;
685
- if (it.delta)
686
- slide.addText(it.delta || '', { x: cx + 0.2, y: ty, w: cardW - 0.4, h: 0.3, fontSize: 12, bold: true, color: COLORS.secondary, align: 'left', fontFace: FONTS.mono });
687
- });
688
- }
689
- function renderTheme02BigStatV1(slide, props) {
690
- const x = 0.8;
691
- const w = 8.4;
692
- if (props.kicker)
693
- slide.addText(props.kicker, { x, y: 1.5, w, h: 0.4, fontSize: 14, bold: true, color: COLORS.accent, align: 'center', fontFace: FONTS.mono });
694
- if (props.title)
695
- slide.addText(props.title, { x, y: 2.0, w, h: 0.4, fontSize: 18, bold: true, color: COLORS.secondary, align: 'center', fontFace: FONTS.heading });
696
- const valStr = (props.value || '') + (props.unit ? ' ' + props.unit : '');
697
- if (valStr)
698
- slide.addText(valStr, { x: x + 0.5, y: 2.5, w: w - 1, h: 1.4, fontSize: 60, bold: true, color: COLORS.primary, align: 'center', valign: 'middle', fontFace: FONTS.heading });
699
- let ty = 4.0;
700
- if (props.label)
701
- slide.addText(props.label, { x, y: ty, w, h: 0.35, fontSize: 16, color: COLORS.primary, align: 'center', fontFace: FONTS.body });
702
- ty += 0.4;
703
- if (props.delta)
704
- slide.addText(props.delta, { x, y: ty, w, h: 0.3, fontSize: 14, bold: true, color: COLORS.accent, align: 'center', fontFace: FONTS.mono });
705
- ty += 0.35;
706
- if (props.footnote)
707
- slide.addText(props.footnote, { x, y: ty, w, h: 0.3, fontSize: 12, color: COLORS.secondary, align: 'center', fontFace: FONTS.body });
708
- }
709
- function renderTheme02CycleV1(slide, props) {
710
- const x = 0.8;
711
- const w = 8.4;
712
- if (props.kicker)
713
- addKicker(slide, props.kicker, x);
714
- if (props.title)
715
- addTitle(slide, props.title, x, 1.25, w);
716
- if (props.subtitle)
717
- slide.addText(props.subtitle, { x, y: 2.05, w, h: 0.35, fontSize: 16, color: COLORS.secondary, align: 'left', fontFace: FONTS.body });
718
- const steps = (props.steps || []).slice(0, 6);
719
- if (steps.length === 0)
720
- return;
721
- const n = steps.length;
722
- const cardW = (w - 0.3 * (n - 1)) / n;
723
- const startY = 2.6;
724
- const cardH = 2.0;
725
- steps.forEach((s, i) => {
726
- const cx = x + i * (cardW + 0.3);
727
- addTheme02Card(slide, cx, startY, cardW, cardH, 0.12);
728
- slide.addText(String(i + 1), { x: cx + 0.2, y: startY + 0.16, w: 0.6, h: 0.5, fontSize: 22, bold: true, color: COLORS.accent, fontFace: FONTS.heading });
729
- slide.addText(s.title || '', { x: cx + 0.2, y: startY + 0.7, w: cardW - 0.4, h: 0.4, fontSize: 14, bold: true, color: COLORS.primary, fontFace: FONTS.heading });
730
- if (s.desc)
731
- slide.addText(s.desc || '', { x: cx + 0.2, y: startY + 1.1, w: cardW - 0.4, h: cardH - 1.2, fontSize: 11, color: COLORS.secondary, fontFace: FONTS.body });
732
- });
733
- slide.addText('↻ 闭环循环', { x, y: startY + cardH + 0.2, w, h: 0.3, fontSize: 13, color: COLORS.accent, align: 'center', fontFace: FONTS.mono });
734
- }
735
- function renderTheme02SwimlaneV1(slide, props) {
736
- const x = 0.8;
737
- const w = 8.4;
738
- if (props.kicker)
739
- addKicker(slide, props.kicker, x);
740
- if (props.title)
741
- addTitle(slide, props.title, x, 1.25, w);
742
- if (props.subtitle)
743
- slide.addText(props.subtitle, { x, y: 2.05, w, h: 0.35, fontSize: 16, color: COLORS.secondary, align: 'left', fontFace: FONTS.body });
744
- const phases = (props.phases || []).slice(0, 5);
745
- const lanes = (props.lanes || []).slice(0, 4);
746
- if (phases.length === 0 || lanes.length === 0)
747
- return;
748
- const startY = 2.55;
749
- const nameW = 1.4;
750
- const gridX = x + nameW;
751
- const gridW = w - nameW;
752
- const colW = gridW / phases.length;
753
- const laneH = Math.min(0.9, (5.0 - startY) / lanes.length);
754
- phases.forEach((p, i) => slide.addText(p, { x: gridX + i * colW, y: startY, w: colW, h: 0.35, fontSize: 12, bold: true, color: COLORS.accent, align: 'center', fontFace: FONTS.heading }));
755
- let ly = startY + 0.4;
756
- lanes.forEach((lane) => {
757
- slide.addText(lane.name || '', { x, y: ly, w: nameW, h: laneH, fontSize: 12, bold: true, color: COLORS.primary, align: 'left', valign: 'middle', fontFace: FONTS.heading });
758
- (lane.items || []).slice(0, phases.length).forEach((it, i) => {
759
- slide.addText(it || '', { x: gridX + i * colW + 0.1, y: ly, w: colW - 0.2, h: laneH, fontSize: 11, color: COLORS.secondary, align: 'center', valign: 'middle', fontFace: FONTS.body });
760
- });
761
- ly += laneH;
762
- });
763
- }
764
- function renderTheme02PyramidV1(slide, props) {
765
- const x = 0.8;
766
- const w = 8.4;
767
- if (props.kicker)
768
- addKicker(slide, props.kicker, x);
769
- if (props.title)
770
- addTitle(slide, props.title, x, 1.25, w);
771
- if (props.subtitle)
772
- slide.addText(props.subtitle, { x, y: 2.05, w, h: 0.35, fontSize: 16, color: COLORS.secondary, align: 'left', fontFace: FONTS.body });
773
- const levels = (props.levels || []).slice(0, 5);
774
- if (levels.length === 0)
775
- return;
776
- const startY = 2.6;
777
- const totalH = 2.4;
778
- const bandH = totalH / levels.length;
779
- const denom = Math.max(levels.length - 1, 1);
780
- levels.forEach((lv, i) => {
781
- const topW = w * (0.5 + 0.5 * (i / denom));
782
- const cx = x + (w - topW) / 2;
783
- const cy = startY + i * bandH;
784
- slide.addShape('rect', { x: cx, y: cy, w: topW, h: bandH * 0.92, fill: { color: T2_CHART[i % T2_CHART.length] }, line: { color: COLORS.border, width: 1 } });
785
- slide.addText(lv.title || '', { x: cx, y: cy, w: topW, h: bandH * 0.92, fontSize: 14, bold: true, color: COLORS.white, align: 'center', valign: 'middle', fontFace: FONTS.heading });
786
- });
787
- }
788
- function renderTheme02OrgChartV1(slide, props) {
789
- const x = 0.8;
790
- const w = 8.4;
791
- if (props.kicker)
792
- addKicker(slide, props.kicker, x);
793
- if (props.title)
794
- addTitle(slide, props.title, x, 1.25, w);
795
- if (props.subtitle)
796
- slide.addText(props.subtitle, { x, y: 2.05, w, h: 0.35, fontSize: 16, color: COLORS.secondary, align: 'left', fontFace: FONTS.body });
797
- const root = props.root;
798
- const children = (props.children || []).slice(0, 5);
799
- if (!root)
800
- return;
801
- const cx = x + w / 2;
802
- const rootW = 3.0;
803
- const rootH = 1.0;
804
- const rootY = 2.5;
805
- addTheme02Card(slide, cx - rootW / 2, rootY, rootW, rootH, 0.12);
806
- slide.addText(root.title || '', { x: cx - rootW / 2, y: rootY + 0.12, w: rootW, h: 0.4, fontSize: 16, bold: true, color: COLORS.primary, align: 'center', fontFace: FONTS.heading });
807
- if (root.sub)
808
- slide.addText(root.sub, { x: cx - rootW / 2, y: rootY + 0.52, w: rootW, h: 0.35, fontSize: 11, color: COLORS.secondary, align: 'center', fontFace: FONTS.body });
809
- if (children.length) {
810
- const childY = rootY + rootH + 0.6;
811
- const childW = (w - 0.3 * (children.length - 1)) / children.length;
812
- const childH = 1.2;
813
- children.forEach((c, i) => {
814
- const ccx = x + i * (childW + 0.3);
815
- addTheme02Card(slide, ccx, childY, childW, childH, 0.12);
816
- slide.addText(c.title || '', { x: ccx, y: childY + 0.12, w: childW, h: 0.4, fontSize: 14, bold: true, color: COLORS.primary, align: 'center', fontFace: FONTS.heading });
817
- if (c.sub)
818
- slide.addText(c.sub, { x: ccx, y: childY + 0.52, w: childW, h: 0.5, fontSize: 11, color: COLORS.secondary, align: 'center', valign: 'top', fontFace: FONTS.body });
819
- });
820
- }
821
- }
822
- function renderTheme02FlowV1(slide, props) {
823
- const x = 0.8;
824
- const w = 8.4;
825
- if (props.kicker)
826
- addKicker(slide, props.kicker, x);
827
- if (props.title)
828
- addTitle(slide, props.title, x, 1.25, w);
829
- if (props.subtitle)
830
- slide.addText(props.subtitle, { x, y: 2.05, w, h: 0.35, fontSize: 16, color: COLORS.secondary, align: 'left', fontFace: FONTS.body });
831
- const steps = (props.steps || []).slice(0, 5);
832
- if (steps.length === 0)
833
- return;
834
- const arrowW = 0.4;
835
- const n = steps.length;
836
- const cardW = (w - arrowW * (n - 1)) / n;
837
- const startY = 2.7;
838
- const cardH = 1.9;
839
- steps.forEach((s, i) => {
840
- const cx = x + i * (cardW + arrowW);
841
- addTheme02Card(slide, cx, startY, cardW, cardH, 0.12);
842
- slide.addText(String(i + 1), { x: cx + 0.2, y: startY + 0.16, w: 0.6, h: 0.5, fontSize: 22, bold: true, color: COLORS.accent, fontFace: FONTS.heading });
843
- slide.addText(s.title || '', { x: cx + 0.2, y: startY + 0.7, w: cardW - 0.4, h: 0.4, fontSize: 14, bold: true, color: COLORS.primary, fontFace: FONTS.heading });
844
- if (s.desc)
845
- slide.addText(s.desc || '', { x: cx + 0.2, y: startY + 1.1, w: cardW - 0.4, h: cardH - 1.2, fontSize: 11, color: COLORS.secondary, fontFace: FONTS.body });
846
- if (i < n - 1)
847
- slide.addText('→', { x: cx + cardW, y: startY, w: arrowW, h: cardH, fontSize: 24, bold: true, color: COLORS.accent, align: 'center', valign: 'middle', fontFace: FONTS.body });
848
- });
849
- }
850
- function renderTheme02TableV2(slide, props) {
851
- const x = 0.8;
852
- const w = 8.4;
853
- if (props.kicker)
854
- addKicker(slide, props.kicker, x);
855
- if (props.title)
856
- addTitle(slide, props.title, x, 1.25, w);
857
- if (props.subtitle)
858
- slide.addText(props.subtitle, { x, y: 2.05, w, h: 0.35, fontSize: 16, color: COLORS.secondary, align: 'left', fontFace: FONTS.body });
859
- const columns = (props.columns || []).slice(0, 5);
860
- const rows = (props.rows || []).slice(0, 8);
861
- if (columns.length === 0)
862
- return;
863
- const startY = 2.55;
864
- const colW = w / columns.length;
865
- const headerH = 0.4;
866
- const rowH = Math.min(0.42, (5.0 - startY - headerH) / Math.max(rows.length, 1));
867
- columns.forEach((c, i) => slide.addText(c, { x: x + i * colW, y: startY, w: colW, h: headerH, fontSize: 13, bold: true, color: COLORS.accent, align: 'left', valign: 'middle', fontFace: FONTS.heading }));
868
- slide.addShape('rect', { x, y: startY + headerH, w, h: 0.02, fill: { color: COLORS.border } });
869
- let ry = startY + headerH;
870
- rows.forEach((row, r) => {
871
- if (r % 2 === 1)
872
- slide.addShape('rect', { x, y: ry, w, h: rowH, fill: { color: COLORS.surfaceElevated } });
873
- columns.forEach((_, i) => slide.addText((row && row[i]) || '', { x: x + i * colW + 0.05, y: ry, w: colW - 0.1, h: rowH, fontSize: 11, color: COLORS.primary, align: 'left', valign: 'middle', fontFace: FONTS.body }));
874
- ry += rowH;
875
- });
876
- }
877
- function renderTheme02ImageSplitV1(slide, props) {
878
- const x = 0.8;
879
- const w = 8.4;
880
- const textW = 4.0;
881
- const imgX = x + textW + 0.4;
882
- const imgW = w - textW - 0.4;
883
- if (props.kicker)
884
- addKicker(slide, props.kicker, x);
885
- if (props.title)
886
- addTitle(slide, props.title, x, 1.25, textW);
887
- if (props.subtitle)
888
- slide.addText(props.subtitle, { x, y: 2.05, w: textW, h: 0.6, fontSize: 16, color: COLORS.secondary, fontFace: FONTS.body });
889
- const bullets = (props.bullets || []).slice(0, 5);
890
- let by = 2.8;
891
- bullets.forEach((b) => { slide.addText('✓ ' + b, { x, y: by, w: textW, h: 0.45, fontSize: 14, color: COLORS.primary, fontFace: FONTS.body }); by += 0.5; });
892
- if (props.image)
893
- addImageMaybe(slide, props.image, imgX, 2.0, imgW, 3.2);
894
- else {
895
- slide.addShape('rect', { x: imgX, y: 2.0, w: imgW, h: 3.2, fill: { color: COLORS.surfaceElevated }, line: { color: COLORS.border, width: 1 } });
896
- slide.addText('图片占位', { x: imgX, y: 2.0, w: imgW, h: 3.2, fontSize: 14, color: COLORS.secondary, align: 'center', valign: 'middle', fontFace: FONTS.body });
897
- }
898
- }
899
- function renderTheme02ImageGridV2(slide, props) {
900
- const x = 0.8;
901
- const w = 8.4;
902
- if (props.kicker)
903
- addKicker(slide, props.kicker, x);
904
- if (props.title)
905
- addTitle(slide, props.title, x, 1.25, w);
906
- if (props.subtitle)
907
- slide.addText(props.subtitle, { x, y: 2.05, w, h: 0.3, fontSize: 16, color: COLORS.secondary, fontFace: FONTS.body });
908
- const imgs = (props.images || []).slice(0, 4);
909
- const caps = (props.captions || []).slice(0, 4);
910
- const tileW = (w - 0.3) / 2;
911
- const tileH = 1.4;
912
- const startY = 2.5;
913
- imgs.forEach((im, i) => {
914
- const tx = x + (i % 2) * (tileW + 0.3);
915
- const ty = startY + Math.floor(i / 2) * (tileH + 0.3);
916
- if (im)
917
- addImageMaybe(slide, im, tx, ty, tileW, tileH);
918
- else {
919
- slide.addShape('rect', { x: tx, y: ty, w: tileW, h: tileH, fill: { color: COLORS.surfaceElevated }, line: { color: COLORS.border, width: 1 } });
920
- slide.addText('图片占位', { x: tx, y: ty, w: tileW, h: tileH, fontSize: 12, color: COLORS.secondary, align: 'center', valign: 'middle', fontFace: FONTS.body });
921
- }
922
- if (caps[i])
923
- slide.addText(caps[i], { x: tx, y: ty + tileH + 0.02, w: tileW, h: 0.28, fontSize: 11, color: COLORS.secondary, align: 'left', fontFace: FONTS.body });
924
- });
925
- }
926
- function renderTheme02SpotlightV1(slide, props) {
927
- const x = 0.8;
928
- const w = 8.4;
929
- if (props.image)
930
- addImageMaybe(slide, props.image, 0, 0, 10, 5.625);
931
- else
932
- slide.addShape('rect', { x: 0, y: 0, w: 10, h: 5.625, fill: { color: COLORS.surfaceElevated } });
933
- slide.addShape('rect', { x: 0, y: 0, w: 10, h: 5.625, fill: { color: '080A0E', transparency: 45 } });
934
- if (props.kicker)
935
- slide.addText(props.kicker, { x, y: 1.5, w, h: 0.4, fontSize: 14, bold: true, color: COLORS.accent, align: 'center', fontFace: FONTS.mono });
936
- if (props.title)
937
- slide.addText(props.title, { x, y: 2.1, w, h: 1.0, fontSize: 40, bold: true, color: COLORS.white, align: 'center', valign: 'middle', fontFace: FONTS.heading });
938
- if (props.subtitle)
939
- slide.addText(props.subtitle, { x: x + 0.5, y: 3.3, w: w - 1, h: 0.8, fontSize: 18, color: COLORS.white, align: 'center', fontFace: FONTS.body });
940
- if (props.caption)
941
- slide.addText(props.caption, { x, y: 5.0, w, h: 0.3, fontSize: 12, color: COLORS.white, align: 'center', fontFace: FONTS.mono });
942
- }
943
- function renderTheme02ChapterV3(slide, props) {
944
- const x = 0.8;
945
- const w = 8.4;
946
- if (props.number)
947
- slide.addText(props.number, { x, y: 1.6, w, h: 1.6, fontSize: 96, bold: true, color: COLORS.accent, align: 'center', valign: 'middle', fontFace: FONTS.heading });
948
- if (props.kicker)
949
- slide.addText(props.kicker, { x, y: 3.3, w, h: 0.4, fontSize: 14, bold: true, color: COLORS.secondary, align: 'center', fontFace: FONTS.mono });
950
- if (props.title)
951
- slide.addText(props.title, { x, y: 3.75, w, h: 0.6, fontSize: 32, bold: true, color: COLORS.primary, align: 'center', fontFace: FONTS.heading });
952
- slide.addShape('rect', { x: x + w / 2 - 1.0, y: 4.5, w: 2.0, h: 0.02, fill: { color: COLORS.accent } });
953
- if (props.subtitle)
954
- slide.addText(props.subtitle, { x, y: 4.65, w, h: 0.4, fontSize: 15, color: COLORS.secondary, align: 'center', fontFace: FONTS.body });
955
- }
956
- function renderTheme02SectionDividerV1(slide, props) {
957
- const x = 0.8;
958
- const w = 8.4;
959
- if (props.index)
960
- slide.addText(props.index, { x, y: 1.9, w, h: 0.4, fontSize: 14, bold: true, color: COLORS.accent, align: 'center', fontFace: FONTS.mono });
961
- if (props.kicker)
962
- slide.addText(props.kicker, { x, y: 2.35, w, h: 0.4, fontSize: 13, bold: true, color: COLORS.secondary, align: 'center', fontFace: FONTS.mono });
963
- if (props.title)
964
- slide.addText(props.title, { x, y: 2.8, w, h: 0.8, fontSize: 36, bold: true, color: COLORS.primary, align: 'center', fontFace: FONTS.heading });
965
- slide.addShape('rect', { x: x + w / 2 - 1.2, y: 3.7, w: 2.4, h: 0.02, fill: { color: COLORS.accent } });
966
- if (props.subtitle)
967
- slide.addText(props.subtitle, { x, y: 3.9, w, h: 0.5, fontSize: 16, color: COLORS.secondary, align: 'center', fontFace: FONTS.body });
968
- }
969
- function renderTheme02LogoWallV1(slide, props) {
970
- const x = 0.8;
971
- const w = 8.4;
972
- if (props.kicker)
973
- addKicker(slide, props.kicker, x);
974
- if (props.title)
975
- addTitle(slide, props.title, x, 1.25, w);
976
- if (props.subtitle)
977
- slide.addText(props.subtitle, { x, y: 2.05, w, h: 0.3, fontSize: 16, color: COLORS.secondary, fontFace: FONTS.body });
978
- const logos = (props.logos || []).slice(0, 8);
979
- if (logos.length === 0)
980
- return;
981
- const cols = 4;
982
- const rows = Math.ceil(logos.length / cols);
983
- const cardW = (w - 0.3 * (cols - 1)) / cols;
984
- const cardH = Math.min(1.1, (5.0 - 2.5) / rows - 0.2);
985
- logos.forEach((lg, i) => {
986
- const cx = x + (i % cols) * (cardW + 0.3);
987
- const cy = 2.55 + Math.floor(i / cols) * (cardH + 0.25);
988
- addTheme02Card(slide, cx, cy, cardW, cardH, 0.12);
989
- slide.addText(lg.name || '', { x: cx + 0.15, y: cy + 0.18, w: cardW - 0.3, h: 0.4, fontSize: 15, bold: true, color: COLORS.primary, align: 'center', fontFace: FONTS.heading });
990
- if (lg.sub)
991
- slide.addText(lg.sub, { x: cx + 0.15, y: cy + 0.58, w: cardW - 0.3, h: 0.3, fontSize: 11, color: COLORS.secondary, align: 'center', fontFace: FONTS.body });
992
- });
993
- }
994
- export function registerTheme02Renderers(registerPptxLayoutRenderer) {
995
- registerPptxLayoutRenderer('theme02_cover_v1', renderCoverV1);
996
- registerPptxLayoutRenderer('theme02_cover_v2', renderCoverV1);
997
- registerPptxLayoutRenderer('theme02_chapter_v1', renderTheme02ChapterV1);
998
- registerPptxLayoutRenderer('theme02_chapter_v2', renderTheme02ChapterV2);
999
- registerPptxLayoutRenderer('theme02_metric_big', renderTheme01MetricBig);
1000
- registerPptxLayoutRenderer('theme02_number_showcase_v1', renderTheme02NumberShowcaseV1);
1001
- registerPptxLayoutRenderer('theme02_chart_v1', renderTheme02ChartV1);
1002
- registerPptxLayoutRenderer('theme02_chart_funnel', renderChartFunnel);
1003
- registerPptxLayoutRenderer('theme02_chart_donut', renderTheme01ChartDonut);
1004
- registerPptxLayoutRenderer('theme02_chart_heatmap', renderChartHeatmap);
1005
- registerPptxLayoutRenderer('theme02_chart_radar', renderChartRadar);
1006
- registerPptxLayoutRenderer('theme02_chart_gauge', renderChartGauge);
1007
- registerPptxLayoutRenderer('theme02_content_v1', renderTheme02ContentV1);
1008
- registerPptxLayoutRenderer('theme02_delta_v1', renderTheme02DeltaV1);
1009
- registerPptxLayoutRenderer('theme02_closing_v1', renderClosing);
1010
- registerPptxLayoutRenderer('theme02_table_of_contents_v1', renderTheme02TableOfContentsV1);
1011
- registerPptxLayoutRenderer('theme02_metrics_v1', renderStatsV1);
1012
- registerPptxLayoutRenderer('theme02_team_v1', renderTeamV1);
1013
- registerPptxLayoutRenderer('theme02_timeline_v1', renderTimelineV1);
1014
- registerPptxLayoutRenderer('theme02_comparison_v1', renderComparisonV1);
1015
- registerPptxLayoutRenderer('theme02_image_v1', renderTheme02ImageV1);
1016
- registerPptxLayoutRenderer('theme02_quote_v1', renderTheme02QuoteV1);
1017
- registerPptxLayoutRenderer('theme02_quote_v2', renderTheme02QuoteV2);
1018
- registerPptxLayoutRenderer('theme02_bento_v1', renderTheme02BentoV1);
1019
- registerPptxLayoutRenderer('theme02_feature_v1', renderFeatureV1);
1020
- registerPptxLayoutRenderer('theme02_gallery_v1', renderTheme01GalleryV1);
1021
- registerPptxLayoutRenderer('theme02_pricing_v1', renderPricingV1);
1022
- registerPptxLayoutRenderer('theme02_process_v1', renderTheme02ProcessV1);
1023
- registerPptxLayoutRenderer('theme02_progress_v1', renderTheme02ProgressV1);
1024
- registerPptxLayoutRenderer('theme02_roadmap_v1', renderTheme01RoadmapV1);
1025
- registerPptxLayoutRenderer('theme02_swot_v1', renderSwotV1);
1026
- registerPptxLayoutRenderer('theme02_faq_v1', renderFaqV1);
1027
- registerPptxLayoutRenderer('theme02_filmstrip_v1', renderTheme01FilmstripV1);
1028
- registerPptxLayoutRenderer('theme02_partners_v1', renderPartnersV1);
1029
- registerPptxLayoutRenderer('theme02_pest_v1', renderPestV1);
1030
- registerPptxLayoutRenderer('theme02_stats_v1', renderStatsV1);
1031
- registerPptxLayoutRenderer('theme02_table_v1', renderTheme01TableV1);
1032
- registerPptxLayoutRenderer('theme02_tags_v1', renderTheme01TagsV1);
1033
- registerPptxLayoutRenderer('theme02_testimonial_v1', renderTestimonialV1);
1034
- registerPptxLayoutRenderer('theme02_feature_v2', renderTheme02FeatureV2);
1035
- registerPptxLayoutRenderer('theme02_checklist_v1', renderTheme02ChecklistV1);
1036
- registerPptxLayoutRenderer('theme02_steps_v1', renderTheme02StepsV1);
1037
- registerPptxLayoutRenderer('theme02_card_grid_v1', renderTheme02CardGridV1);
1038
- registerPptxLayoutRenderer('theme02_highlight_v1', renderTheme02HighlightV1);
1039
- registerPptxLayoutRenderer('theme02_comparison_v2', renderTheme02ComparisonV2);
1040
- registerPptxLayoutRenderer('theme02_matrix_v1', renderTheme02MatrixV1);
1041
- registerPptxLayoutRenderer('theme02_stat_grid_v1', renderTheme02StatGridV1);
1042
- registerPptxLayoutRenderer('theme02_cover_v3', renderTheme02CoverV3);
1043
- registerPptxLayoutRenderer('theme02_closing_v2', renderTheme02ClosingV2);
1044
- registerPptxLayoutRenderer('theme02_chart_bar_v1', renderTheme02ChartBarV1);
1045
- registerPptxLayoutRenderer('theme02_chart_line_v1', renderTheme02ChartLineV1);
1046
- registerPptxLayoutRenderer('theme02_chart_area_v1', renderTheme02ChartAreaV1);
1047
- registerPptxLayoutRenderer('theme02_chart_stack_v1', renderTheme02ChartStackV1);
1048
- registerPptxLayoutRenderer('theme02_kpi_strip_v1', renderTheme02KpiStripV1);
1049
- registerPptxLayoutRenderer('theme02_big_stat_v1', renderTheme02BigStatV1);
1050
- registerPptxLayoutRenderer('theme02_cycle_v1', renderTheme02CycleV1);
1051
- registerPptxLayoutRenderer('theme02_swimlane_v1', renderTheme02SwimlaneV1);
1052
- registerPptxLayoutRenderer('theme02_pyramid_v1', renderTheme02PyramidV1);
1053
- registerPptxLayoutRenderer('theme02_org_chart_v1', renderTheme02OrgChartV1);
1054
- registerPptxLayoutRenderer('theme02_flow_v1', renderTheme02FlowV1);
1055
- registerPptxLayoutRenderer('theme02_table_v2', renderTheme02TableV2);
1056
- registerPptxLayoutRenderer('theme02_image_split_v1', renderTheme02ImageSplitV1);
1057
- registerPptxLayoutRenderer('theme02_image_grid_v2', renderTheme02ImageGridV2);
1058
- registerPptxLayoutRenderer('theme02_spotlight_v1', renderTheme02SpotlightV1);
1059
- registerPptxLayoutRenderer('theme02_chapter_v3', renderTheme02ChapterV3);
1060
- registerPptxLayoutRenderer('theme02_section_divider_v1', renderTheme02SectionDividerV1);
1061
- registerPptxLayoutRenderer('theme02_logo_wall_v1', renderTheme02LogoWallV1);
1062
- }
1063
- //# sourceMappingURL=theme02-pptx.js.map