mulmocast 2.1.40 → 2.2.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.
- package/README.md +1 -1
- package/lib/actions/image_agents.d.ts +2 -1
- package/lib/actions/image_agents.js +3 -3
- package/lib/actions/images.d.ts +3 -1
- package/lib/actions/images.js +1 -0
- package/lib/methods/mulmo_media_source.d.ts +1 -0
- package/lib/methods/mulmo_media_source.js +17 -3
- package/lib/slide/blocks.d.ts +2 -0
- package/lib/slide/blocks.js +54 -2
- package/lib/slide/index.d.ts +2 -2
- package/lib/slide/index.js +1 -1
- package/lib/slide/layouts/columns.js +4 -4
- package/lib/slide/layouts/comparison.js +4 -3
- package/lib/slide/layouts/grid.js +3 -3
- package/lib/slide/render.js +24 -1
- package/lib/slide/schema.d.ts +454 -0
- package/lib/slide/schema.js +19 -0
- package/lib/slide/utils.d.ts +12 -1
- package/lib/slide/utils.js +55 -2
- package/lib/types/schema.d.ts +448 -0
- package/lib/types/slide.d.ts +454 -0
- package/lib/types/slide.js +19 -0
- package/lib/types/type.d.ts +1 -0
- package/lib/utils/context.d.ts +182 -0
- package/lib/utils/html_render.js +44 -6
- package/lib/utils/image_plugins/slide.d.ts +14 -0
- package/lib/utils/image_plugins/slide.js +101 -2
- package/package.json +1 -1
- package/scripts/test/img_detector.png +0 -0
- package/scripts/test/img_higgs.png +0 -0
- package/scripts/test/img_lhc.png +0 -0
- package/scripts/test/test_slide_chart_mermaid.json +148 -0
- package/scripts/test/test_slide_image_ref.json +261 -0
- package/scripts/test/test_slide_image_ref_en.json +287 -0
package/README.md
CHANGED
|
@@ -350,7 +350,7 @@ MulmoCast includes a powerful **Slide DSL** (`type: "slide"`) for creating struc
|
|
|
350
350
|
### Features
|
|
351
351
|
|
|
352
352
|
- **11 Layouts**: title, columns, comparison, grid, bigQuote, stats, timeline, split, matrix, table, funnel
|
|
353
|
-
- **
|
|
353
|
+
- **10 Content Block Types**: text, bullets, code, callout, metric, divider, image, imageRef, chart, mermaid
|
|
354
354
|
- **13-Color Theme System**: Semantic color palette with dark/light support
|
|
355
355
|
- **6 Preset Themes**: dark, pop, warm, creative, minimal, corporate
|
|
356
356
|
|
|
@@ -52,12 +52,13 @@ export declare const imagePreprocessAgent: (namedInputs: {
|
|
|
52
52
|
context: MulmoStudioContext;
|
|
53
53
|
beat: MulmoBeat;
|
|
54
54
|
index: number;
|
|
55
|
-
imageRefs
|
|
55
|
+
imageRefs?: Record<string, string>;
|
|
56
56
|
}) => Promise<ImagePreprocessAgentResponse>;
|
|
57
57
|
export declare const imagePluginAgent: (namedInputs: {
|
|
58
58
|
context: MulmoStudioContext;
|
|
59
59
|
beat: MulmoBeat;
|
|
60
60
|
index: number;
|
|
61
|
+
imageRefs?: Record<string, string>;
|
|
61
62
|
}) => Promise<void>;
|
|
62
63
|
export declare const htmlImageGeneratorAgent: (namedInputs: {
|
|
63
64
|
file: string;
|
|
@@ -88,18 +88,18 @@ export const imagePreprocessAgent = async (namedInputs) => {
|
|
|
88
88
|
return { ...returnValue, imagePath, imageFromMovie: true }; // no image prompt, only movie prompt
|
|
89
89
|
}
|
|
90
90
|
// referenceImages for "edit_image", openai agent.
|
|
91
|
-
const referenceImages = MulmoBeatMethods.getImageReferenceForImageGenerator(beat, imageRefs);
|
|
91
|
+
const referenceImages = MulmoBeatMethods.getImageReferenceForImageGenerator(beat, imageRefs ?? {});
|
|
92
92
|
const prompt = imagePrompt(beat, imageAgentInfo.imageParams.style);
|
|
93
93
|
// ImageGenearalPreprocessAgentResponse
|
|
94
94
|
return { ...returnValue, imagePath, referenceImageForMovie: imagePath, imageAgentInfo, prompt, referenceImages };
|
|
95
95
|
};
|
|
96
96
|
export const imagePluginAgent = async (namedInputs) => {
|
|
97
|
-
const { context, beat, index } = namedInputs;
|
|
97
|
+
const { context, beat, index, imageRefs } = namedInputs;
|
|
98
98
|
const { imagePath } = getBeatPngImagePath(context, index);
|
|
99
99
|
const plugin = MulmoBeatMethods.getPlugin(beat);
|
|
100
100
|
try {
|
|
101
101
|
MulmoStudioContextMethods.setBeatSessionState(context, "image", index, beat.id, true);
|
|
102
|
-
const processorParams = { beat, context, imagePath, ...htmlStyle(context, beat) };
|
|
102
|
+
const processorParams = { beat, context, imagePath, imageRefs, ...htmlStyle(context, beat) };
|
|
103
103
|
await plugin.process(processorParams);
|
|
104
104
|
MulmoStudioContextMethods.setBeatSessionState(context, "image", index, beat.id, false);
|
|
105
105
|
}
|
package/lib/actions/images.d.ts
CHANGED
|
@@ -29,7 +29,7 @@ export declare const beat_graph_data: {
|
|
|
29
29
|
context: MulmoStudioContext;
|
|
30
30
|
beat: import("../types/type.js").MulmoBeat;
|
|
31
31
|
index: number;
|
|
32
|
-
imageRefs
|
|
32
|
+
imageRefs?: Record<string, string>;
|
|
33
33
|
}) => Promise<({
|
|
34
34
|
imageParams?: MulmoImageParams;
|
|
35
35
|
movieFile?: string;
|
|
@@ -162,11 +162,13 @@ export declare const beat_graph_data: {
|
|
|
162
162
|
context: MulmoStudioContext;
|
|
163
163
|
beat: import("../types/type.js").MulmoBeat;
|
|
164
164
|
index: number;
|
|
165
|
+
imageRefs?: Record<string, string>;
|
|
165
166
|
}) => Promise<void>;
|
|
166
167
|
inputs: {
|
|
167
168
|
context: string;
|
|
168
169
|
beat: string;
|
|
169
170
|
index: string;
|
|
171
|
+
imageRefs: string;
|
|
170
172
|
onComplete: string[];
|
|
171
173
|
};
|
|
172
174
|
};
|
package/lib/actions/images.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { MulmoMediaSource, MulmoMediaMermaidSource, MulmoStudioContext, ImageType } from "../types/index.js";
|
|
2
2
|
export declare const getExtention: (contentType: string | null, url: string) => string;
|
|
3
|
+
export declare const pathToDataUrl: (filePath: string) => string;
|
|
3
4
|
export declare const MulmoMediaSourceMethods: {
|
|
4
5
|
getText(mediaSource: MulmoMediaMermaidSource, context: MulmoStudioContext): Promise<string | null>;
|
|
5
6
|
resolve(mediaSource: MulmoMediaSource | undefined, context: MulmoStudioContext): string | null;
|
|
@@ -60,12 +60,26 @@ const urlToDataUrl = async (url, timeoutMs = DEFAULT_FETCH_TIMEOUT_MS) => {
|
|
|
60
60
|
clearTimeout(timeoutId);
|
|
61
61
|
}
|
|
62
62
|
};
|
|
63
|
+
/** Map file extension to MIME type for data URLs */
|
|
64
|
+
const extensionToMimeType = (ext) => {
|
|
65
|
+
const mimeMap = {
|
|
66
|
+
jpg: "image/jpeg",
|
|
67
|
+
jpeg: "image/jpeg",
|
|
68
|
+
png: "image/png",
|
|
69
|
+
gif: "image/gif",
|
|
70
|
+
webp: "image/webp",
|
|
71
|
+
svg: "image/svg+xml",
|
|
72
|
+
bmp: "image/bmp",
|
|
73
|
+
ico: "image/x-icon",
|
|
74
|
+
};
|
|
75
|
+
return mimeMap[ext.toLowerCase()] ?? `image/${ext}`;
|
|
76
|
+
};
|
|
63
77
|
// Convert local file path to data URL (base64 encoded)
|
|
64
|
-
const pathToDataUrl = (filePath) => {
|
|
78
|
+
export const pathToDataUrl = (filePath) => {
|
|
65
79
|
assert(fs.existsSync(filePath), `File not found: ${filePath}`, false, mediaSourceFileNotFoundError(filePath));
|
|
66
80
|
const buffer = fs.readFileSync(filePath);
|
|
67
|
-
const
|
|
68
|
-
const mimeType =
|
|
81
|
+
const ext = filePath.split(".").pop()?.toLowerCase() ?? "png";
|
|
82
|
+
const mimeType = extensionToMimeType(ext);
|
|
69
83
|
return `data:${mimeType};base64,${buffer.toString("base64")}`;
|
|
70
84
|
};
|
|
71
85
|
// Convert base64 string to data URL format
|
package/lib/slide/blocks.d.ts
CHANGED
|
@@ -3,3 +3,5 @@ import type { ContentBlock } from "./schema.js";
|
|
|
3
3
|
export declare const renderContentBlock: (block: ContentBlock) => string;
|
|
4
4
|
/** Render an array of content blocks to HTML */
|
|
5
5
|
export declare const renderContentBlocks: (blocks: ContentBlock[]) => string;
|
|
6
|
+
/** Render content blocks with fixed aspect-ratio container for image blocks (used in card layouts) */
|
|
7
|
+
export declare const renderCardContentBlocks: (blocks: ContentBlock[]) => string;
|
package/lib/slide/blocks.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { escapeHtml, nl2br, c } from "./utils.js";
|
|
1
|
+
import { escapeHtml, nl2br, c, generateSlideId } from "./utils.js";
|
|
2
2
|
/** Render a single content block to HTML */
|
|
3
3
|
export const renderContentBlock = (block) => {
|
|
4
4
|
switch (block.type) {
|
|
@@ -16,6 +16,12 @@ export const renderContentBlock = (block) => {
|
|
|
16
16
|
return renderDivider(block);
|
|
17
17
|
case "image":
|
|
18
18
|
return renderImage(block);
|
|
19
|
+
case "imageRef":
|
|
20
|
+
return renderImageRefPlaceholder(block);
|
|
21
|
+
case "chart":
|
|
22
|
+
return renderChart(block);
|
|
23
|
+
case "mermaid":
|
|
24
|
+
return renderMermaid(block);
|
|
19
25
|
default:
|
|
20
26
|
return `<p class="text-sm text-d-muted font-body">[unknown block type]</p>`;
|
|
21
27
|
}
|
|
@@ -24,6 +30,17 @@ export const renderContentBlock = (block) => {
|
|
|
24
30
|
export const renderContentBlocks = (blocks) => {
|
|
25
31
|
return blocks.map(renderContentBlock).join("\n");
|
|
26
32
|
};
|
|
33
|
+
/** Render content blocks with fixed aspect-ratio container for image blocks (used in card layouts) */
|
|
34
|
+
export const renderCardContentBlocks = (blocks) => {
|
|
35
|
+
return blocks
|
|
36
|
+
.map((block) => {
|
|
37
|
+
if (block.type === "image") {
|
|
38
|
+
return `<div class="aspect-video shrink-0 overflow-hidden">${renderContentBlock(block)}</div>`;
|
|
39
|
+
}
|
|
40
|
+
return renderContentBlock(block);
|
|
41
|
+
})
|
|
42
|
+
.join("\n");
|
|
43
|
+
};
|
|
27
44
|
const resolveTextColor = (block) => {
|
|
28
45
|
if (block.color)
|
|
29
46
|
return `text-${c(block.color)}`;
|
|
@@ -93,5 +110,40 @@ const renderDivider = (block) => {
|
|
|
93
110
|
};
|
|
94
111
|
const renderImage = (block) => {
|
|
95
112
|
const fit = block.fit === "cover" ? "object-cover" : "object-contain";
|
|
96
|
-
return `<img src="${escapeHtml(block.src)}" alt="${escapeHtml(block.alt || "")}" class="rounded ${fit}
|
|
113
|
+
return `<div class="min-h-0 flex-1 overflow-hidden flex items-center"><img src="${escapeHtml(block.src)}" alt="${escapeHtml(block.alt || "")}" class="rounded ${fit} w-full h-full" /></div>`;
|
|
114
|
+
};
|
|
115
|
+
/** Placeholder for unresolved imageRef blocks — should be resolved before rendering */
|
|
116
|
+
const renderImageRefPlaceholder = (block) => {
|
|
117
|
+
return `<div class="min-h-0 flex-1 overflow-hidden flex items-center justify-center bg-d-alt rounded"><p class="text-sm text-d-dim font-body">[imageRef: ${escapeHtml(block.ref)}]</p></div>`;
|
|
118
|
+
};
|
|
119
|
+
const renderChart = (block) => {
|
|
120
|
+
const chartId = generateSlideId("chart");
|
|
121
|
+
const chartData = JSON.stringify(block.chartData);
|
|
122
|
+
const titleHtml = block.title ? `<p class="text-sm font-bold text-d-text font-body mb-2">${escapeHtml(block.title)}</p>` : "";
|
|
123
|
+
return `<div class="flex-1 min-h-0 flex flex-col">
|
|
124
|
+
${titleHtml}
|
|
125
|
+
<div class="flex-1 min-h-0 relative">
|
|
126
|
+
<canvas id="${chartId}" data-chart-ready="false"></canvas>
|
|
127
|
+
</div>
|
|
128
|
+
<script>(function(){
|
|
129
|
+
const ctx=document.getElementById('${chartId}');
|
|
130
|
+
const d=${chartData};
|
|
131
|
+
if(!d.options)d.options={};
|
|
132
|
+
d.options.animation=false;
|
|
133
|
+
d.options.responsive=true;
|
|
134
|
+
d.options.maintainAspectRatio=false;
|
|
135
|
+
new Chart(ctx,d);
|
|
136
|
+
requestAnimationFrame(()=>requestAnimationFrame(()=>{ctx.dataset.chartReady="true"}));
|
|
137
|
+
})()</script>
|
|
138
|
+
</div>`;
|
|
139
|
+
};
|
|
140
|
+
const renderMermaid = (block) => {
|
|
141
|
+
const mermaidId = generateSlideId("mermaid");
|
|
142
|
+
const titleHtml = block.title ? `<p class="text-sm font-bold text-d-text font-body mb-2">${escapeHtml(block.title)}</p>` : "";
|
|
143
|
+
return `<div class="flex-1 min-h-0 flex flex-col">
|
|
144
|
+
${titleHtml}
|
|
145
|
+
<div class="flex-1 min-h-0 flex justify-center items-center">
|
|
146
|
+
<div id="${mermaidId}" class="mermaid">${escapeHtml(block.code)}</div>
|
|
147
|
+
</div>
|
|
148
|
+
</div>`;
|
|
97
149
|
};
|
package/lib/slide/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { generateSlideHTML } from "./render.js";
|
|
2
2
|
export { renderSlideContent } from "./layouts/index.js";
|
|
3
3
|
export { renderContentBlock, renderContentBlocks } from "./blocks.js";
|
|
4
|
-
export { mulmoSlideMediaSchema, slideLayoutSchema, slideThemeSchema, contentBlockSchema, accentColorKeySchema } from "./schema.js";
|
|
5
|
-
export type { MulmoSlideMedia, SlideLayout, SlideTheme, SlideThemeColors, SlideThemeFonts, ContentBlock, AccentColorKey, TitleSlide, ColumnsSlide, ComparisonSlide, GridSlide, BigQuoteSlide, StatsSlide, TimelineSlide, SplitSlide, MatrixSlide, TableSlide, FunnelSlide, Card, CalloutBar, SlideStyle, } from "./schema.js";
|
|
4
|
+
export { mulmoSlideMediaSchema, slideLayoutSchema, slideThemeSchema, contentBlockSchema, imageRefBlockSchema, chartBlockSchema, mermaidBlockSchema, accentColorKeySchema, } from "./schema.js";
|
|
5
|
+
export type { MulmoSlideMedia, SlideLayout, SlideTheme, SlideThemeColors, SlideThemeFonts, ContentBlock, ImageRefBlock, ChartBlock, MermaidBlock, AccentColorKey, TitleSlide, ColumnsSlide, ComparisonSlide, GridSlide, BigQuoteSlide, StatsSlide, TimelineSlide, SplitSlide, MatrixSlide, TableSlide, FunnelSlide, Card, CalloutBar, SlideStyle, } from "./schema.js";
|
package/lib/slide/index.js
CHANGED
|
@@ -4,4 +4,4 @@ export { generateSlideHTML } from "./render.js";
|
|
|
4
4
|
export { renderSlideContent } from "./layouts/index.js";
|
|
5
5
|
export { renderContentBlock, renderContentBlocks } from "./blocks.js";
|
|
6
6
|
// Schemas
|
|
7
|
-
export { mulmoSlideMediaSchema, slideLayoutSchema, slideThemeSchema, contentBlockSchema, accentColorKeySchema } from "./schema.js";
|
|
7
|
+
export { mulmoSlideMediaSchema, slideLayoutSchema, slideThemeSchema, contentBlockSchema, imageRefBlockSchema, chartBlockSchema, mermaidBlockSchema, accentColorKeySchema, } from "./schema.js";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { escapeHtml, c, cardWrap, numBadge, iconSquare, slideHeader, renderCalloutBar } from "../utils.js";
|
|
2
|
-
import {
|
|
2
|
+
import { renderCardContentBlocks } from "../blocks.js";
|
|
3
3
|
const buildColumnCard = (col) => {
|
|
4
4
|
const accent = col.accentColor || "primary";
|
|
5
5
|
const inner = [];
|
|
@@ -23,8 +23,8 @@ const buildColumnCard = (col) => {
|
|
|
23
23
|
}
|
|
24
24
|
if (col.content) {
|
|
25
25
|
const centerCls = col.icon ? "text-center" : "";
|
|
26
|
-
inner.push(`<div class="mt-3 space-y-3 ${centerCls}">`);
|
|
27
|
-
inner.push(
|
|
26
|
+
inner.push(`<div class="mt-3 space-y-3 flex-1 min-h-0 overflow-hidden flex flex-col ${centerCls}">`);
|
|
27
|
+
inner.push(renderCardContentBlocks(col.content));
|
|
28
28
|
inner.push(`</div>`);
|
|
29
29
|
}
|
|
30
30
|
if (col.footer) {
|
|
@@ -43,7 +43,7 @@ export const layoutColumns = (data) => {
|
|
|
43
43
|
colElements.push(`<div class="flex items-center shrink-0"><span class="text-2xl text-d-dim">\u25B6</span></div>`);
|
|
44
44
|
}
|
|
45
45
|
});
|
|
46
|
-
parts.push(`<div class="flex gap-4 px-12 mt-5 flex-1">`);
|
|
46
|
+
parts.push(`<div class="flex gap-4 px-12 mt-5 flex-1 min-h-0 items-stretch">`);
|
|
47
47
|
parts.push(colElements.join("\n"));
|
|
48
48
|
parts.push(`</div>`);
|
|
49
49
|
if (data.callout) {
|
|
@@ -5,19 +5,20 @@ const buildPanel = (panel) => {
|
|
|
5
5
|
const inner = [];
|
|
6
6
|
inner.push(`<h3 class="text-xl font-bold text-${c(accent)} font-body">${escapeHtml(panel.title)}</h3>`);
|
|
7
7
|
if (panel.content) {
|
|
8
|
-
inner.push(`<div class="mt-4 space-y-3">`);
|
|
8
|
+
inner.push(`<div class="mt-4 space-y-3 flex-1 min-h-0 overflow-hidden flex flex-col">`);
|
|
9
9
|
inner.push(renderContentBlocks(panel.content));
|
|
10
10
|
inner.push(`</div>`);
|
|
11
11
|
}
|
|
12
12
|
if (panel.footer) {
|
|
13
|
-
|
|
13
|
+
if (!panel.content)
|
|
14
|
+
inner.push(`<div class="flex-1"></div>`);
|
|
14
15
|
inner.push(`<p class="text-sm text-d-dim font-body mt-3">${escapeHtml(panel.footer)}</p>`);
|
|
15
16
|
}
|
|
16
17
|
return cardWrap(accent, inner.join("\n"), "flex-1");
|
|
17
18
|
};
|
|
18
19
|
export const layoutComparison = (data) => {
|
|
19
20
|
const parts = [slideHeader(data)];
|
|
20
|
-
parts.push(`<div class="flex gap-5 px-12 mt-5 flex-1">`);
|
|
21
|
+
parts.push(`<div class="flex gap-5 px-12 mt-5 flex-1 min-h-0 items-stretch">`);
|
|
21
22
|
parts.push(buildPanel(data.left));
|
|
22
23
|
parts.push(buildPanel(data.right));
|
|
23
24
|
parts.push(`</div>`);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { escapeHtml, nl2br, c, cardWrap, numBadge, iconSquare } from "../utils.js";
|
|
2
|
-
import {
|
|
2
|
+
import { renderCardContentBlocks } from "../blocks.js";
|
|
3
3
|
export const layoutGrid = (data) => {
|
|
4
4
|
const accent = data.accentColor || "primary";
|
|
5
5
|
const nCols = data.gridColumns || 3;
|
|
@@ -8,7 +8,7 @@ export const layoutGrid = (data) => {
|
|
|
8
8
|
parts.push(`<div class="px-12 pt-5 shrink-0">`);
|
|
9
9
|
parts.push(` <h2 class="text-[42px] leading-tight font-title font-bold text-d-text">${nl2br(data.title)}</h2>`);
|
|
10
10
|
parts.push(`</div>`);
|
|
11
|
-
parts.push(`<div class="grid grid-cols-${nCols} gap-4 px-12 mt-5 flex-1 content-start">`);
|
|
11
|
+
parts.push(`<div class="grid grid-cols-${nCols} gap-4 px-12 mt-5 flex-1 min-h-0 overflow-hidden content-start">`);
|
|
12
12
|
(data.items || []).forEach((item) => {
|
|
13
13
|
const itemAccent = item.accentColor || "primary";
|
|
14
14
|
const inner = [];
|
|
@@ -31,7 +31,7 @@ export const layoutGrid = (data) => {
|
|
|
31
31
|
inner.push(`<p class="text-sm text-d-muted font-body mt-3">${escapeHtml(item.description)}</p>`);
|
|
32
32
|
}
|
|
33
33
|
if (item.content) {
|
|
34
|
-
inner.push(`<div class="mt-3 space-y-3">${
|
|
34
|
+
inner.push(`<div class="mt-3 space-y-3 flex-1 min-h-0 overflow-hidden flex flex-col">${renderCardContentBlocks(item.content)}</div>`);
|
|
35
35
|
}
|
|
36
36
|
parts.push(cardWrap(itemAccent, inner.join("\n")));
|
|
37
37
|
});
|
package/lib/slide/render.js
CHANGED
|
@@ -1,9 +1,31 @@
|
|
|
1
|
-
import { escapeHtml, buildTailwindConfig, sanitizeHex } from "./utils.js";
|
|
1
|
+
import { escapeHtml, buildTailwindConfig, sanitizeHex, detectBlockTypes } from "./utils.js";
|
|
2
2
|
import { renderSlideContent } from "./layouts/index.js";
|
|
3
|
+
/** Determine if a hex color is dark (luminance < 128) */
|
|
4
|
+
const isDarkBg = (hex) => {
|
|
5
|
+
const r = parseInt(hex.slice(0, 2), 16);
|
|
6
|
+
const g = parseInt(hex.slice(2, 4), 16);
|
|
7
|
+
const b = parseInt(hex.slice(4, 6), 16);
|
|
8
|
+
return (r * 299 + g * 587 + b * 114) / 1000 < 128;
|
|
9
|
+
};
|
|
10
|
+
/** Build CDN script tags for chart/mermaid when needed */
|
|
11
|
+
const buildCdnScripts = (theme, slide) => {
|
|
12
|
+
const { hasChart, hasMermaid } = detectBlockTypes(slide);
|
|
13
|
+
const scripts = [];
|
|
14
|
+
if (hasChart) {
|
|
15
|
+
scripts.push('<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>');
|
|
16
|
+
}
|
|
17
|
+
if (hasMermaid) {
|
|
18
|
+
const mermaidTheme = isDarkBg(theme.colors.bg) ? "dark" : "default";
|
|
19
|
+
scripts.push(`<script src="https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.min.js"></script>
|
|
20
|
+
<script>mermaid.initialize({startOnLoad:true,theme:'${mermaidTheme}'})</script>`);
|
|
21
|
+
}
|
|
22
|
+
return scripts.join("\n");
|
|
23
|
+
};
|
|
3
24
|
/** Generate a complete HTML document for a single slide */
|
|
4
25
|
export const generateSlideHTML = (theme, slide) => {
|
|
5
26
|
const content = renderSlideContent(slide);
|
|
6
27
|
const twConfig = buildTailwindConfig(theme);
|
|
28
|
+
const cdnScripts = buildCdnScripts(theme, slide);
|
|
7
29
|
const slideStyle = slide.style;
|
|
8
30
|
const bgCls = slideStyle?.bgColor ? "" : "bg-d-bg";
|
|
9
31
|
const inlineStyle = slideStyle?.bgColor ? ` style="background-color:#${sanitizeHex(slideStyle.bgColor)}"` : "";
|
|
@@ -15,6 +37,7 @@ export const generateSlideHTML = (theme, slide) => {
|
|
|
15
37
|
<meta name="viewport" content="width=1280">
|
|
16
38
|
<script src="https://cdn.tailwindcss.com"></script>
|
|
17
39
|
<script>tailwind.config = ${twConfig}</script>
|
|
40
|
+
${cdnScripts}
|
|
18
41
|
<style>
|
|
19
42
|
html, body { height: 100%; margin: 0; padding: 0; overflow: hidden; }
|
|
20
43
|
</style>
|