@thanh01.pmt/presentation-kit 0.1.1 → 0.2.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.
- package/dist/{chunk-RCHOTRWI.js → chunk-PAHIW5IX.js} +906 -16
- package/dist/chunk-PAHIW5IX.js.map +1 -0
- package/dist/{chunk-N4Q6GUAQ.cjs → chunk-RJMPTRL3.cjs} +920 -15
- package/dist/chunk-RJMPTRL3.cjs.map +1 -0
- package/dist/{click-css-CadkoJ_A.d.cts → click-css-C84mdmU0.d.cts} +37 -8
- package/dist/{click-css-CadkoJ_A.d.ts → click-css-C84mdmU0.d.ts} +37 -8
- package/dist/index.cjs +244 -28
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +87 -3
- package/dist/index.d.ts +87 -3
- package/dist/index.js +172 -3
- package/dist/index.js.map +1 -1
- package/dist/react/index.cjs +726 -21
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.d.cts +50 -6
- package/dist/react/index.d.ts +50 -6
- package/dist/react/index.js +716 -13
- package/dist/react/index.js.map +1 -1
- package/package.json +11 -3
- package/dist/chunk-N4Q6GUAQ.cjs.map +0 -1
- package/dist/chunk-RCHOTRWI.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { resolveTheme, generateScopedCss2, renderSlideAsync, renderSlide } from './chunk-
|
|
2
|
-
export { convertTextArrows, countSlides, darkThemeTokens, defineTheme, detectMermaidBlocks, extractMarpSource, extractMaxClicks, generateClickCss, generateScopedCss2 as generateScopedCss, generateScopedCss as generateScopedCssFromTokens, getHighlighter, getThemeTokens, highlightCodeBlocks, lightThemeTokens, navyThemeTokens, postprocessClicks, preprocessClicks, renderMarp, renderMermaidDiagrams, renderSlide, renderSlideAsync, resolveTheme, sanitizeMermaidCode } from './chunk-
|
|
1
|
+
import { resolveTheme, generateScopedCss2, renderSlideAsync, renderSlide, splitMarkdownSlides } from './chunk-PAHIW5IX.js';
|
|
2
|
+
export { SLIDE_LAYOUT_PRESETS, convertTextArrows, countSlides, darkThemeTokens, defineTheme, detectMermaidBlocks, extractLayoutsAndNotes, extractMarpSource, extractMaxClicks, filterPresenterNotes, generateClickCss, generateLayoutCss, generateScopedCss2 as generateScopedCss, generateScopedCss as generateScopedCssFromTokens, getHighlighter, getSlideLayoutPresetById, getSlideLayoutPresetsByCategory, getThemeTokens, highlightCodeBlocks, isLayoutComment, lightThemeTokens, navyThemeTokens, parseLayoutFromComment, postprocessClicks, preprocessClicks, renderMarp, renderMermaidDiagrams, renderSlide, renderSlideAsync, resolveTheme, sanitizeMermaidCode, serializeLayoutToComment, splitMarkdownSlides, updateSlideLayoutInMarkdown } from './chunk-PAHIW5IX.js';
|
|
3
3
|
|
|
4
4
|
// src/engine.ts
|
|
5
5
|
function createEngine(config = {}) {
|
|
@@ -28,6 +28,175 @@ function createEngine(config = {}) {
|
|
|
28
28
|
};
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
export
|
|
31
|
+
// src/core/export/pptx-converter.ts
|
|
32
|
+
function parseFontSizeToPt(sizeStr, defaultPt = 18) {
|
|
33
|
+
if (!sizeStr) return defaultPt;
|
|
34
|
+
const trimmed = sizeStr.trim().toLowerCase();
|
|
35
|
+
if (trimmed.endsWith("rem")) {
|
|
36
|
+
const rem = parseFloat(trimmed);
|
|
37
|
+
return Math.round(rem * 16 * 0.75) || defaultPt;
|
|
38
|
+
}
|
|
39
|
+
if (trimmed.endsWith("px")) {
|
|
40
|
+
const px = parseFloat(trimmed);
|
|
41
|
+
return Math.round(px * 0.75) || defaultPt;
|
|
42
|
+
}
|
|
43
|
+
if (trimmed.endsWith("pt")) {
|
|
44
|
+
return parseInt(trimmed, 10) || defaultPt;
|
|
45
|
+
}
|
|
46
|
+
const num = parseFloat(trimmed);
|
|
47
|
+
return !isNaN(num) ? Math.round(num) : defaultPt;
|
|
48
|
+
}
|
|
49
|
+
function cleanHexColor(colorStr, defaultHex = "1F2937") {
|
|
50
|
+
if (!colorStr) return defaultHex;
|
|
51
|
+
const hex = colorStr.trim().replace(/^#/, "");
|
|
52
|
+
if (/^[0-9A-Fa-f]{6}$/.test(hex)) return hex.toUpperCase();
|
|
53
|
+
if (/^[0-9A-Fa-f]{3}$/.test(hex)) {
|
|
54
|
+
return hex.split("").map((c) => c + c).join("").toUpperCase();
|
|
55
|
+
}
|
|
56
|
+
return defaultHex;
|
|
57
|
+
}
|
|
58
|
+
function extractSlideBgColor(slideMd, defaultColor) {
|
|
59
|
+
const match = slideMd.match(/<!--\s*_backgroundColor:\s*([^\s>]+)\s*-->/i);
|
|
60
|
+
if (match && match[1]) {
|
|
61
|
+
return cleanHexColor(match[1], defaultColor);
|
|
62
|
+
}
|
|
63
|
+
return defaultColor;
|
|
64
|
+
}
|
|
65
|
+
function parseSimpleMarkdown(md) {
|
|
66
|
+
const lines = md.split("\n");
|
|
67
|
+
const blocks = [];
|
|
68
|
+
for (const line of lines) {
|
|
69
|
+
const trimmed = line.trim();
|
|
70
|
+
if (!trimmed) continue;
|
|
71
|
+
if (trimmed.startsWith("<!--") && trimmed.endsWith("-->")) continue;
|
|
72
|
+
const hMatch = trimmed.match(/^(#{1,6})\s+(.*)$/);
|
|
73
|
+
if (hMatch && hMatch[1] && hMatch[2]) {
|
|
74
|
+
blocks.push({
|
|
75
|
+
type: "heading",
|
|
76
|
+
level: hMatch[1].length,
|
|
77
|
+
text: hMatch[2].replace(/\*\*(.*?)\*\*/g, "$1").replace(/\*(.*?)\*/g, "$1")
|
|
78
|
+
});
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
if (/^[-*+]\s+/.test(trimmed)) {
|
|
82
|
+
blocks.push({
|
|
83
|
+
type: "bullet",
|
|
84
|
+
text: trimmed.replace(/^[-*+]\s+/, "").replace(/\*\*(.*?)\*\*/g, "$1")
|
|
85
|
+
});
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
blocks.push({
|
|
89
|
+
type: "paragraph",
|
|
90
|
+
text: trimmed.replace(/\*\*(.*?)\*\*/g, "$1").replace(/\*(.*?)\*/g, "$1")
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
return blocks;
|
|
94
|
+
}
|
|
95
|
+
async function convertMarkdownToPptx(markdown, options) {
|
|
96
|
+
const PptxGenModule = await import('pptxgenjs');
|
|
97
|
+
const PptxGenClass = PptxGenModule.default || PptxGenModule;
|
|
98
|
+
const pptx = new PptxGenClass();
|
|
99
|
+
pptx.layout = "LAYOUT_WIDE";
|
|
100
|
+
if (options?.title) pptx.title = options.title;
|
|
101
|
+
if (options?.author) pptx.author = options.author;
|
|
102
|
+
const renderResult = renderSlide(markdown);
|
|
103
|
+
const { slides } = splitMarkdownSlides(markdown);
|
|
104
|
+
const isDark = options?.theme !== "light";
|
|
105
|
+
const defaultBg = isDark ? "14151B" : "FFFFFF";
|
|
106
|
+
const defaultText = isDark ? "F1F5F9" : "1E293B";
|
|
107
|
+
const defaultHeading = isDark ? "38BDF8" : "0284C7";
|
|
108
|
+
const scaleX = 13.33 / 1280;
|
|
109
|
+
const scaleY = 7.5 / 720;
|
|
110
|
+
slides.forEach((slideMd, slideIndex) => {
|
|
111
|
+
const pptxSlide = pptx.addSlide();
|
|
112
|
+
const bgColor = extractSlideBgColor(slideMd, defaultBg);
|
|
113
|
+
pptxSlide.background = { color: bgColor };
|
|
114
|
+
const notes = renderResult.notes?.[slideIndex];
|
|
115
|
+
if (notes && notes.length > 0) {
|
|
116
|
+
pptxSlide.addNotes(notes.join("\n\n"));
|
|
117
|
+
}
|
|
118
|
+
const layout = renderResult.layouts?.[slideIndex];
|
|
119
|
+
const blocks = parseSimpleMarkdown(slideMd);
|
|
120
|
+
if (layout && layout.boxes && layout.boxes.length > 0) {
|
|
121
|
+
for (const box of layout.boxes) {
|
|
122
|
+
const [x, y, w, h] = box.pos;
|
|
123
|
+
const xInch = Math.max(0, x * scaleX);
|
|
124
|
+
const yInch = Math.max(0, y * scaleY);
|
|
125
|
+
const wInch = Math.min(13.33 - xInch, w * scaleX);
|
|
126
|
+
const hInch = Math.min(7.5 - yInch, h * scaleY);
|
|
127
|
+
let contentText = "";
|
|
128
|
+
if (box.target === "h1") {
|
|
129
|
+
contentText = blocks.find((b) => b.type === "heading" && b.level === 1)?.text || "Heading";
|
|
130
|
+
} else if (box.target === "h2") {
|
|
131
|
+
contentText = blocks.find((b) => b.type === "heading" && b.level === 2)?.text || "Subheading";
|
|
132
|
+
} else {
|
|
133
|
+
contentText = blocks.find((b) => b.type === "paragraph" || b.type === "bullet")?.text || "Content block";
|
|
134
|
+
}
|
|
135
|
+
const fontSizePt = parseFontSizeToPt(box.fontSize, box.target === "h1" ? 28 : 16);
|
|
136
|
+
const colorHex = cleanHexColor(box.color, box.target === "h1" ? defaultHeading : defaultText);
|
|
137
|
+
pptxSlide.addText(contentText, {
|
|
138
|
+
x: xInch,
|
|
139
|
+
y: yInch,
|
|
140
|
+
w: wInch,
|
|
141
|
+
h: hInch,
|
|
142
|
+
fontSize: fontSizePt,
|
|
143
|
+
color: colorHex,
|
|
144
|
+
align: box.textAlign ?? "left",
|
|
145
|
+
bold: box.target?.startsWith("h") ?? false,
|
|
146
|
+
fontFace: "Arial"
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
} else {
|
|
150
|
+
let currentY = 0.8;
|
|
151
|
+
const marginX = 1;
|
|
152
|
+
const contentWidth = 11.33;
|
|
153
|
+
for (const block of blocks) {
|
|
154
|
+
if (block.type === "heading") {
|
|
155
|
+
const fontSize = block.level === 1 ? 30 : block.level === 2 ? 22 : 18;
|
|
156
|
+
const color = block.level === 1 ? defaultHeading : defaultText;
|
|
157
|
+
pptxSlide.addText(block.text, {
|
|
158
|
+
x: marginX,
|
|
159
|
+
y: currentY,
|
|
160
|
+
w: contentWidth,
|
|
161
|
+
h: 0.8,
|
|
162
|
+
fontSize,
|
|
163
|
+
color,
|
|
164
|
+
bold: true,
|
|
165
|
+
fontFace: "Arial"
|
|
166
|
+
});
|
|
167
|
+
currentY += 0.9;
|
|
168
|
+
} else if (block.type === "bullet") {
|
|
169
|
+
pptxSlide.addText(block.text, {
|
|
170
|
+
x: marginX + 0.3,
|
|
171
|
+
y: currentY,
|
|
172
|
+
w: contentWidth - 0.3,
|
|
173
|
+
h: 0.45,
|
|
174
|
+
fontSize: 15,
|
|
175
|
+
color: defaultText,
|
|
176
|
+
bullet: true,
|
|
177
|
+
fontFace: "Arial"
|
|
178
|
+
});
|
|
179
|
+
currentY += 0.5;
|
|
180
|
+
} else {
|
|
181
|
+
pptxSlide.addText(block.text, {
|
|
182
|
+
x: marginX,
|
|
183
|
+
y: currentY,
|
|
184
|
+
w: contentWidth,
|
|
185
|
+
h: 0.5,
|
|
186
|
+
fontSize: 15,
|
|
187
|
+
color: defaultText,
|
|
188
|
+
fontFace: "Arial"
|
|
189
|
+
});
|
|
190
|
+
currentY += 0.55;
|
|
191
|
+
}
|
|
192
|
+
if (currentY > 6.5) break;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
});
|
|
196
|
+
const pptxArray = await pptx.write({ outputType: "arraybuffer" });
|
|
197
|
+
return new Uint8Array(pptxArray);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
export { cleanHexColor, convertMarkdownToPptx, createEngine, parseFontSizeToPt };
|
|
32
201
|
//# sourceMappingURL=index.js.map
|
|
33
202
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/engine.ts"],"names":["generateScopedCss"],"mappings":";;;;AAyBO,SAAS,YAAA,CAAa,MAAA,GAA4B,EAAC,EAAgB;AACxE,EAAA,MAAM,YAAA,GAAe,OAAO,KAAA,IAAS,MAAA;AACrC,EAAA,MAAM,kBAAA,GAAqB,OAAO,WAAA,IAAe,IAAA;AACjD,EAAA,MAAM,oBAAA,GAAuB,OAAO,aAAA,IAAiB,IAAA;AAErD,EAAA,SAAS,aAAa,OAAA,EAAkD;AACtE,IAAA,OAAO;AAAA,MACL,KAAA,EAAO,SAAS,KAAA,IAAS,YAAA;AAAA,MACzB,WAAA,EAAa,SAAS,WAAA,IAAe,kBAAA;AAAA,MACrC,aAAA,EAAe,SAAS,aAAA,IAAiB,oBAAA;AAAA,MACzC,aAAa,OAAA,EAAS;AAAA,KACxB;AAAA,EACF;AAEA,EAAA,OAAO;AAAA,IACL,MAAA,EAAQ,CAAC,QAAA,EAAkB,OAAA,KAAoD;AAC7E,MAAA,OAAO,WAAA,CAAY,QAAA,EAAU,YAAA,CAAa,OAAO,CAAC,CAAA;AAAA,IACpD,CAAA;AAAA,IAEA,WAAA,EAAa,OAAO,QAAA,EAAkB,OAAA,KAAkE;AACtG,MAAA,OAAO,gBAAA,CAAiB,QAAA,EAAU,YAAA,CAAa,OAAO,CAAC,CAAA;AAAA,IACzD,CAAA;AAAA,IAEA,WAAA,EAAa,CAAC,KAAA,EAAkC,WAAA,KAAiC;AAC/E,MAAA,MAAM,QAAA,GAAW,aAAa,KAAK,CAAA;AACnC,MAAA,OAAOA,kBAAAA,CAAkB,UAAU,WAAW,CAAA;AAAA,IAChD;AAAA,GACF;AACF","file":"index.js","sourcesContent":["// ═══════════════════════════════════════════════════════════════════════════════\n// Engine Factory — createEngine() for advanced usage\n// ═══════════════════════════════════════════════════════════════════════════════\n\nimport type { SlideEngine, SlideEngineConfig, SlideRenderOptions, SlideRenderResult, SlideRenderAsyncResult, BuiltInTheme, SlideTheme } from './types.js';\nimport { renderSlide, renderSlideAsync } from './core/render.js';\nimport { resolveTheme, generateScopedCss } from './themes/index.js';\n\n/**\n * Create a slide engine instance with default configuration.\n *\n * @example\n * ```ts\n * const engine = createEngine({ theme: 'dark' });\n *\n * // Synchronous\n * const result = engine.render(markdown);\n *\n * // Async (with Shiki highlighting)\n * const fullResult = await engine.renderAsync(markdown);\n *\n * // Get theme CSS independently\n * const css = engine.getThemeCss('navy');\n * ```\n */\nexport function createEngine(config: SlideEngineConfig = {}): SlideEngine {\n const defaultTheme = config.theme ?? 'dark';\n const defaultEnableShiki = config.enableShiki ?? true;\n const defaultEnableMermaid = config.enableMermaid ?? true;\n\n function mergeOptions(options?: SlideRenderOptions): SlideRenderOptions {\n return {\n theme: options?.theme ?? defaultTheme,\n enableShiki: options?.enableShiki ?? defaultEnableShiki,\n enableMermaid: options?.enableMermaid ?? defaultEnableMermaid,\n containerId: options?.containerId,\n };\n }\n\n return {\n render: (markdown: string, options?: SlideRenderOptions): SlideRenderResult => {\n return renderSlide(markdown, mergeOptions(options));\n },\n\n renderAsync: async (markdown: string, options?: SlideRenderOptions): Promise<SlideRenderAsyncResult> => {\n return renderSlideAsync(markdown, mergeOptions(options));\n },\n\n getThemeCss: (theme: BuiltInTheme | SlideTheme, containerId?: string): string => {\n const resolved = resolveTheme(theme);\n return generateScopedCss(resolved, containerId);\n },\n };\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/engine.ts","../src/core/export/pptx-converter.ts"],"names":["generateScopedCss"],"mappings":"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","file":"index.js","sourcesContent":["// ═══════════════════════════════════════════════════════════════════════════════\n// Engine Factory — createEngine() for advanced usage\n// ═══════════════════════════════════════════════════════════════════════════════\n\nimport type { SlideEngine, SlideEngineConfig, SlideRenderOptions, SlideRenderResult, SlideRenderAsyncResult, BuiltInTheme, SlideTheme } from './types.js';\nimport { renderSlide, renderSlideAsync } from './core/render.js';\nimport { resolveTheme, generateScopedCss } from './themes/index.js';\n\n/**\n * Create a slide engine instance with default configuration.\n *\n * @example\n * ```ts\n * const engine = createEngine({ theme: 'dark' });\n *\n * // Synchronous\n * const result = engine.render(markdown);\n *\n * // Async (with Shiki highlighting)\n * const fullResult = await engine.renderAsync(markdown);\n *\n * // Get theme CSS independently\n * const css = engine.getThemeCss('navy');\n * ```\n */\nexport function createEngine(config: SlideEngineConfig = {}): SlideEngine {\n const defaultTheme = config.theme ?? 'dark';\n const defaultEnableShiki = config.enableShiki ?? true;\n const defaultEnableMermaid = config.enableMermaid ?? true;\n\n function mergeOptions(options?: SlideRenderOptions): SlideRenderOptions {\n return {\n theme: options?.theme ?? defaultTheme,\n enableShiki: options?.enableShiki ?? defaultEnableShiki,\n enableMermaid: options?.enableMermaid ?? defaultEnableMermaid,\n containerId: options?.containerId,\n };\n }\n\n return {\n render: (markdown: string, options?: SlideRenderOptions): SlideRenderResult => {\n return renderSlide(markdown, mergeOptions(options));\n },\n\n renderAsync: async (markdown: string, options?: SlideRenderOptions): Promise<SlideRenderAsyncResult> => {\n return renderSlideAsync(markdown, mergeOptions(options));\n },\n\n getThemeCss: (theme: BuiltInTheme | SlideTheme, containerId?: string): string => {\n const resolved = resolveTheme(theme);\n return generateScopedCss(resolved, containerId);\n },\n };\n}\n","// ═══════════════════════════════════════════════════════════════════════════════\n// pptx-converter.ts — Pure JS converter: Marp Markdown + Layouts → Native PPTX\n//\n// Generates native, fully editable PowerPoint textboxes and shapes\n// that open directly in PowerPoint, Keynote, and Google Slides.\n// ═══════════════════════════════════════════════════════════════════════════════\n\nimport { renderSlide } from '../render.js';\nimport { splitMarkdownSlides } from '../layout/serializer.js';\nimport type { LayoutBoxConfig } from '../../types.js';\n\nexport interface PptxExportOptions {\n title?: string;\n author?: string;\n theme?: 'dark' | 'light' | 'navy';\n}\n\n/**\n * Convert font size string ('2.5rem', '32px', '24pt') to pt number for PPTX\n */\nexport function parseFontSizeToPt(sizeStr?: string, defaultPt: number = 18): number {\n if (!sizeStr) return defaultPt;\n const trimmed = sizeStr.trim().toLowerCase();\n\n if (trimmed.endsWith('rem')) {\n const rem = parseFloat(trimmed);\n return Math.round(rem * 16 * 0.75) || defaultPt;\n }\n if (trimmed.endsWith('px')) {\n const px = parseFloat(trimmed);\n return Math.round(px * 0.75) || defaultPt;\n }\n if (trimmed.endsWith('pt')) {\n return parseInt(trimmed, 10) || defaultPt;\n }\n const num = parseFloat(trimmed);\n return !isNaN(num) ? Math.round(num) : defaultPt;\n}\n\n/**\n * Clean hex color string (removes leading #)\n */\nexport function cleanHexColor(colorStr?: string, defaultHex: string = '1F2937'): string {\n if (!colorStr) return defaultHex;\n const hex = colorStr.trim().replace(/^#/, '');\n if (/^[0-9A-Fa-f]{6}$/.test(hex)) return hex.toUpperCase();\n if (/^[0-9A-Fa-f]{3}$/.test(hex)) {\n return hex.split('').map(c => c + c).join('').toUpperCase();\n }\n return defaultHex;\n}\n\n/**\n * Extract slide-level background color directive from slide markdown\n */\nfunction extractSlideBgColor(slideMd: string, defaultColor: string): string {\n const match = slideMd.match(/<!--\\s*_backgroundColor:\\s*([^\\s>]+)\\s*-->/i);\n if (match && match[1]) {\n return cleanHexColor(match[1], defaultColor);\n }\n return defaultColor;\n}\n\n/**\n * Parse plain markdown lines into simple text items\n */\ninterface SimpleBlock {\n type: 'heading' | 'paragraph' | 'bullet' | 'code';\n level?: number;\n text: string;\n}\n\nfunction parseSimpleMarkdown(md: string): SimpleBlock[] {\n const lines = md.split('\\n');\n const blocks: SimpleBlock[] = [];\n\n for (const line of lines) {\n const trimmed = line.trim();\n if (!trimmed) continue;\n if (trimmed.startsWith('<!--') && trimmed.endsWith('-->')) continue;\n\n // Heading\n const hMatch = trimmed.match(/^(#{1,6})\\s+(.*)$/);\n if (hMatch && hMatch[1] && hMatch[2]) {\n blocks.push({\n type: 'heading',\n level: hMatch[1].length,\n text: hMatch[2].replace(/\\*\\*(.*?)\\*\\*/g, '$1').replace(/\\*(.*?)\\*/g, '$1'),\n });\n continue;\n }\n\n // Bullet\n if (/^[-*+]\\s+/.test(trimmed)) {\n blocks.push({\n type: 'bullet',\n text: trimmed.replace(/^[-*+]\\s+/, '').replace(/\\*\\*(.*?)\\*\\*/g, '$1'),\n });\n continue;\n }\n\n // Paragraph\n blocks.push({\n type: 'paragraph',\n text: trimmed.replace(/\\*\\*(.*?)\\*\\*/g, '$1').replace(/\\*(.*?)\\*/g, '$1'),\n });\n }\n\n return blocks;\n}\n\n/**\n * Convert Marp Markdown presentation to an editable PPTX file using pptxgenjs.\n *\n * Each box in `layout.boxes` maps directly from 1280x720 canvas px to 13.33 x 7.5 inch PPTX coordinates.\n */\nexport async function convertMarkdownToPptx(\n markdown: string,\n options?: PptxExportOptions\n): Promise<Uint8Array> {\n const PptxGenModule = await import('pptxgenjs');\n const PptxGenClass = (PptxGenModule as any).default || PptxGenModule;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const pptx = new (PptxGenClass as any)();\n\n pptx.layout = 'LAYOUT_WIDE'; // 16:9 aspect ratio (13.33 x 7.5 inches)\n if (options?.title) pptx.title = options.title;\n if (options?.author) pptx.author = options.author;\n\n const renderResult = renderSlide(markdown);\n const { slides } = splitMarkdownSlides(markdown);\n\n const isDark = options?.theme !== 'light';\n const defaultBg = isDark ? '14151B' : 'FFFFFF';\n const defaultText = isDark ? 'F1F5F9' : '1E293B';\n const defaultHeading = isDark ? '38BDF8' : '0284C7';\n\n // Coordinate scales from 1280x720 to 13.33x7.5 inches\n const scaleX = 13.33 / 1280;\n const scaleY = 7.5 / 720;\n\n slides.forEach((slideMd, slideIndex) => {\n const pptxSlide = pptx.addSlide();\n\n // 1. Set background\n const bgColor = extractSlideBgColor(slideMd, defaultBg);\n pptxSlide.background = { color: bgColor };\n\n // 2. Add Speaker / Teacher Notes\n const notes = renderResult.notes?.[slideIndex];\n if (notes && notes.length > 0) {\n pptxSlide.addNotes(notes.join('\\n\\n'));\n }\n\n // 3. Layout boxes\n const layout = renderResult.layouts?.[slideIndex];\n const blocks = parseSimpleMarkdown(slideMd);\n\n if (layout && layout.boxes && layout.boxes.length > 0) {\n // Freeform layout: Render boxes with exact pos\n for (const box of layout.boxes) {\n const [x, y, w, h] = box.pos;\n const xInch = Math.max(0, x * scaleX);\n const yInch = Math.max(0, y * scaleY);\n const wInch = Math.min(13.33 - xInch, w * scaleX);\n const hInch = Math.min(7.5 - yInch, h * scaleY);\n\n // Find corresponding text\n let contentText = '';\n if (box.target === 'h1') {\n contentText = blocks.find(b => b.type === 'heading' && b.level === 1)?.text || 'Heading';\n } else if (box.target === 'h2') {\n contentText = blocks.find(b => b.type === 'heading' && b.level === 2)?.text || 'Subheading';\n } else {\n contentText = blocks.find(b => b.type === 'paragraph' || b.type === 'bullet')?.text || 'Content block';\n }\n\n const fontSizePt = parseFontSizeToPt(box.fontSize, box.target === 'h1' ? 28 : 16);\n const colorHex = cleanHexColor(box.color, box.target === 'h1' ? defaultHeading : defaultText);\n\n pptxSlide.addText(contentText, {\n x: xInch,\n y: yInch,\n w: wInch,\n h: hInch,\n fontSize: fontSizePt,\n color: colorHex,\n align: box.textAlign ?? 'left',\n bold: box.target?.startsWith('h') ?? false,\n fontFace: 'Arial',\n });\n }\n } else {\n // Flow layout fallback: Stack headings and paragraphs nicely\n let currentY = 0.8;\n const marginX = 1.0;\n const contentWidth = 11.33;\n\n for (const block of blocks) {\n if (block.type === 'heading') {\n const fontSize = block.level === 1 ? 30 : block.level === 2 ? 22 : 18;\n const color = block.level === 1 ? defaultHeading : defaultText;\n\n pptxSlide.addText(block.text, {\n x: marginX,\n y: currentY,\n w: contentWidth,\n h: 0.8,\n fontSize,\n color,\n bold: true,\n fontFace: 'Arial',\n });\n currentY += 0.9;\n } else if (block.type === 'bullet') {\n pptxSlide.addText(block.text, {\n x: marginX + 0.3,\n y: currentY,\n w: contentWidth - 0.3,\n h: 0.45,\n fontSize: 15,\n color: defaultText,\n bullet: true,\n fontFace: 'Arial',\n });\n currentY += 0.5;\n } else {\n pptxSlide.addText(block.text, {\n x: marginX,\n y: currentY,\n w: contentWidth,\n h: 0.5,\n fontSize: 15,\n color: defaultText,\n fontFace: 'Arial',\n });\n currentY += 0.55;\n }\n\n if (currentY > 6.5) break; // Prevent vertical overflow off slide\n }\n }\n });\n\n const pptxArray = (await pptx.write({ outputType: 'arraybuffer' })) as ArrayBuffer;\n return new Uint8Array(pptxArray);\n}\n"]}
|