skill-forge-hub 0.4.0 → 0.5.1

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 (41) hide show
  1. package/CHANGELOG.md +61 -0
  2. package/README.md +77 -22
  3. package/annual-report/SKILL.md +10 -3
  4. package/annual-report/resources/disk-and-html.md +7 -7
  5. package/annual-report/resources/reference.html +135 -41
  6. package/annual-report/user-config.md +3 -2
  7. package/article-to-html/SKILL.md +23 -0
  8. package/article-to-html/design-system.md +86 -21
  9. package/article-to-html/user-config.md +18 -0
  10. package/cli.js +71 -23
  11. package/daily-report/SKILL.md +10 -3
  12. package/daily-report/resources/disk-and-html.md +2 -2
  13. package/daily-report/resources/reference.html +163 -65
  14. package/daily-report/user-config.md +3 -2
  15. package/deck-stage/SKILL.md +80 -0
  16. package/deck-stage/package-lock.json +172 -0
  17. package/deck-stage/package.json +15 -0
  18. package/deck-stage/resources/cover-template.html +139 -0
  19. package/deck-stage/resources/data-schema.md +111 -0
  20. package/deck-stage/resources/pptx-builder.js +394 -0
  21. package/deck-stage/resources/usage-guide.md +20 -0
  22. package/deck-stage/user-config.md +26 -0
  23. package/magic-deck/SKILL.md +27 -13
  24. package/magic-deck/resources/content-guide.md +58 -0
  25. package/magic-deck/resources/deck-spec.md +380 -30
  26. package/magic-deck/resources/reference.html +1803 -82
  27. package/magic-deck/resources/usage-guide.md +42 -0
  28. package/magic-deck/user-config.md +10 -7
  29. package/meta-forge/SKILL.md +2 -0
  30. package/meta-forge/resources/forge-rules.md +1 -0
  31. package/package.json +16 -4
  32. package/prompt-pouch/SKILL.md +81 -0
  33. package/prompt-pouch/resources/data-format.md +67 -0
  34. package/prompt-pouch/resources/reference.html +2038 -0
  35. package/prompt-pouch/resources/usage-guide.md +32 -0
  36. package/prompt-pouch/user-config.md +22 -0
  37. package/template/SKILL.md +1 -0
  38. package/weekly-report/SKILL.md +10 -3
  39. package/weekly-report/resources/disk-and-html.md +2 -2
  40. package/weekly-report/resources/reference.html +184 -95
  41. package/weekly-report/user-config.md +3 -2
@@ -0,0 +1,394 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * pptx-builder.js — 登台 deck-stage 生成器(真相源)
4
+ * 用法: node pptx-builder.js --data <data.json> --out <输出.pptx>
5
+ * 读 data.json(schema 见 data-schema.md),按页类型渲染 16:9 pptx。
6
+ * 主题四套与报告 HTML 深蓝面板体系同源:tech 深蓝科技(默认)/ biz 浅色商务 / mono 极简黑白 / warm 温暖渐变。
7
+ * 依赖: pptxgenjs(本 skill 目录 node_modules,离线可用)。
8
+ */
9
+ 'use strict';
10
+
11
+ const path = require('path');
12
+ const PptxGenJS = require('pptxgenjs');
13
+ let SHAPES, CHART_TYPES; // pptxgenjs 4.x: ShapeType/ChartType 是实例属性,main 里赋值
14
+
15
+ /* ── 主题令牌(与报告 HTML CSS 变量同源) ─────────────────────────── */
16
+ const THEMES = {
17
+ tech: { bg: '0A1929', panel: '0B1E37', panelLine: '0F3350', text: 'E6F1FF', muted: '8892B0',
18
+ accent: '38BDF8', accent2: '0EA5E9', border: '1E3A5F', soft: '12263F', up: '34D399', down: 'FB7185' },
19
+ biz: { bg: 'FFFFFF', panel: 'F8FAFC', panelLine: 'E2E8F0', text: '0F172A', muted: '64748B',
20
+ accent: '2563EB', accent2: '1D4ED8', border: 'E2E8F0', soft: 'F1F5F9', up: '059669', down: 'E11D48' },
21
+ mono: { bg: 'FFFFFF', panel: 'FFFFFF', panelLine: 'D4D4D4', text: '111111', muted: '666666',
22
+ accent: '000000', accent2: '333333', border: 'CCCCCC', soft: 'F5F5F5', up: '059669', down: 'DC2626' },
23
+ warm: { bg: '1C0F0F', panel: '2A1717', panelLine: '4A2F1F', text: 'FFF1E6', muted: 'C4A98F',
24
+ accent: 'FDBA74', accent2: 'F97316', border: '4A2F1F', soft: '33201A', up: '4ADE80', down: 'FDA4AF' }
25
+ };
26
+
27
+ /* 五段卡片节色(与年报同款) */
28
+ const SECTION_COLORS = {
29
+ blue: { c: '38BDF8', c2: '0EA5E9' },
30
+ green: { c: '5EEAD4', c2: '14B8A6' },
31
+ purple: { c: 'A5B4FC', c2: '6366F1' },
32
+ rose: { c: 'FDA4AF', c2: 'E11D48' },
33
+ orange: { c: 'FDBA74', c2: 'F97316' }
34
+ };
35
+
36
+ const FONT = 'Microsoft YaHei';
37
+ const PW = 13.33, PH = 7.5; // 16:9 英寸
38
+ const MARGIN_L = 0.85; // 内容左边距(节色竖线在 0.5)
39
+ const CONTENT_W = PW - MARGIN_L - 0.35;
40
+
41
+ /* ── CLI ─────────────────────────────────────────────────────────── */
42
+ function parseArgs(argv) {
43
+ const o = {};
44
+ for (let i = 2; i < argv.length; i++) {
45
+ if (argv[i] === '--data' && argv[i + 1]) o.data = argv[++i];
46
+ else if (argv[i] === '--out' && argv[i + 1]) o.out = argv[++i];
47
+ }
48
+ if (!o.data || !o.out) {
49
+ console.error('用法: node pptx-builder.js --data <data.json> --out <输出.pptx>');
50
+ process.exit(1);
51
+ }
52
+ return o;
53
+ }
54
+
55
+ /* ── 工具函数 ────────────────────────────────────────────────────── */
56
+ function section(name) {
57
+ return SECTION_COLORS[name] || SECTION_COLORS.blue;
58
+ }
59
+
60
+ /* 页面壳:背景 + 页脚 + 节色竖线 */
61
+ function shell(slide, t, colorName, footer) {
62
+ slide.background = { color: t.bg };
63
+ // 左侧节色竖线(贯穿整页,同色光晕)
64
+ slide.addShape(SHAPES.rect, {
65
+ x: 0.5, y: 0.72, w: 0.075, h: PH - 1.44, fill: { color: section(colorName).c }, line: { color: section(colorName).c, width: 0 }
66
+ });
67
+ slide.addShape(SHAPES.rect, {
68
+ x: 0.46, y: 0.68, w: 0.155, h: PH - 1.36, fill: { color: section(colorName).c, transparency: 82 }, line: { color: section(colorName).c, width: 0 }
69
+ });
70
+ // 页脚
71
+ if (footer) {
72
+ slide.addText(footer, { x: 7.3, y: PH - 0.42, w: PW - 7.7, h: 0.3, fontSize: 9, color: t.muted, fontFace: FONT, align: 'right', valign: 'middle' });
73
+ }
74
+ }
75
+
76
+ /* 页标题(节色 + 微光晕) */
77
+ function pageTitle(slide, t, colorName, text) {
78
+ const c = section(colorName).c;
79
+ slide.addText(text, {
80
+ x: MARGIN_L, y: 0.42, w: CONTENT_W, h: 0.6, fontSize: 25, bold: true, color: c, fontFace: FONT, valign: 'middle',
81
+ shadow: { type: 'outer', color: c, blur: 9, offset: 0, angle: 90, opacity: 0.55 }
82
+ });
83
+ }
84
+
85
+ /* 圆角卡片(panel 底 + 细描边) */
86
+ function roundCard(slide, t, x, y, w, h, opts) {
87
+ opts = opts || {};
88
+ slide.addShape(SHAPES.roundRect, {
89
+ x, y, w, h, rectRadius: 0.07,
90
+ fill: { color: opts.fill || t.panel },
91
+ line: { color: opts.line || t.panelLine, width: 1 },
92
+ shadow: opts.noShadow ? undefined : { type: 'outer', color: '000000', blur: 6, offset: 2, angle: 90, opacity: 0.35 }
93
+ });
94
+ }
95
+
96
+ /* 序号圆球徽章(径向受光模拟:主圆 + 左上高光点 + 外光晕) */
97
+ function badge(slide, t, colorName, x, y, d, num) {
98
+ const c = section(colorName).c, c2 = section(colorName).c2;
99
+ slide.addShape(SHAPES.ellipse, { x: x - 0.055, y: y - 0.055, w: d + 0.11, h: d + 0.11, fill: { color: c, transparency: 72 }, line: { color: c, width: 0 } });
100
+ slide.addShape(SHAPES.ellipse, { x, y, w: d, h: d, fill: { color: c2 }, line: { color: c, width: 0.75 } });
101
+ slide.addShape(SHAPES.ellipse, { x: x + d * 0.22, y: y + d * 0.16, w: d * 0.3, h: d * 0.22, fill: { color: c, transparency: 38 }, line: { color: c, width: 0 } });
102
+ slide.addText(String(num), { x, y, w: d, h: d, fontSize: 13, bold: true, color: 'FFFFFF', fontFace: FONT, align: 'center', valign: 'middle' });
103
+ }
104
+
105
+ /* 时间线节点圆 */
106
+ function timelineDot(slide, colorName, x, y) {
107
+ const c = section(colorName).c;
108
+ slide.addShape(SHAPES.ellipse, { x: x - 0.08, y: y - 0.08, w: 0.32, h: 0.32, fill: { color: c, transparency: 75 }, line: { color: c, width: 0 } });
109
+ slide.addShape(SHAPES.ellipse, { x, y, w: 0.16, h: 0.16, fill: { color: c }, line: { color: c, width: 0 } });
110
+ }
111
+
112
+ /* ── 页渲染 ──────────────────────────────────────────────────────── */
113
+
114
+ /* 整页铺图(封面/结尾截图) */
115
+ function renderCoverImage(slide, page) {
116
+ slide.addImage({ path: page.image, x: 0, y: 0, w: PW, h: PH });
117
+ }
118
+
119
+ /* 概览页:标题 + TL;DR + 数据卡横排 */
120
+ function renderOverview(slide, t, page, meta) {
121
+ shell(slide, t, page.color || 'blue', meta.footer);
122
+ pageTitle(slide, t, page.color || 'blue', page.title || '概览');
123
+ if (page.tlDr) {
124
+ slide.addText(page.tlDr, { x: MARGIN_L, y: 1.06, w: CONTENT_W, h: 0.4, fontSize: 12.5, color: t.muted, fontFace: FONT, valign: 'middle' });
125
+ }
126
+ const stats = (page.stats || []).slice(0, 6);
127
+ const n = stats.length;
128
+ if (n === 0) return;
129
+ const gap = 0.3;
130
+ const cardW = (CONTENT_W - gap * (n - 1)) / n;
131
+ const cardY = 1.72, cardH = 1.62;
132
+ stats.forEach((s, i) => {
133
+ const x = MARGIN_L + i * (cardW + gap);
134
+ roundCard(slide, t, x, cardY, cardW, cardH);
135
+ // 卡左侧节色小条
136
+ slide.addShape(SHAPES.rect, {
137
+ x: x + 0.12, y: cardY + 0.22, w: 0.05, h: cardH - 0.44, fill: { color: section(page.color || 'blue').c }, line: { color: section(page.color || 'blue').c, width: 0 }
138
+ });
139
+ slide.addText(String(s.value), { x: x + 0.26, y: cardY + 0.2, w: cardW - 0.4, h: 0.55, fontSize: 27, bold: true, color: section(page.color || 'blue').c, fontFace: FONT });
140
+ slide.addText(s.label, { x: x + 0.26, y: cardY + 0.78, w: cardW - 0.4, h: 0.34, fontSize: 11.5, color: t.muted, fontFace: FONT, valign: 'middle' });
141
+ if (s.delta) {
142
+ const up = s.deltaUp !== false;
143
+ slide.addText(s.delta, { x: x + 0.26, y: cardY + 1.12, w: cardW - 0.4, h: 0.3, fontSize: 10, bold: true, color: up ? t.up : t.down, fontFace: FONT, valign: 'middle' });
144
+ }
145
+ });
146
+ }
147
+
148
+ /* 时间线页:竖线 + 季度节点 + 条目(每页最多 8 条,自动续页) */
149
+ function renderTimeline(slide, t, page, meta, items) {
150
+ shell(slide, t, page.color || 'green', meta.footer);
151
+ pageTitle(slide, t, page.color || 'green', page.title || '时间线');
152
+ const LX = 1.32; // 竖线 x
153
+ const lineTop = 1.42, lineBottom = PH - 0.62;
154
+ slide.addShape(SHAPES.line, {
155
+ x: LX, y: lineTop, w: 0, h: lineBottom - lineTop, line: { color: section(page.color || 'green').c, width: 2 }
156
+ });
157
+ const maxH = lineBottom - lineTop;
158
+ const n = items.length;
159
+ const blockH = Math.min(maxH / n, 1.24); // 每条块高
160
+ let y = lineTop + 0.12;
161
+ for (const it of items) {
162
+ const q = it.q || '';
163
+ timelineDot(slide, page.color || 'green', LX - 0.08, y + 0.1);
164
+ if (q) {
165
+ slide.addText(q, { x: 0.5, y: y + 0.02, w: 0.7, h: 0.3, fontSize: 12.5, bold: true, color: section(page.color || 'green').c, fontFace: FONT, align: 'right', valign: 'middle' });
166
+ }
167
+ slide.addText(it.desc, { x: 1.78, y: y - 0.04, w: PW - 2.2, h: 0.5, fontSize: 14.5, color: t.text, fontFace: FONT, valign: 'middle' });
168
+ if (it.metric) {
169
+ slide.addText(it.metric, { x: 1.78, y: y + 0.44, w: PW - 2.2, h: 0.32, fontSize: 11, color: t.muted, fontFace: FONT, valign: 'middle' });
170
+ }
171
+ y += blockH;
172
+ }
173
+ }
174
+
175
+ /* 条目页:序号球 + 标题 + 描述(每页最多 6 条,自动续页;withImage 时右侧配图、文本区缩窄,仅第一页) */
176
+ function renderBullets(slide, t, page, meta, items, withImage) {
177
+ shell(slide, t, page.color || 'purple', meta.footer);
178
+ pageTitle(slide, t, page.color || 'purple', page.title || '条目');
179
+ // 右侧配图(contain 等比内嵌,不裁切变形)
180
+ let imgX = PW - 0.4;
181
+ if (withImage && page.image) {
182
+ const imgW = 3.35, imgX0 = PW - 0.5 - imgW, imgY0 = 1.5, imgH0 = PH - 2.15;
183
+ slide.addShape(SHAPES.roundRect, {
184
+ x: imgX0 - 0.1, y: imgY0 - 0.1, w: imgW + 0.2, h: imgH0 + 0.2, rectRadius: 0.06,
185
+ fill: { color: t.panel }, line: { color: t.panelLine, width: 1 },
186
+ shadow: { type: 'outer', color: '000000', blur: 6, offset: 2, angle: 90, opacity: 0.3 }
187
+ });
188
+ slide.addImage({ path: page.image, x: imgX0, y: imgY0, w: imgW, h: imgH0, sizing: { type: 'contain', w: imgW, h: imgH0 } });
189
+ imgX = imgX0 - 0.3;
190
+ }
191
+ const top = 1.55, bottom = PH - 0.7;
192
+ const step = Math.min((bottom - top) / items.length, 1.32);
193
+ let y = top;
194
+ items.forEach((it, i) => {
195
+ const d = 0.42;
196
+ badge(slide, t, page.color || 'purple', MARGIN_L + 0.06, y + 0.04, d, i + 1);
197
+ const tx = MARGIN_L + 0.72;
198
+ const textW = Math.max(imgX - tx - 0.25, 3.5);
199
+ if (typeof it === 'string') it = { t: it };
200
+ if (it.t) {
201
+ slide.addText(it.t, { x: tx, y: y, w: textW, h: 0.42, fontSize: 15, bold: true, color: t.text, fontFace: FONT, valign: 'middle' });
202
+ }
203
+ if (it.d) {
204
+ slide.addText(it.d, { x: tx, y: y + (it.t ? 0.42 : 0.05), w: textW, h: 0.62, fontSize: 12, color: t.muted, fontFace: FONT, valign: 'top' });
205
+ }
206
+ y += step;
207
+ });
208
+ }
209
+
210
+ /* 柱图页:自绘柱(任意数量,自动算宽)+ 数值 + 标签 */
211
+ function renderChart(slide, t, page, meta) {
212
+ shell(slide, t, page.color || 'orange', meta.footer);
213
+ pageTitle(slide, t, page.color || 'orange', page.title || '图表');
214
+ const values = (page.values || []).map(Number);
215
+ const labels = page.labels || [];
216
+ if (values.length === 0) return;
217
+ const max = Math.max(...values, 1);
218
+ const plotX = 0.95, plotW = PW - 1.55, plotY = 1.85, plotH = 4.35;
219
+ // 基线
220
+ slide.addShape(SHAPES.line, {
221
+ x: plotX, y: plotY + plotH, w: plotW, h: 0, line: { color: t.border, width: 1.2 }
222
+ });
223
+ const n = values.length;
224
+ const slot = plotW / n;
225
+ const barW = Math.min(0.52, slot * 0.6);
226
+ values.forEach((v, i) => {
227
+ const bh = Math.max((v / max) * plotH, v > 0 ? 0.12 : 0);
228
+ const x = plotX + slot * i + (slot - barW) / 2;
229
+ const y = plotY + plotH - bh;
230
+ const c = section(page.color || 'orange').c;
231
+ // 柱 + 顶高光
232
+ slide.addShape(SHAPES.roundRect, {
233
+ x, y, w: barW, h: bh, rectRadius: 0.045,
234
+ fill: { color: c, transparency: 14 }, line: { color: c, width: 0.75 },
235
+ shadow: { type: 'outer', color: c, blur: 6, offset: 0, angle: 90, opacity: 0.35 }
236
+ });
237
+ slide.addText(String(v), { x: x - 0.15, y: y - 0.3, w: barW + 0.3, h: 0.26, fontSize: 10, bold: true, color: t.text, fontFace: FONT, align: 'center', valign: 'middle' });
238
+ if (labels[i]) {
239
+ slide.addText(labels[i], { x: plotX + slot * i, y: plotY + plotH + 0.1, w: slot, h: 0.3, fontSize: 10.5, color: t.muted, fontFace: FONT, align: 'center', valign: 'middle' });
240
+ }
241
+ });
242
+ }
243
+
244
+ /* 饼图页:原生饼图(可编辑),items=[{label,value}],page.percent=true 时显示百分比 */
245
+ function renderChartPie(slide, t, page, meta, SHAPES_UNUSED) {
246
+ shell(slide, t, page.color || 'green', meta.footer);
247
+ pageTitle(slide, t, page.color || 'green', page.title || '占比');
248
+ const items = (page.items || []).filter(function(i){ return i && Number(i.value) > 0; });
249
+ if (items.length === 0) return;
250
+ const pieData = [{
251
+ name: page.seriesName || '占比',
252
+ labels: items.map(function(i){ return i.label; }),
253
+ values: items.map(function(i){ return Number(i.value); })
254
+ }];
255
+ const palette = ['38BDF8', '5EEAD4', 'A5B4FC', 'FDA4AF', 'FDBA74', '34D399', 'F472B6', 'FBBF24', 'A78BFA', 'F87171'];
256
+ slide.addChart(CHART_TYPES.pie, pieData, {
257
+ x: 1.05, y: 1.35, w: PW - 2.1, h: PH - 2.0,
258
+ chartColors: palette,
259
+ showLegend: true, legendPos: 'r', legendColor: t.muted, legendFontFace: FONT, legendFontSize: 11,
260
+ dataLabelColor: t.text, dataLabelFontFace: FONT, dataLabelFontSize: 10.5,
261
+ showValue: page.percent ? false : true,
262
+ showPercent: !!page.percent,
263
+ showLabel: true,
264
+ dataLabelPosition: 'outEnd',
265
+ holeSize: page.donut ? 45 : 0
266
+ });
267
+ }
268
+
269
+ /* 折线页:原生折线(可编辑),series=[{name,values}] 多线;单线可直接用 values */
270
+ function renderChartLine(slide, t, page, meta, SHAPES_UNUSED) {
271
+ shell(slide, t, page.color || 'blue', meta.footer);
272
+ pageTitle(slide, t, page.color || 'blue', page.title || '趋势');
273
+ const labels = page.labels || [];
274
+ let series = page.series;
275
+ if (!series) series = [{ name: page.seriesName || '数值', values: page.values || [] }];
276
+ const c = section(page.color || 'blue').c;
277
+ const chartData = series.map(function(s){ return { name: s.name, labels: labels, values: (s.values || []).map(Number) }; });
278
+ if (chartData.length === 0 || labels.length === 0) return;
279
+ slide.addChart(CHART_TYPES.line, chartData, {
280
+ x: 1.05, y: 1.4, w: PW - 2.1, h: PH - 2.1,
281
+ chartColors: [c, '34D399', 'FBBF24', 'F472B6'],
282
+ lineSize: 2.5, lineSmooth: !!page.smooth,
283
+ showLegend: series.length > 1, legendPos: 'b', legendColor: t.muted, legendFontFace: FONT, legendFontSize: 10.5,
284
+ catAxisLabelColor: t.muted, catAxisLabelFontFace: FONT, catAxisLabelFontSize: 10,
285
+ valAxisLabelColor: t.muted, valAxisLabelFontFace: FONT, valAxisLabelFontSize: 10,
286
+ valGridLine: { color: t.border, style: 'solid', size: 0.5 },
287
+ showValue: false,
288
+ valAxisHidden: false
289
+ });
290
+ }
291
+
292
+ /* 演讲备注:page.notes 写在该类型第一页;page.notesList 数组按分页依次写 */
293
+ function addNotesIfAny(slide, page, idx) {
294
+ if (typeof page.notes === 'string' && idx === 0) slide.addNotes(page.notes);
295
+ else if (Array.isArray(page.notesList) && page.notesList[idx]) slide.addNotes(String(page.notesList[idx]));
296
+ }
297
+
298
+ /* ── 主流程 ──────────────────────────────────────────────────────── */
299
+ function main() {
300
+ const args = parseArgs(process.argv);
301
+ let data;
302
+ try {
303
+ data = JSON.parse(require('fs').readFileSync(args.data, 'utf8'));
304
+ } catch (e) {
305
+ console.error('data.json 读取/解析失败:', e.message);
306
+ process.exit(1);
307
+ }
308
+ const meta = data.meta || {};
309
+ const theme = THEMES[data.theme] ? data.theme : 'tech';
310
+ const t = THEMES[theme];
311
+
312
+ const pptx = new PptxGenJS();
313
+ pptx.defineLayout({ name: 'WIDE', width: PW, height: PH });
314
+ pptx.layout = 'WIDE';
315
+ SHAPES = pptx.ShapeType;
316
+ CHART_TYPES = pptx.ChartType;
317
+ pptx.author = 'SPECCORE TEAM';
318
+ pptx.title = meta.title || '报告';
319
+
320
+ for (const page of data.pages || []) {
321
+ switch (page.type) {
322
+ case 'cover-image':
323
+ case 'closing-image': {
324
+ if (page.image) {
325
+ const s = pptx.addSlide();
326
+ renderCoverImage(s, page);
327
+ addNotesIfAny(s, page, 0);
328
+ } else console.warn('跳过图片页:缺 image 字段');
329
+ break;
330
+ }
331
+ case 'overview': {
332
+ const s = pptx.addSlide();
333
+ renderOverview(s, t, page, meta);
334
+ addNotesIfAny(s, page, 0);
335
+ break;
336
+ }
337
+ case 'timeline': {
338
+ const items = (page.items || []).flatMap(g => {
339
+ if (g.items) return g.items.map(it => Object.assign({ q: g.q }, it));
340
+ return [g];
341
+ });
342
+ let pi = 0;
343
+ for (let i = 0; i < items.length; i += 8) {
344
+ const sub = Object.assign({}, page, { items: items.slice(i, i + 8) });
345
+ const s = pptx.addSlide();
346
+ renderTimeline(s, t, sub, meta, sub.items);
347
+ addNotesIfAny(s, page, pi++);
348
+ }
349
+ break;
350
+ }
351
+ case 'bullets': {
352
+ const items = page.items || [];
353
+ let pi = 0;
354
+ for (let i = 0; i < items.length; i += 6) {
355
+ const s = pptx.addSlide();
356
+ renderBullets(s, t, page, meta, items.slice(i, i + 6), i === 0);
357
+ addNotesIfAny(s, page, pi++);
358
+ }
359
+ break;
360
+ }
361
+ case 'chart': {
362
+ const s = pptx.addSlide();
363
+ renderChart(s, t, page, meta);
364
+ addNotesIfAny(s, page, 0);
365
+ break;
366
+ }
367
+ case 'chart-pie': {
368
+ const s = pptx.addSlide();
369
+ renderChartPie(s, t, page, meta);
370
+ addNotesIfAny(s, page, 0);
371
+ break;
372
+ }
373
+ case 'chart-line': {
374
+ const s = pptx.addSlide();
375
+ renderChartLine(s, t, page, meta);
376
+ addNotesIfAny(s, page, 0);
377
+ break;
378
+ }
379
+ default:
380
+ console.warn('跳过未知页类型:', page.type);
381
+ }
382
+ }
383
+
384
+ const outDir = path.dirname(args.out);
385
+ require('fs').mkdirSync(outDir, { recursive: true });
386
+ pptx.writeFile({ fileName: args.out }).then(f => {
387
+ console.log('PPTX_OK ' + path.resolve(f));
388
+ }).catch(e => {
389
+ console.error('生成失败:', e.message);
390
+ process.exit(1);
391
+ });
392
+ }
393
+
394
+ main();
@@ -0,0 +1,20 @@
1
+ # 登台 deck-stage 使用指南
2
+
3
+ 登台(deck-stage)把日报/周报/年报的产出——或任何散装素材——转成专业 .pptx 演示文稿:常规页(数据卡/时间线/条目/柱图/饼图/折线)原生生成、完全可编辑,封面与结尾页用 HTML 截图贴图、视觉保真。页面与数据分离,四主题(深蓝科技/浅色商务/极简黑白/温暖渐变)与报告同源,改主题只改一处。
4
+
5
+ ## 怎么用
6
+
7
+ 1. **一句话登台**:对 AI 说「把今天的日报转成 PPT」「把这份年报做成 pptx」,或直接给报告文件路径;没有现成报告时给主题大纲也行,AI 会先提炼要点
8
+ 2. **三问交互**:AI 会先问你素材(指定目录/指定文件/贴文字/口述主题)→ 再报页结构大纲表给你增删改 → 最后问成片方式(一键成片 / 逐页确认)
9
+ 3. **逐页确认**:选逐页确认时,每页先报排版方案(版式 + 内容 + 图表 + 演讲备注),你 OK 一页出一页;说「第 N 页改成 XX」重出单页、「换主题 biz」全局换、「加一页/删一页」改骨架
10
+ 4. **自动排版**:AI 按报告结构自动映射版式——概览成数据卡页、成果成时间线页、条目成徽章页、提交节奏成柱图页,页数多了自动续页
11
+ 5. **封面与结尾**:默认用深蓝面板封面模板(发光标题 + 数据卡),结尾「谢谢观看」;想用自己的图片说一声即可
12
+ 6. **改风格**:说「用浅色商务主题」「用极简黑白」——四套主题秒换;想永久换默认,改 `user-config.md` 的 THEME
13
+ 7. **改内容**:生成后可直接在 PowerPoint/WPS 里编辑常规页(文本/饼图/折线都是原生元素);想改截图页(封面/结尾)让 AI 重新生成即可
14
+ 8. **落盘位置**:默认落到报告目录(`user-config.md` 的 DIR 可改);说「放到桌面」即按临时要求落盘
15
+
16
+ ## 提醒
17
+
18
+ - 转换不改原报告:md/HTML 原样保留,pptx 是新增产物
19
+ - 同名 pptx 不会覆盖旧文件,自动加日期后缀
20
+ - 生成全程离线,pptx 双击即开,可放心走内网流程
@@ -0,0 +1,26 @@
1
+ # 落盘根目录(自动按目标日期建子目录;目录不存在时回退用户主目录下 reports)
2
+ DIR_MAC: /Users/luzhaosheng/MyWork/智慧食堂/reports
3
+ DIR_WIN: %USERPROFILE%\reports
4
+
5
+ # pptx 主题(与报告四主题同源)
6
+ # 可选:tech 深蓝科技(默认)/ biz 浅色商务 / mono 极简黑白 / warm 温暖渐变
7
+ THEME: tech
8
+
9
+ # 成片方式(三问模型第三问的默认答案)
10
+ # 可选:page-by-page 逐页确认(默认,每页先报排版方案,OK 才出片)/ once 一键成片
11
+ MODE: page-by-page
12
+
13
+ # 受众画像(演讲备注话术参考:开场白/数据解读/过渡句的语气与深度)
14
+ AUDIENCE: 内部汇报
15
+
16
+ # 是否生成演讲备注(每页 notes:开场白/数据解读/过渡句)
17
+ NOTES: true
18
+
19
+ # 生成后是否自动打开 pptx(macOS open / Windows start)
20
+ OPEN_PPTX: true
21
+
22
+ # 落盘前是否先确认目标目录
23
+ CONFIRM: false
24
+
25
+ # 配置结构版本(升级迁移用,勿手改)
26
+ CONFIG_VERSION: 2
@@ -1,49 +1,63 @@
1
1
  ---
2
2
  name: magic-deck
3
- description: 把内容做成 HTML 形态、PPT 体验的魔法幻灯(单文件离线打开、零外部依赖)。内置播放引擎(自动播放/循环/间隔可设/从头或当前播放)、键盘热键(翻页/直达/截图/复制/全屏)、五套内置模板(经典商务/极简留白/杂志画册/黑金发布/时间线叙事,放映时经「🎨 模板」面板随时切换——可应用到全部页/仅本页/勾选页,标题自动映射到模板固定位置)+ 四套主题即点即换(整份或单页自由换肤)、页内连接与外部链接、逐页 PNG 截图(本页/选页批量,选页面板默认全选=一键导出全部页)与本页/选页批量文字复制(均不含任何操作元素)、全屏放映纯净模式(无关元素自动退场)。当用户请求把内容做成幻灯片/PPT/演示文稿、生成 HTML 幻灯、做分享页/汇报页,提到 magic-deck 或 /magic-deck,或询问其用法(怎么用/使用说明/能干什么)时使用。
3
+ description: 把内容做成 HTML 形态、PPT 体验的魔法幻灯(单文件离线打开、零外部依赖)。内置播放引擎(自动播放/循环/间隔可设/从头或当前播放)、键盘热键(翻页/直达/截图/复制/全屏)、十六套内置模板(经典商务/极简留白/杂志画册/黑金发布/时间线叙事/流程步骤/海报宣传/复古报纸/金融报表/蓝图工程/教学板书/高管简报/扁平磁贴/模块网格/暗夜深色/自然清新,放映时经「🎨 模板」面板随时切换
4
+ + 模板面板「💼 领域」Tab 二十一套成套模板(六分类:教育学术——毕业答辩/开题报告/教学课件;职场商务——年终汇报/竞聘述职/商业计划书/求职简历;企业运营——产品发布/科技发布会/营销方案/企业培训/年会盛典;专业领域——金融分析/医疗科普/法律合规;党政政务——政务汇报/党建汇报;生活场景——餐饮品牌/旅游攻略/节日庆典/公益慈善,一键生成整套页面——封面+目录+内容页+图表页,两段确认可撤销)+ 图表动画五式(柱状生长/折线描画/饼图扇入/仪表扫弧/数字滚动,图表挂 data-anim-chart 翻页自动播放、翻回重放,保留图表自身动画)——可应用到全部页/仅本页/勾选页,支持「预览本页」先看效果再应用,标题自动映射到模板固定位置)、编辑能力(撤销/重做 Ctrl+Z、右侧页面管理边栏:缩略图跳页/新页/复页/排序/删页)+ 十六套主题面板即点即换(全部页/仅本页自由换肤)、动画库(右侧边栏「⚙ 工具」Tab:翻页风格八套 / 元素入场十二式 / 离场十二式 / 强调八式 / 标题文字动画四式,应用范围全部页面或仅本页,即点即换、选择记忆)、页内连接与外部链接、逐页 PNG 截图(本页/选页批量,选页面板默认全选=一键导出全部页)与本页/选页批量文字复制(均不含任何操作元素)、全屏放映纯净模式(无关元素自动退场)。当用户请求把内容做成幻灯片/PPT/演示文稿、生成 HTML 幻灯、做分享页/汇报页,提到 magic-deck 或 /magic-deck,或询问其用法(怎么用/使用说明/能干什么)时使用。
4
5
  allowed-tools: Bash, Read, Write
5
6
  disable-model-invocation: false
6
7
  ---
7
8
 
8
9
  # 魔法幻灯生成(magic-deck)
9
10
 
10
- 把用户内容排版为单文件 HTML 幻灯片(16:9 舞台 + 播放引擎 + 四套风格 + 全屏放映),落盘并自动打开。
11
+ 把用户内容排版为单文件 HTML 幻灯片(16:9 舞台 + 播放引擎 + 十六套风格 + 全屏放映),落盘并自动打开。
11
12
 
12
13
  ## 生成流程
13
14
 
14
- 0. **读用户配置**(TEMPLATE/THEME/WIDTH/HEIGHT/SEC/FILENAME/DIR 等)→ 先 Read 同目录 `user-config.md`
15
- 1. **收集内容**:解析用户提供的内容(文字/文件/结构化要点),确定页数与每页版式
16
- 2. **生成 HTML**(每页写语义结构——.s-head 标题 / .card 卡片 / .points 要点 / .bignum 数据,按 TEMPLATE 在对应页 slide 挂 data-template 声明默认版式;四主题 CSS 变量、单页主题按钮、模板面板、播放引擎、热键、截图/复制、全屏纯净模式)→ 先 Read `resources/reference.html` + `resources/deck-spec.md` 两份,以 reference.html 为骨架逐处替换内容
17
- 3. **落盘与命名**:写 `<文件名>.html` 到 DIR 目录,自动打开浏览器供用户放映。命名三级优先:①用户口头指定文件名 → 用指定名;②未指定且 FILENAME=auto → 按本次内容主题+用途总结中文名(如「智能探针系统-产品介绍」,让用户日后能按名找回继续改);③FILENAME 写死固定名 → 用固定名。重名一律自动加日期后缀,绝不覆盖;落盘目录复用不清空、不改动目录内既有文件
18
- 4. **使用咨询**(用户只问「怎么用」等用法、未要求生成)→ 输出纯文字使用指南(功能/热键/截图/复制/全屏/主题),不生成 HTML
15
+ 0. **读用户配置**(TEMPLATE/THEME/TRANSITION/ANIM/WIDTH/HEIGHT/SEC/FILENAME/DIR 等)→ 先 Read 同目录 `user-config.md`
16
+ 1. **收集内容**:解析用户提供的内容(文字/文件/结构化要点),确定页数与每页版式 → 先 Read `resources/content-guide.md` 按内容气质选版式骨架 / 按领域选成套模板 / 按用途定页数节奏 / 按数据挑图表形式
17
+ 2. **生成 HTML**(每页写语义结构——.s-head 标题 / .card 卡片 / .points 要点 / .bignum 数据,按 TEMPLATE 在对应页 slide 挂 data-template 声明默认版式;十六主题 CSS 变量、主题面板、动画库(deck 标签 data-transition 翻页八套 + 元素 data-anim 入场十二式 / data-anim-leave 离场 / data-anim-emphasis 强调 + 标题文字拆分 + 边栏 ⚙ 工具 Tab 控制)、模板面板(16 套 + 预览本页)、编辑能力(撤销/重做/页面管理边栏:新页/复页/排序/删页)、播放引擎、热键、截图/复制、全屏纯净模式)→ 先 Read `resources/reference.html` + `resources/deck-spec.md` 两份,以 reference.html 为骨架逐处替换内容
18
+ 2.5 **交付前自检**(落盘前必做,硬门禁不可跳过):写工作区 `outputs/<文件名>.html` 后——
19
+ - Bash 起 `python3 -m http.server 8941`(后台,端口占用换 8942/8943…)
20
+ - Playwright headless 自检(缺则 `pip3 install --user playwright`;用系统 Chrome,无需 playwright install 下载浏览器):`p.chromium.launch(channel="chrome", headless=True)` → goto 页面 → 挂 pageerror 监听(零容忍)→ 按页数循环 `go(i)` 把每一页翻完
21
+ - 截首 / 中 / 末三页 PNG,Read 自查:版式正常、文字无溢出截断、图表数据到位、无豆腐块缺字
22
+ - 任何报错或视觉问题:修 HTML → 重检;全绿才进第 3 步
23
+ 3. **落盘与命名**:自检通过后 `cp` 到 DIR 目录,自动打开浏览器供用户放映。命名三级优先:①用户口头指定文件名 → 用指定名;②未指定且 FILENAME=auto → 按本次内容主题+用途总结中文名(如「智能探针系统-产品介绍」,让用户日后能按名找回继续改);③FILENAME 写死固定名 → 用固定名。重名一律自动加日期后缀,绝不覆盖;落盘目录复用不清空、不改动目录内既有文件
24
+ 4. **使用咨询**(用户只问「怎么用 / 使用说明 / 能干什么」等用法、未要求生成)→ 读 `resources/usage-guide.md`,将其内容原样输出(首行标题改为「魔法幻灯生成器使用指南」),不生成 HTML
19
25
 
20
26
  ## ⚙️ 配置与自定义
21
27
 
22
- **执行本 skill 前必须先 Read 同目录 `user-config.md`**:默认配置(TEMPLATE/THEME/WIDTH/HEIGHT/SEC/FILENAME/DIR 等)与用户模板都在该文件中,以它为准。升级技能时 user-config.md 不会被覆盖,你的修改永久保留;官方更新配置时另存 `user-config.md.upstream` 对照文件,可按需手动合并新配置点。
28
+ **执行本 skill 前必须先 Read 同目录 `user-config.md`**:默认配置(TEMPLATE/THEME/TRANSITION/ANIM/WIDTH/HEIGHT/SEC/FILENAME/DIR 等)与用户模板都在该文件中,以它为准。升级技能时 user-config.md 不会被覆盖,你的修改永久保留;官方更新配置时另存 `user-config.md.upstream` 对照文件,可按需手动合并新配置点。
23
29
 
24
30
  ## 规则速览
25
31
 
26
32
  - 单文件 HTML 离线双击可打开:禁用 CDN/@import/外链 CSS/JS/图片/网络字体,字体用系统栈
27
- - 模板五套(版式骨架)与主题四套(配色)正交组合,均运行时即点即换:模板经「🎨 模板」面板应用(全部页/仅本页/勾选页),生成时按 TEMPLATE/口头点名在 slide 挂 data-template 声明默认版式;标题自动映射——timeline 取卡片标题做节点、keynote 取页标题做大观点、magazine 左文右图分栏,切回经典商务恢复原排版内容不丢;`.slide` 自带 `background:var(--bg)` 使单页换肤整页变色
33
+ - 模板十六套(版式骨架)与主题十六套(配色)正交组合,均运行时即点即换:模板经「🎨 模板」面板应用(全部页/仅本页/勾选页),先「👁 预览本页」看效果(不落 data-template,取消自动还原);生成时按 TEMPLATE/口头点名在 slide 挂 data-template 声明默认版式;标题自动映射——timeline 取卡片标题做节点、keynote 取页标题做大观点、magazine 左文右图分栏、steps 卡片转箭头步骤条、vintage 卡片收双栏报纸、poster 要点转斜切色带、finance 百分比转仪表环,切回经典商务恢复原排版内容不丢;`.slide` 自带 `background:var(--bg)` 使单页换肤整页变色
34
+ - 编辑操作全套可回退:Ctrl+Z 撤销 / Ctrl+Shift+Z 重做(工具栏 ↶↷);「▤ 页导航 / G 键」右侧页面管理边栏:点击缩略图跳页、悬停操作条复页/上移/下移/删页;「+ 新页」在边栏头部,16 版式任选插在当前页后;全部编辑只作用于内存,刷新后回到原始 deck
28
35
  - 工具栏控件统一 flex 垂直居中:`.toolbar/.group` flex + align-items:center + gap;`.tbtn` inline-flex + gap:6px + line-height:1;checkbox 14×14 与文字 6px 间距,文字不贴控件、上下不偏斜
29
36
  - 页面 1280×720 舞台 + 按窗口等比缩放(--sc),窗口任意大小不变形
30
- - 结构顺序:背景装饰 → 顶部进度条 → 工具栏 → 页码 → 提示 → 左右箭头 → 舞台页组 → 说明弹层 → 播放引擎
37
+ - 领域/业务成套模板 21 套(thesis/proposal/teaching/report/competition/plan/hiring/product/tech/marketing/training/ceremony/finance/medical/law/gov/party/food/travel/festival/charity,六分类):面板「💼 领域」Tab 点选后一键生成整套页面(P 生成器产封面+目录+内容页+图表页+结束页,每套 6-8 页,主版式各不相同);两段确认(profArmed)防误触,pushUndo 可撤销;生成后与版式/主题正交——每页照常挂 data-template 运行时换版式、主题照常换肤;演示页 15 的 21 张 .prof-demo 卡点击直达面板并选中
38
+ - 图表五式动画(L6,容器挂 data-anim-chart):bars 柱状生长(--h 定高 + --bi 错峰)/ line 折线描画(dashoffset 归零 + 圆点按序浮现)/ pie 饼图扇入(弧段 rotate 定起点 + --po 扫入)/ ring 仪表扫弧(顶部起扫 + 中心数字)/ count 数字滚动(data-count + data-count-dec,go() 里 rAF 900ms 缓动);图表动画独立于元素入场体系(图表是「会动的数据」不挂 data-anim);prefers-reduced-motion 全部直显;全部主题变量驱动换肤即变色
39
+ - 动画库与模板/主题正交,收纳在右侧边栏「⚙ 工具」Tab(工具栏 ⚙ 按钮或 G 键进入,应用范围 radio 全部页面/仅本页):翻页八套(slide-lr 左右滑移默认 / fade / slide-ud / zoom / flip 3D / cube 立方翻转 / wipe 平移擦除 / dissolve 溶解模糊,deck 标签 data-transition)即点即切;元素入场十二式(fade-up/fade-in/fade-down/zoom-in/slide-left/slide-right/slide-up/slide-down/pop/bounce-in/flip-in/blur + none,元素挂 data-anim + data-anim-delay 八档错落)翻到该页自动播放;离场十二式(同套离场方向,元素挂 data-anim-leave)翻页前元素先离场 0.45s 再切页;强调八式(pulse/swing/shake/breathe/flash/tada/spin/glow,元素挂 data-anim-emphasis 循环播放);标题文字动画四式(none 无动画默认 / char 逐字 / word 逐词 / line 逐行,运行时拆分标题文字);「开启」复选框一键全关,选择均 localStorage 记忆;prefers-reduced-motion 下全部退化为直切
40
+ - 结构顺序:背景装饰 → 顶部进度条 → 工具栏 → 页码 → 提示 → 左右箭头 → 右侧边栏(sp-tabs 双 Tab:▤ 页面 / ⚙ 工具)→ 舞台页组 → 说明弹层 → 播放引擎
31
41
  - 左右箭头不常驻:默认透明,鼠标在页面内才显现;触屏设备(hover:none)保持可见
32
42
  - 全屏放映纯净模式:`:root:fullscreen` 下工具栏/页码/提示/箭头/弹层与背景氛围层全部隐藏、页面黑底 `#000`(与 PPT 软件放映一致:幻灯按比例充满画面,比例不匹配处补黑边,绝无主题氛围边框),只留主体;热键全屏下照常生效
33
- - 截图与复制只含主体内容,绝不包含工具栏/页码/箭头等操作元素;复制分「⧉ 复制本页」(当前页文字)与「▦ 选页复制」(面板勾选任意页、默认全选=一键复制全部,按「【第 N 页】」分隔);截图分「📷 截图本页」与「▦ 选页截图」(面板勾选任意页、默认全选=串行逐张下载全部页 slide-页码-主题.png,进度实时显示已保存 k/N 页;文字通道不含图片,图片走截图)
43
+ - 截图与复制只含主体内容,绝不包含工具栏/页码/箭头等操作元素(按钮收纳在边栏 ⚙ 工具 Tab,热键 S/C 照常);复制分「⧉ 复制本页」(当前页文字)与「▦ 选页复制」(面板勾选任意页、默认全选=一键复制全部,按「【第 N 页】」分隔);截图分「📷 截图本页」与「▦ 选页截图」(面板勾选任意页、默认全选=串行逐张下载全部页 slide-页码-主题.png,进度实时显示已保存 k/N 页;文字通道不含图片,图片走截图)
34
44
  - 落盘命名三级优先:口头指定 > FILENAME=auto 按主题+用途总结中文名 > 写死固定名;重名加日期后缀不覆盖,目录复用不清空
35
45
  - 完整规范(骨架/主题色值/模板库/单页主题/播放引擎/热键表/截图实现)见 `resources/deck-spec.md`
36
46
 
37
47
  ## 🔧 配置版本机制(升级保护)
38
48
 
39
- `user-config.md` 首行有 `CONFIG_VERSION` 字段,声明配置结构版本。本 skill 当前要求版本为 1。每次执行前先读 user-config.md:
49
+ `user-config.md` 首行有 `CONFIG_VERSION` 字段,声明配置结构版本。本 skill 当前要求版本为 2。每次执行前先读 user-config.md:
40
50
 
41
51
  1. 若 CONFIG_VERSION 缺失或小于当前要求版本:先将旧 `user-config.md` 备份为 `user-config.md.bak-<时间戳>`
42
52
  2. 按版本迁移规则迁移字段(新增字段补默认值、改名字段按映射改名、删除字段保留在备份并在对话中提示用户)
43
53
  3. 更新 `CONFIG_VERSION` 为当前版本
44
54
  4. 对话告知用户「配置已升级到 vN:新增 X、改名 Y」
45
55
 
46
- 禁止在未声明版本变更的情况下静默修改配置语义;升级技能时 user-config.md 不会被覆盖(官方更新另存 `user-config.md.upstream` 对照文件)。
56
+ 禁止在未声明版本变更的情况下静默修改配置语义;
57
+
58
+ **无法迁移项处理**(值域失效 / 语义漂移 / 数据不兼容):三情形——①旧值不在新值域(如旧模板/主题名已移除)②同名键含义变化(单位/枚举变更)③数据文件 schema 不兼容。三绝不:绝不静默(无法迁移项停下问,不猜值)、绝不丢失(旧值永远在 .bak-时间戳)、一次说清(升级报告三段式:⚠️ 版本与备份路径 → ✅ 已自动迁移清单 → 🟡 无法迁移项逐条附原因+影响+建议替代);随后 AskUserQuestion 三选项(A 用建议值 / B 用户给新值 / C 保持旧配置——不更新 CONFIG_VERSION,每次执行再提醒);报告必附回滚一句话;数据文件迁移前先复制 .bak-时间戳,能映射则映射、不能则写兼容层或留备份提示手动整理
59
+
60
+ 升级技能时 user-config.md 不会被覆盖(官方更新另存 `user-config.md.upstream` 对照文件)。
47
61
 
48
62
  ## 📏 文档同步自检纪律(维护者必读)
49
63
 
@@ -0,0 +1,58 @@
1
+ # 内容适配手册(生成前选版式)
2
+
3
+ > 生成 HTML 前必读:按内容气质挑版式骨架、按领域挑成套模板、按用途定页数节奏、按数据挑图表形式。拿不准时用 AskUserQuestion 让用户在候选里挑,不替用户做风格决策。
4
+
5
+ ## 一、内容气质 → 版式骨架(十六套怎么挑)
6
+
7
+ | 内容类型 | 首选版式 | 备选 | 理由 |
8
+ |:---|:---|:---|:---|
9
+ | 产品发布 / 路演 | keynote 黑金发布 | classic 经典商务 | 大观点单页冲击力强,黑金适合发布场 |
10
+ | 数据汇报 / 财报 | finance 金融报表 | bento 模块网格 | 仪表环 + 数据卡原生就位 |
11
+ | 高管简报 | executive 高管简报 | minimal 极简留白 | 一页一结论,克制利落 |
12
+ | 教学培训 | education 教学板书 | steps 流程步骤 | 板书分栏 + 目标/讲解/练习三段 |
13
+ | 技术分享 / 架构 | blueprint 蓝图工程 | metro 扁平磁贴 | 工程感线条与图纸气质 |
14
+ | 时间线叙事 / 复盘 | timeline 时间线叙事 | vintage 复古报纸 | 节点卡片天然承载阶段演进 |
15
+ | 文艺 / 故事 / 品牌 | magazine 杂志画册 | poster 海报宣传 | 左文右图分栏 + 大幅留白 |
16
+ | 新闻通讯 / 资讯 | vintage 复古报纸 | classic 经典商务 | 双栏报纸排版信息密度高 |
17
+ | 活动海报 / 单页宣传 | poster 海报宣传 | magazine 杂志画册 | 要点转斜切色带,视觉先行 |
18
+ | 极简个人页 / 简历 | minimal 极简留白 | bento 模块网格 | 居中大字 + 要点列表 |
19
+ | 暗色氛围 / 发布会备选 | dark 暗夜深色 | keynote 黑金发布 | 深底高对比,适合屏幕放映 |
20
+ | 清新生活 / 公益 | nature 自然清新 | minimal 极简留白 | 绿色系温和感 |
21
+
22
+ ## 二、领域成套模板(二十一 套,六分类怎么挑)
23
+
24
+ 用户内容主题与某套高度契合时,优先推荐领域模板(一键生成整套:封面 + 目录 + 内容页 + 图表页 + 结束页,每套 6-8 页):
25
+
26
+ | 分类 | 模板 | 适用场景 |
27
+ |:---|:---|:---|
28
+ | 教育学术 | thesis 毕业答辩 / proposal 开题报告 / teaching 教学课件 | 学位答辩、课题申报、课堂 |
29
+ | 职场商务 | report 年终汇报 / competition 竞聘述职 / plan 商业计划书 / hiring 求职简历 | 述职、融资、求职 |
30
+ | 企业运营 | product 产品发布 / tech 科技发布会 / marketing 营销方案 / training 企业培训 / ceremony 年会盛典 | 发布会、培训、年会 |
31
+ | 专业领域 | finance 金融分析 / medical 医疗科普 / law 法律合规 | 行业分析、科普、合规宣贯 |
32
+ | 党政政务 | gov 政务汇报 / party 党建汇报 | 政府汇报、党建活动 |
33
+ | 生活场景 | food 餐饮品牌 / travel 旅游攻略 / festival 节日庆典 / charity 公益慈善 | 探店、攻略、节庆、公益 |
34
+
35
+ 生成后领域模板与版式 / 主题正交:每页仍可挂 data-template 运行时换十六版式、主题照常换肤,不是死骨架。
36
+
37
+ ## 三、页数与节奏
38
+
39
+ | 用途 | 建议页数 | 结构 |
40
+ |:---|:---|:---|
41
+ | 工作汇报 / 述职 | 6-8 页 | 封面 1 + 目录 1 + 正文 3-4 + 图表 1 + 结束 1 |
42
+ | 教学培训 | 8-10 页 | 封面 + 目标 + 讲解 4-6 + 练习 + 小结 |
43
+ | 路演 / 发布会 | 8-12 页 | 封面 + 痛点 + 方案 3-5 + 数据 + 里程碑 + 结束 |
44
+ | 单页海报 | 1 页 | 大字标题 + 3-5 条要点 + 落款 |
45
+
46
+ **页内纪律**:单页要点 ≤ 5 条;单页数据指标 ≤ 3 个;页标题 ≤ 12 字;超量自动拆页,禁止一页塞满。
47
+
48
+ ## 四、图表选型(挂 data-anim-chart)
49
+
50
+ | 数据形态 | 图表 | 容器挂法 |
51
+ |:---|:---|:---|
52
+ | 趋势 / 变化 | line 折线描画 | `data-anim-chart="line"` |
53
+ | 占比 / 构成 | pie 饼图扇入 | `data-anim-chart="pie"` |
54
+ | 对比 / 排名 | bars 柱状生长 | `data-anim-chart="bars"` |
55
+ | 完成率 / 进度 | ring 仪表扫弧 | `data-anim-chart="ring"` |
56
+ | 大数字 / 里程碑 | count 数字滚动 | `data-count="128"`(可带 `data-count-dec`) |
57
+
58
+ 数值纪律:业务结果指标优先、git 统计兜底;数字必带对比基准(同比 / 环比)。