bit-ppt-generator 0.3.0

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.
@@ -0,0 +1,480 @@
1
+ #!/usr/bin/env node
2
+ import fs from "node:fs";
3
+ import os from "node:os";
4
+ import path from "node:path";
5
+ import {
6
+ ROOT,
7
+ checkDeckFile,
8
+ defaultFont,
9
+ generateDeckFile,
10
+ listLayouts,
11
+ } from "../src/generate.mjs";
12
+ import {
13
+ getAllGuides,
14
+ getGuideOverview,
15
+ getGuideWorkflow,
16
+ getImagePlaceholderGuide,
17
+ getLayoutExample,
18
+ getLayoutGuide,
19
+ getLayoutSchema,
20
+ getSpeakerNotesGuide,
21
+ getWritingRules,
22
+ listGuideLayouts,
23
+ } from "../src/layout-guides.mjs";
24
+
25
+ function printHelp() {
26
+ console.log(`bit-ppt
27
+
28
+ Usage:
29
+ bit-ppt generate <input.yaml> <output.pptx> [--json] [--strict] [font options]
30
+ bit-ppt check <input.yaml> [--json] [--strict]
31
+ bit-ppt list-layouts [--json]
32
+ bit-ppt guide [topic] [name] [--json]
33
+ bit-ppt doctor [--json]
34
+
35
+ Progressive guide:
36
+ bit-ppt guide
37
+ bit-ppt guide all --json
38
+ bit-ppt guide workflow --json
39
+ bit-ppt guide layouts
40
+ bit-ppt guide layout imageText
41
+ bit-ppt guide schema imageText --json
42
+ bit-ppt guide example imageText
43
+ bit-ppt guide speaker-notes
44
+ bit-ppt guide image-placeholder
45
+ bit-ppt guide writing-rules
46
+
47
+ Quality options:
48
+ --json Print machine-readable JSON where supported
49
+ --strict Treat validation warnings as failures
50
+
51
+ Font options:
52
+ --font-cn <name> Chinese/CJK font, default: ${defaultFont.cn}
53
+ --font-cn-light <name> Light Chinese/CJK font, default: ${defaultFont.cnLight}
54
+ --font-en <name> Latin font, default: ${defaultFont.en}
55
+ --font-serif <name> Serif font, default: ${defaultFont.serif}
56
+ --font-code <name> Code font, default: ${defaultFont.code}
57
+ `);
58
+ }
59
+
60
+ function printJson(value) {
61
+ console.log(JSON.stringify(value, null, 2));
62
+ }
63
+
64
+ function printGuideOverview(overview) {
65
+ console.log(`${overview.name}
66
+
67
+ ${overview.purpose}
68
+
69
+ Workflow:
70
+ ${overview.workflow.map((item, idx) => ` ${idx + 1}. ${item}`).join("\n")}
71
+
72
+ Guide commands:
73
+ ${overview.commands.map((item) => ` ${item}`).join("\n")}
74
+
75
+ Guided layouts:
76
+ ${overview.guidedLayouts.join(", ")}
77
+ `);
78
+ }
79
+
80
+ function printLayoutGuide(guide) {
81
+ console.log(`${guide.layout}
82
+
83
+ Purpose:
84
+ ${guide.purpose}
85
+
86
+ Use when:
87
+ ${guide.whenToUse}
88
+
89
+ Fields:
90
+ ${Object.entries(guide.fields).map(([name, spec]) => {
91
+ const required = spec.required ? "required" : "optional";
92
+ const value = spec.value ? ` = ${spec.value}` : "";
93
+ return ` ${name}: ${spec.type}${value} (${required})`;
94
+ }).join("\n")}
95
+
96
+ Limits:
97
+ ${Object.entries(guide.limits || {}).map(([name, spec]) => ` ${name}: ${Object.entries(spec).map(([key, value]) => `${key}=${value}`).join(", ")}`).join("\n") || " none"}
98
+
99
+ Notes:
100
+ ${guide.notes.map((item) => ` - ${item}`).join("\n")}
101
+ `);
102
+ }
103
+
104
+ function printExample(example) {
105
+ console.log(JSON.stringify(example, null, 2));
106
+ }
107
+
108
+ function printWritingRules(rules) {
109
+ console.log(`Writing rules:
110
+ ${rules.map((item) => ` - ${item}`).join("\n")}
111
+ `);
112
+ }
113
+
114
+ function printSpeakerNotesGuide(guide) {
115
+ console.log(`Speaker notes
116
+
117
+ Purpose:
118
+ ${guide.purpose}
119
+
120
+ Fields:
121
+ ${Object.entries(guide.fields).map(([name, spec]) => {
122
+ const required = spec.required ? "required" : "optional";
123
+ return ` ${name}: ${spec.type} (${required})`;
124
+ }).join("\n")}
125
+
126
+ Aliases:
127
+ ${guide.aliases.join(", ")}
128
+
129
+ Limits:
130
+ ${Object.entries(guide.limits || {}).map(([name, spec]) => ` ${name}: ${Object.entries(spec).map(([key, value]) => `${key}=${value}`).join(", ")}`).join("\n")}
131
+
132
+ Notes:
133
+ ${guide.notes.map((item) => ` - ${item}`).join("\n")}
134
+
135
+ Example:
136
+ ${JSON.stringify(guide.example, null, 2)}
137
+ `);
138
+ }
139
+
140
+ function printImagePlaceholderGuide(guide) {
141
+ console.log(`Image placeholder
142
+
143
+ Purpose:
144
+ ${guide.purpose}
145
+
146
+ Use when:
147
+ ${guide.whenToUse}
148
+
149
+ Fields:
150
+ ${Object.entries(guide.fields).map(([name, spec]) => {
151
+ const required = spec.required ? "required" : "optional";
152
+ return ` ${name}: ${spec.type} (${required})`;
153
+ }).join("\n")}
154
+
155
+ Limits:
156
+ ${Object.entries(guide.limits || {}).map(([name, spec]) => ` ${name}: ${Object.entries(spec).map(([key, value]) => `${key}=${value}`).join(", ")}`).join("\n")}
157
+
158
+ Notes:
159
+ ${guide.notes.map((item) => ` - ${item}`).join("\n")}
160
+
161
+ Example:
162
+ ${JSON.stringify(guide.example, null, 2)}
163
+ `);
164
+ }
165
+
166
+ function printAvailableGuideLayouts() {
167
+ console.log(listGuideLayouts().join("\n"));
168
+ }
169
+
170
+ function printWorkflow(workflow) {
171
+ console.log(`Workflow:
172
+ ${workflow.map((item, idx) => ` ${idx + 1}. ${item}`).join("\n")}
173
+ `);
174
+ }
175
+
176
+ function printGuideHelp() {
177
+ console.log(`bit-ppt guide
178
+
179
+ Usage:
180
+ bit-ppt guide
181
+ bit-ppt guide all --json
182
+ bit-ppt guide workflow [--json]
183
+ bit-ppt guide layouts
184
+ bit-ppt guide layout <name> [--json]
185
+ bit-ppt guide schema <name> --json
186
+ bit-ppt guide example <name> [--json]
187
+ bit-ppt guide speaker-notes [--json]
188
+ bit-ppt guide image-placeholder [--json]
189
+ bit-ppt guide writing-rules [--json]
190
+ `);
191
+ }
192
+
193
+ function doctorCheck(name, ok, message, details = {}) {
194
+ return { name, ok, message, ...details };
195
+ }
196
+
197
+ function canImportPackage(packageName) {
198
+ try {
199
+ import.meta.resolve(packageName);
200
+ return true;
201
+ } catch {
202
+ return false;
203
+ }
204
+ }
205
+
206
+ function runDoctor() {
207
+ const checks = [];
208
+ const version = process.versions.node;
209
+ const major = Number(version.split(".")[0]);
210
+ checks.push(doctorCheck("node", major >= 18, `Node.js ${version}`, { version }));
211
+
212
+ ["pptxgenjs", "yaml", "jszip", "latex-to-omml", "@modelcontextprotocol/sdk", "zod"].forEach((packageName) => {
213
+ const available = canImportPackage(packageName);
214
+ checks.push(doctorCheck(`dependency:${packageName}`, available, available ? `${packageName} is available` : `${packageName} is missing`));
215
+ });
216
+
217
+ ["assets/bit-campus-photo.png", "assets/bit-campus-line.png", "content/example.yaml", "content/body-layout-test.yaml", "content/chart-flow-test.yaml"].forEach((relativePath) => {
218
+ const fileName = path.join(ROOT, relativePath);
219
+ checks.push(doctorCheck(`file:${relativePath}`, fs.existsSync(fileName), fs.existsSync(fileName) ? "found" : "missing", { path: fileName }));
220
+ });
221
+
222
+ [
223
+ ["fixture:example", "content/example.yaml"],
224
+ ["fixture:body-layouts", "content/body-layout-test.yaml"],
225
+ ["fixture:charts", "content/chart-flow-test.yaml"],
226
+ ].forEach(([name, relativePath]) => {
227
+ try {
228
+ const result = checkDeckFile(path.join(ROOT, relativePath));
229
+ checks.push(doctorCheck(name, result.validation.errors.length === 0, result.validation.errors.length ? "validation errors found" : "check passed", {
230
+ warnings: result.validation.warnings.length,
231
+ errors: result.validation.errors.length,
232
+ }));
233
+ } catch (error) {
234
+ checks.push(doctorCheck(name, false, error.message || String(error)));
235
+ }
236
+ });
237
+
238
+ try {
239
+ const outputDir = path.join(ROOT, "output");
240
+ fs.mkdirSync(outputDir, { recursive: true });
241
+ const probe = path.join(outputDir, `.bit-ppt-doctor-${process.pid}.tmp`);
242
+ fs.writeFileSync(probe, "ok");
243
+ fs.unlinkSync(probe);
244
+ checks.push(doctorCheck("output:writable", true, "output directory is writable", { path: outputDir }));
245
+ } catch (error) {
246
+ checks.push(doctorCheck("output:writable", false, error.message || String(error)));
247
+ }
248
+
249
+ const ok = checks.every((item) => item.ok);
250
+ return {
251
+ ok,
252
+ platform: process.platform,
253
+ arch: process.arch,
254
+ cwd: process.cwd(),
255
+ root: ROOT,
256
+ checks,
257
+ };
258
+ }
259
+
260
+ function printDoctor(result) {
261
+ console.log(`BIT PPT doctor: ${result.ok ? "ok" : "failed"}
262
+
263
+ Root:
264
+ ${result.root}
265
+
266
+ Checks:
267
+ ${result.checks.map((item) => ` ${item.ok ? "OK" : "FAIL"} ${item.name}: ${item.message}`).join("\n")}
268
+ `);
269
+ }
270
+
271
+ function parseOptions(args) {
272
+ const options = {};
273
+ const positional = [];
274
+ for (let idx = 0; idx < args.length; idx += 1) {
275
+ const arg = args[idx];
276
+ if (arg === "--json") options.json = true;
277
+ else if (arg === "--strict") options.strict = true;
278
+ else if (arg === "--font-cn" || arg === "--font-cjk") options.fontCn = args[++idx];
279
+ else if (arg === "--font-cn-light") options.fontCnLight = args[++idx];
280
+ else if (arg === "--font-en" || arg === "--font-latin") options.fontEn = args[++idx];
281
+ else if (arg === "--font-serif") options.fontSerif = args[++idx];
282
+ else if (arg === "--font-code") options.fontCode = args[++idx];
283
+ else if (arg === "-h" || arg === "--help") options.help = true;
284
+ else positional.push(arg);
285
+ }
286
+ return { options, positional };
287
+ }
288
+
289
+ function printCheck(result, asJson) {
290
+ if (asJson) {
291
+ console.log(JSON.stringify(result, null, 2));
292
+ return;
293
+ }
294
+ const errors = result.validation.errors.length;
295
+ const warnings = result.validation.warnings.length;
296
+ console.log(`slides: ${result.inputSlides} -> ${result.outputSlides}`);
297
+ console.log(`errors: ${errors}`);
298
+ console.log(`warnings: ${warnings}`);
299
+ if (result.actions.length) {
300
+ console.log(`actions: ${result.actions.map((item) => `${item.title}->${item.parts}`).join(", ")}`);
301
+ }
302
+ if (result.repairPrompt) {
303
+ console.log("\nrepair prompt:");
304
+ console.log(result.repairPrompt);
305
+ }
306
+ }
307
+
308
+ function hasStrictFailure(result, options) {
309
+ return Boolean(options.strict && result.validation.warnings.length);
310
+ }
311
+
312
+ function applyCheckExitCode(result, options) {
313
+ if (result.validation.errors.length || hasStrictFailure(result, options)) {
314
+ process.exitCode = 1;
315
+ }
316
+ }
317
+
318
+ function makeStrictWarningError(result) {
319
+ const error = new Error("Deck validation warning(s) failed strict mode.");
320
+ error.validation = result.validation;
321
+ error.repairPrompt = result.repairPrompt;
322
+ return error;
323
+ }
324
+
325
+ function printGuide(command, name, asJson) {
326
+ if (!command) {
327
+ const overview = getGuideOverview();
328
+ if (asJson) printJson(overview);
329
+ else printGuideOverview(overview);
330
+ return;
331
+ }
332
+
333
+ if (command === "help" || command === "-h" || command === "--help") {
334
+ printGuideHelp();
335
+ return;
336
+ }
337
+
338
+ if (command === "all") {
339
+ const guides = getAllGuides();
340
+ if (asJson) printJson(guides);
341
+ else printGuideOverview(guides.overview);
342
+ return;
343
+ }
344
+
345
+ if (command === "workflow") {
346
+ const workflow = getGuideWorkflow();
347
+ if (asJson) printJson(workflow);
348
+ else printWorkflow(workflow);
349
+ return;
350
+ }
351
+
352
+ if (command === "speaker-notes") {
353
+ const guide = getSpeakerNotesGuide();
354
+ if (asJson) printJson(guide);
355
+ else printSpeakerNotesGuide(guide);
356
+ return;
357
+ }
358
+
359
+ if (command === "image-placeholder") {
360
+ const guide = getImagePlaceholderGuide();
361
+ if (asJson) printJson(guide);
362
+ else printImagePlaceholderGuide(guide);
363
+ return;
364
+ }
365
+
366
+ if (command === "layouts") {
367
+ const layouts = listGuideLayouts();
368
+ if (asJson) printJson(layouts);
369
+ else printAvailableGuideLayouts();
370
+ return;
371
+ }
372
+
373
+ if (command === "layout") {
374
+ if (!name) throw new Error("Usage: bit-ppt guide layout <name>");
375
+ const guide = getLayoutGuide(name);
376
+ if (!guide) throw new Error(`No guide available for layout: ${name}`);
377
+ if (asJson) printJson(guide);
378
+ else printLayoutGuide(guide);
379
+ return;
380
+ }
381
+
382
+ if (command === "schema") {
383
+ if (!name) throw new Error("Usage: bit-ppt guide schema <name>");
384
+ const schema = getLayoutSchema(name);
385
+ if (!schema) throw new Error(`No schema available for layout: ${name}`);
386
+ printJson(schema);
387
+ return;
388
+ }
389
+
390
+ if (command === "example") {
391
+ if (!name) throw new Error("Usage: bit-ppt guide example <name>");
392
+ const example = getLayoutExample(name);
393
+ if (!example) throw new Error(`No example available for layout: ${name}`);
394
+ if (asJson) printJson(example);
395
+ else printExample(example);
396
+ return;
397
+ }
398
+
399
+ if (command === "writing-rules") {
400
+ const rules = getWritingRules();
401
+ if (asJson) printJson(rules);
402
+ else printWritingRules(rules);
403
+ return;
404
+ }
405
+
406
+ throw new Error(`Unknown guide topic: ${command}`);
407
+ }
408
+
409
+ async function main() {
410
+ const [command, ...rest] = process.argv.slice(2);
411
+ const { options, positional } = parseOptions(rest);
412
+ if (!command || command === "help" || command === "-h" || command === "--help" || options.help) {
413
+ printHelp();
414
+ return;
415
+ }
416
+
417
+ if (command === "list-layouts") {
418
+ const layouts = listLayouts();
419
+ if (options.json) console.log(JSON.stringify(layouts, null, 2));
420
+ else console.log(layouts.join("\n"));
421
+ return;
422
+ }
423
+
424
+ if (command === "guide") {
425
+ printGuide(positional[0], positional[1], options.json);
426
+ return;
427
+ }
428
+
429
+ if (command === "doctor") {
430
+ const result = runDoctor();
431
+ if (options.json) printJson(result);
432
+ else printDoctor(result);
433
+ if (!result.ok) process.exitCode = 1;
434
+ return;
435
+ }
436
+
437
+ if (command === "check") {
438
+ const input = positional[0];
439
+ if (!input) throw new Error("Missing input YAML path.");
440
+ const result = checkDeckFile(path.resolve(input));
441
+ printCheck(result, options.json);
442
+ applyCheckExitCode(result, options);
443
+ return;
444
+ }
445
+
446
+ if (command === "generate") {
447
+ const [input, output] = positional;
448
+ if (!input || !output) throw new Error("Usage: bit-ppt generate <input.yaml> <output.pptx>");
449
+ if (options.strict) {
450
+ const strictCheck = checkDeckFile(path.resolve(input));
451
+ if (hasStrictFailure(strictCheck, options)) throw makeStrictWarningError(strictCheck);
452
+ }
453
+ const result = await generateDeckFile(path.resolve(input), path.resolve(output), options);
454
+ if (options.json) {
455
+ printJson(result);
456
+ return;
457
+ }
458
+ if (result.validation.warnings.length) {
459
+ console.warn(`Validation warning(s): ${result.validation.warnings.length}`);
460
+ console.warn(result.repairPrompt);
461
+ }
462
+ if (result.preflight.length) {
463
+ console.log(`Preflight adjusted ${result.preflight.length} slide(s): ${result.preflight.map((item) => `${item.title}->${item.parts}`).join(", ")}`);
464
+ }
465
+ console.log(`Generated ${result.output}`);
466
+ console.log(`Fonts: cn=${result.fonts.cn}, en=${result.fonts.en}, serif=${result.fonts.serif}, code=${result.fonts.code}`);
467
+ return;
468
+ }
469
+
470
+ throw new Error(`Unknown command: ${command}`);
471
+ }
472
+
473
+ main().catch((error) => {
474
+ if (error.validation) {
475
+ console.error(JSON.stringify({ error: error.message, validation: error.validation, repairPrompt: error.repairPrompt }, null, 2));
476
+ } else {
477
+ console.error(error.message || error);
478
+ }
479
+ process.exitCode = 1;
480
+ });
@@ -0,0 +1,112 @@
1
+ meta:
2
+ title: 北理工正文页型扩展示例
3
+ subtitle: Architecture / ablation / case / image / code / appendix
4
+ author: BIT PPT Template Generator
5
+ advisor: 北京理工大学
6
+ date: 2026.05
7
+
8
+ slides:
9
+ - layout: title
10
+
11
+ - layout: architecture
12
+ title: 模型驱动的 PPT 生成架构
13
+ layers:
14
+ - title: 输入层
15
+ components: [论文草稿, 实验结果, 图片素材, 用户约束]
16
+ note: 将非结构化材料整理为可验证的内容块。
17
+ - title: 规划层
18
+ components: [章节规划, 页型选择, 字段填充, 修复提示]
19
+ note: AI 只选择 layout 并填写 YAML,不直接操作 PowerPoint。
20
+ - title: 生成层
21
+ components: [溢出检测, 公式 OMML, 表格拆页, PPTX 写入]
22
+ note: 模板侧保证字号、位置、颜色和 $p_{\theta}(x_i)$ 等公式写入。
23
+ - title: 验证层
24
+ components: [结构检查, 版面预检, 人工复核]
25
+ note: 发现异常后回传给模型压缩或改写内容。
26
+
27
+ - layout: ablation
28
+ title: 消融实验页
29
+ baseline: 基线模型使用完整模块组合,目标函数为 $L(\theta)$。
30
+ items:
31
+ - factor: 检索增强
32
+ setting: 移除检索上下文
33
+ delta: "-6.2%"
34
+ conclusion: 长文档问题下降明显
35
+ - factor: 结构化规划
36
+ setting: 直接生成正文页
37
+ delta: "+3.8 overflow"
38
+ conclusion: 页面溢出风险上升
39
+ - factor: 公式写入
40
+ setting: OMML 替换为图片
41
+ delta: 可编辑性下降
42
+ conclusion: 小公式不适合图片
43
+ - factor: 表格拆页
44
+ setting: 关闭 preflight
45
+ delta: 2 页重叠
46
+ conclusion: 长表格必须预检
47
+
48
+ - layout: caseStudy
49
+ title: 案例分析页
50
+ image: assets/bit-campus-photo.png
51
+ caption: 示例图片区域,可替换为实验样本、系统截图或结果图。
52
+ context:
53
+ - 输入是一段包含公式、表格和图片引用的论文草稿。
54
+ - 目标是生成北理工答辩风格的可编辑 PPTX。
55
+ method:
56
+ - 先让模型选择正文类型,再填充短字段。
57
+ - 生成器负责固定版式、OMML 公式和溢出检测。
58
+ result:
59
+ - 输出可在 WPS / PowerPoint 中继续编辑。
60
+ - 版式一致性优于直接让模型写 PPT 代码。
61
+
62
+ - layout: imageGrid
63
+ title: 多图结果展示
64
+ images:
65
+ - path: assets/bit-campus-photo.png
66
+ caption: 输入样本
67
+ - path: assets/bit-campus-photo.png
68
+ caption: 中间特征
69
+ - path: assets/bit-campus-photo.png
70
+ caption: 预测结果
71
+ - path: assets/bit-campus-photo.png
72
+ caption: 误差区域
73
+ - path: assets/bit-campus-photo.png
74
+ caption: 对照方法
75
+ - path: assets/bit-campus-photo.png
76
+ caption: 本文方法
77
+
78
+ - layout: code
79
+ title: 算法伪代码页
80
+ language: Algorithm
81
+ code: |
82
+ Input: draft D, template layouts T
83
+ 1. parse D into claims, evidence and assets
84
+ 2. select layout t in T for each content block
85
+ 3. generate YAML fields with concise text
86
+ 4. run preflight and formula conversion
87
+ 5. repair overflow until deck is valid
88
+ noteTitle: 关键约束
89
+ notes:
90
+ - 模型只产出结构化 YAML。
91
+ - 公式使用 OMML 写入,避免图片变形。
92
+ - 长列表、长表格和参考文献优先自动拆页。
93
+
94
+ - layout: appendix
95
+ title: 附录索引页
96
+ items:
97
+ - key: A1
98
+ title: 数据集细节
99
+ text: 样本来源、筛选规则和统计分布。
100
+ - key: A2
101
+ title: 超参数设置
102
+ text: 学习率、batch size 和训练轮数。
103
+ - key: A3
104
+ title: 更多公式
105
+ text: 包括 $p_{\theta}(x_i)$、$L(\theta)$ 与推导步骤。
106
+ - key: A4
107
+ title: 额外可视化
108
+ text: 多组实验结果图和失败案例。
109
+
110
+ - layout: closing
111
+ title: 谢谢
112
+ subtitle: 新增正文页型测试完成
@@ -0,0 +1,82 @@
1
+ meta:
2
+ title: 图表与流程图能力测试
3
+ subtitle: Native PowerPoint flowchart and chart layouts
4
+ author: BIT PPT Template Generator
5
+ advisor: 北京理工大学
6
+ date: 2026.05
7
+
8
+ slides:
9
+ - layout: title
10
+
11
+ - layout: flowchart
12
+ title: 方法流程图
13
+ nodes:
14
+ - id: input
15
+ text: 输入数据
16
+ note: 文本 / 图像 / 表格
17
+ - id: parse
18
+ text: 结构解析
19
+ note: 提取 claim 与 evidence
20
+ - id: plan
21
+ text: 页型规划
22
+ note: 选择 layout
23
+ - id: render
24
+ text: PPTX 生成
25
+ note: 形状 / 表格 / OMML
26
+ - id: check
27
+ text: 预检修复
28
+ note: 检查溢出
29
+ emphasis: true
30
+ - id: output
31
+ text: 可编辑输出
32
+ note: WPS / PowerPoint
33
+ emphasis: true
34
+ edges:
35
+ - from: input
36
+ to: parse
37
+ - from: parse
38
+ to: plan
39
+ - from: plan
40
+ to: render
41
+ - from: render
42
+ to: check
43
+ - from: check
44
+ to: output
45
+ note: 流程图使用 PowerPoint 原生形状和箭头,可继续编辑。
46
+
47
+ - layout: chart
48
+ title: 柱状图:不同方案指标对比
49
+ type: bar
50
+ categories: [Dataset A, Dataset B, Dataset C, Dataset D]
51
+ valueAxisTitle: Accuracy
52
+ series:
53
+ - name: Baseline
54
+ values: [81.2, 83.5, 84.1, 82.4]
55
+ - name: Ours
56
+ values: [85.6, 87.2, 88.0, 86.5]
57
+ caption: 原生 PowerPoint 柱状图,可在 WPS / PowerPoint 中编辑数据。
58
+
59
+ - layout: chart
60
+ title: 折线图:训练过程变化
61
+ type: line
62
+ categories: [Epoch 1, Epoch 2, Epoch 3, Epoch 4, Epoch 5]
63
+ valueAxisTitle: Loss
64
+ series:
65
+ - name: Train
66
+ values: [0.92, 0.71, 0.55, 0.44, 0.39]
67
+ - name: Valid
68
+ values: [0.98, 0.78, 0.63, 0.57, 0.52]
69
+ caption: 折线图适合展示训练损失、指标趋势或时间序列。
70
+
71
+ - layout: chart
72
+ title: 饼图:错误类型分布
73
+ type: pie
74
+ categories: [识别错误, 定位错误, 格式错误, 其他]
75
+ series:
76
+ - name: Error Types
77
+ values: [42, 27, 18, 13]
78
+ caption: 饼图适合少量类别占比,不建议用于精细数值比较。
79
+
80
+ - layout: closing
81
+ title: 谢谢
82
+ subtitle: 图表与流程图测试完成