mulmocast 2.1.40 → 2.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/README.md +1 -1
- package/assets/schemas/html_prompt.json +60 -0
- package/assets/schemas/mulmo_script.json +283 -0
- package/assets/slide_themes/corporate.json +18 -0
- package/assets/slide_themes/creative.json +18 -0
- package/assets/slide_themes/dark.json +18 -0
- package/assets/slide_themes/minimal.json +18 -0
- package/assets/slide_themes/pop.json +18 -0
- package/assets/slide_themes/warm.json +18 -0
- package/assets/styles/akira_comic.json +22 -0
- package/assets/styles/ani.json +43 -0
- package/assets/styles/children_book.json +13 -0
- package/assets/styles/comic_strips.json +13 -0
- package/assets/styles/drslump_comic.json +22 -0
- package/assets/styles/ghibli_comic.json +22 -0
- package/assets/styles/ghibli_shorts.json +28 -0
- package/assets/styles/ghost_comic.json +29 -0
- package/assets/styles/leda.json +29 -0
- package/assets/styles/onepiece_comic.json +22 -0
- package/assets/styles/slide_corporate.json +30 -0
- package/assets/styles/slide_creative.json +30 -0
- package/assets/styles/slide_dark.json +30 -0
- package/assets/styles/slide_minimal.json +30 -0
- package/assets/styles/slide_pop.json +30 -0
- package/assets/styles/slide_warm.json +30 -0
- 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/data/index.d.ts +2 -0
- package/lib/data/index.js +2 -0
- package/lib/data/slideStyles.d.ts +206 -0
- package/lib/data/slideStyles.js +206 -0
- package/lib/data/slideThemes.d.ts +134 -0
- package/lib/data/slideThemes.js +134 -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 +9 -2
- 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/lib/utils/context.d.ts
CHANGED
|
@@ -331,6 +331,19 @@ export declare const createStudioData: (_mulmoScript: MulmoScript, fileName: str
|
|
|
331
331
|
src: string;
|
|
332
332
|
alt?: string | undefined;
|
|
333
333
|
fit?: "contain" | "cover" | undefined;
|
|
334
|
+
} | {
|
|
335
|
+
type: "imageRef";
|
|
336
|
+
ref: string;
|
|
337
|
+
alt?: string | undefined;
|
|
338
|
+
fit?: "contain" | "cover" | undefined;
|
|
339
|
+
} | {
|
|
340
|
+
type: "chart";
|
|
341
|
+
chartData: Record<string, unknown>;
|
|
342
|
+
title?: string | undefined;
|
|
343
|
+
} | {
|
|
344
|
+
type: "mermaid";
|
|
345
|
+
code: string;
|
|
346
|
+
title?: string | undefined;
|
|
334
347
|
})[] | undefined;
|
|
335
348
|
footer?: string | undefined;
|
|
336
349
|
label?: string | undefined;
|
|
@@ -398,6 +411,19 @@ export declare const createStudioData: (_mulmoScript: MulmoScript, fileName: str
|
|
|
398
411
|
src: string;
|
|
399
412
|
alt?: string | undefined;
|
|
400
413
|
fit?: "contain" | "cover" | undefined;
|
|
414
|
+
} | {
|
|
415
|
+
type: "imageRef";
|
|
416
|
+
ref: string;
|
|
417
|
+
alt?: string | undefined;
|
|
418
|
+
fit?: "contain" | "cover" | undefined;
|
|
419
|
+
} | {
|
|
420
|
+
type: "chart";
|
|
421
|
+
chartData: Record<string, unknown>;
|
|
422
|
+
title?: string | undefined;
|
|
423
|
+
} | {
|
|
424
|
+
type: "mermaid";
|
|
425
|
+
code: string;
|
|
426
|
+
title?: string | undefined;
|
|
401
427
|
})[] | undefined;
|
|
402
428
|
footer?: string | undefined;
|
|
403
429
|
};
|
|
@@ -441,6 +467,19 @@ export declare const createStudioData: (_mulmoScript: MulmoScript, fileName: str
|
|
|
441
467
|
src: string;
|
|
442
468
|
alt?: string | undefined;
|
|
443
469
|
fit?: "contain" | "cover" | undefined;
|
|
470
|
+
} | {
|
|
471
|
+
type: "imageRef";
|
|
472
|
+
ref: string;
|
|
473
|
+
alt?: string | undefined;
|
|
474
|
+
fit?: "contain" | "cover" | undefined;
|
|
475
|
+
} | {
|
|
476
|
+
type: "chart";
|
|
477
|
+
chartData: Record<string, unknown>;
|
|
478
|
+
title?: string | undefined;
|
|
479
|
+
} | {
|
|
480
|
+
type: "mermaid";
|
|
481
|
+
code: string;
|
|
482
|
+
title?: string | undefined;
|
|
444
483
|
})[] | undefined;
|
|
445
484
|
footer?: string | undefined;
|
|
446
485
|
};
|
|
@@ -506,6 +545,19 @@ export declare const createStudioData: (_mulmoScript: MulmoScript, fileName: str
|
|
|
506
545
|
src: string;
|
|
507
546
|
alt?: string | undefined;
|
|
508
547
|
fit?: "contain" | "cover" | undefined;
|
|
548
|
+
} | {
|
|
549
|
+
type: "imageRef";
|
|
550
|
+
ref: string;
|
|
551
|
+
alt?: string | undefined;
|
|
552
|
+
fit?: "contain" | "cover" | undefined;
|
|
553
|
+
} | {
|
|
554
|
+
type: "chart";
|
|
555
|
+
chartData: Record<string, unknown>;
|
|
556
|
+
title?: string | undefined;
|
|
557
|
+
} | {
|
|
558
|
+
type: "mermaid";
|
|
559
|
+
code: string;
|
|
560
|
+
title?: string | undefined;
|
|
509
561
|
})[] | undefined;
|
|
510
562
|
}[];
|
|
511
563
|
layout: "grid";
|
|
@@ -619,6 +671,19 @@ export declare const createStudioData: (_mulmoScript: MulmoScript, fileName: str
|
|
|
619
671
|
src: string;
|
|
620
672
|
alt?: string | undefined;
|
|
621
673
|
fit?: "contain" | "cover" | undefined;
|
|
674
|
+
} | {
|
|
675
|
+
type: "imageRef";
|
|
676
|
+
ref: string;
|
|
677
|
+
alt?: string | undefined;
|
|
678
|
+
fit?: "contain" | "cover" | undefined;
|
|
679
|
+
} | {
|
|
680
|
+
type: "chart";
|
|
681
|
+
chartData: Record<string, unknown>;
|
|
682
|
+
title?: string | undefined;
|
|
683
|
+
} | {
|
|
684
|
+
type: "mermaid";
|
|
685
|
+
code: string;
|
|
686
|
+
title?: string | undefined;
|
|
622
687
|
})[] | undefined;
|
|
623
688
|
dark?: boolean | undefined;
|
|
624
689
|
ratio?: number | undefined;
|
|
@@ -665,6 +730,19 @@ export declare const createStudioData: (_mulmoScript: MulmoScript, fileName: str
|
|
|
665
730
|
src: string;
|
|
666
731
|
alt?: string | undefined;
|
|
667
732
|
fit?: "contain" | "cover" | undefined;
|
|
733
|
+
} | {
|
|
734
|
+
type: "imageRef";
|
|
735
|
+
ref: string;
|
|
736
|
+
alt?: string | undefined;
|
|
737
|
+
fit?: "contain" | "cover" | undefined;
|
|
738
|
+
} | {
|
|
739
|
+
type: "chart";
|
|
740
|
+
chartData: Record<string, unknown>;
|
|
741
|
+
title?: string | undefined;
|
|
742
|
+
} | {
|
|
743
|
+
type: "mermaid";
|
|
744
|
+
code: string;
|
|
745
|
+
title?: string | undefined;
|
|
668
746
|
})[] | undefined;
|
|
669
747
|
dark?: boolean | undefined;
|
|
670
748
|
ratio?: number | undefined;
|
|
@@ -718,6 +796,19 @@ export declare const createStudioData: (_mulmoScript: MulmoScript, fileName: str
|
|
|
718
796
|
src: string;
|
|
719
797
|
alt?: string | undefined;
|
|
720
798
|
fit?: "contain" | "cover" | undefined;
|
|
799
|
+
} | {
|
|
800
|
+
type: "imageRef";
|
|
801
|
+
ref: string;
|
|
802
|
+
alt?: string | undefined;
|
|
803
|
+
fit?: "contain" | "cover" | undefined;
|
|
804
|
+
} | {
|
|
805
|
+
type: "chart";
|
|
806
|
+
chartData: Record<string, unknown>;
|
|
807
|
+
title?: string | undefined;
|
|
808
|
+
} | {
|
|
809
|
+
type: "mermaid";
|
|
810
|
+
code: string;
|
|
811
|
+
title?: string | undefined;
|
|
721
812
|
})[] | undefined;
|
|
722
813
|
accentColor?: "success" | "primary" | "accent" | "warning" | "danger" | "info" | "highlight" | undefined;
|
|
723
814
|
}[];
|
|
@@ -1623,6 +1714,19 @@ export declare const initializeContextFromFiles: (files: FileObject, raiseError:
|
|
|
1623
1714
|
src: string;
|
|
1624
1715
|
alt?: string | undefined;
|
|
1625
1716
|
fit?: "contain" | "cover" | undefined;
|
|
1717
|
+
} | {
|
|
1718
|
+
type: "imageRef";
|
|
1719
|
+
ref: string;
|
|
1720
|
+
alt?: string | undefined;
|
|
1721
|
+
fit?: "contain" | "cover" | undefined;
|
|
1722
|
+
} | {
|
|
1723
|
+
type: "chart";
|
|
1724
|
+
chartData: Record<string, unknown>;
|
|
1725
|
+
title?: string | undefined;
|
|
1726
|
+
} | {
|
|
1727
|
+
type: "mermaid";
|
|
1728
|
+
code: string;
|
|
1729
|
+
title?: string | undefined;
|
|
1626
1730
|
})[] | undefined;
|
|
1627
1731
|
footer?: string | undefined;
|
|
1628
1732
|
label?: string | undefined;
|
|
@@ -1690,6 +1794,19 @@ export declare const initializeContextFromFiles: (files: FileObject, raiseError:
|
|
|
1690
1794
|
src: string;
|
|
1691
1795
|
alt?: string | undefined;
|
|
1692
1796
|
fit?: "contain" | "cover" | undefined;
|
|
1797
|
+
} | {
|
|
1798
|
+
type: "imageRef";
|
|
1799
|
+
ref: string;
|
|
1800
|
+
alt?: string | undefined;
|
|
1801
|
+
fit?: "contain" | "cover" | undefined;
|
|
1802
|
+
} | {
|
|
1803
|
+
type: "chart";
|
|
1804
|
+
chartData: Record<string, unknown>;
|
|
1805
|
+
title?: string | undefined;
|
|
1806
|
+
} | {
|
|
1807
|
+
type: "mermaid";
|
|
1808
|
+
code: string;
|
|
1809
|
+
title?: string | undefined;
|
|
1693
1810
|
})[] | undefined;
|
|
1694
1811
|
footer?: string | undefined;
|
|
1695
1812
|
};
|
|
@@ -1733,6 +1850,19 @@ export declare const initializeContextFromFiles: (files: FileObject, raiseError:
|
|
|
1733
1850
|
src: string;
|
|
1734
1851
|
alt?: string | undefined;
|
|
1735
1852
|
fit?: "contain" | "cover" | undefined;
|
|
1853
|
+
} | {
|
|
1854
|
+
type: "imageRef";
|
|
1855
|
+
ref: string;
|
|
1856
|
+
alt?: string | undefined;
|
|
1857
|
+
fit?: "contain" | "cover" | undefined;
|
|
1858
|
+
} | {
|
|
1859
|
+
type: "chart";
|
|
1860
|
+
chartData: Record<string, unknown>;
|
|
1861
|
+
title?: string | undefined;
|
|
1862
|
+
} | {
|
|
1863
|
+
type: "mermaid";
|
|
1864
|
+
code: string;
|
|
1865
|
+
title?: string | undefined;
|
|
1736
1866
|
})[] | undefined;
|
|
1737
1867
|
footer?: string | undefined;
|
|
1738
1868
|
};
|
|
@@ -1798,6 +1928,19 @@ export declare const initializeContextFromFiles: (files: FileObject, raiseError:
|
|
|
1798
1928
|
src: string;
|
|
1799
1929
|
alt?: string | undefined;
|
|
1800
1930
|
fit?: "contain" | "cover" | undefined;
|
|
1931
|
+
} | {
|
|
1932
|
+
type: "imageRef";
|
|
1933
|
+
ref: string;
|
|
1934
|
+
alt?: string | undefined;
|
|
1935
|
+
fit?: "contain" | "cover" | undefined;
|
|
1936
|
+
} | {
|
|
1937
|
+
type: "chart";
|
|
1938
|
+
chartData: Record<string, unknown>;
|
|
1939
|
+
title?: string | undefined;
|
|
1940
|
+
} | {
|
|
1941
|
+
type: "mermaid";
|
|
1942
|
+
code: string;
|
|
1943
|
+
title?: string | undefined;
|
|
1801
1944
|
})[] | undefined;
|
|
1802
1945
|
}[];
|
|
1803
1946
|
layout: "grid";
|
|
@@ -1911,6 +2054,19 @@ export declare const initializeContextFromFiles: (files: FileObject, raiseError:
|
|
|
1911
2054
|
src: string;
|
|
1912
2055
|
alt?: string | undefined;
|
|
1913
2056
|
fit?: "contain" | "cover" | undefined;
|
|
2057
|
+
} | {
|
|
2058
|
+
type: "imageRef";
|
|
2059
|
+
ref: string;
|
|
2060
|
+
alt?: string | undefined;
|
|
2061
|
+
fit?: "contain" | "cover" | undefined;
|
|
2062
|
+
} | {
|
|
2063
|
+
type: "chart";
|
|
2064
|
+
chartData: Record<string, unknown>;
|
|
2065
|
+
title?: string | undefined;
|
|
2066
|
+
} | {
|
|
2067
|
+
type: "mermaid";
|
|
2068
|
+
code: string;
|
|
2069
|
+
title?: string | undefined;
|
|
1914
2070
|
})[] | undefined;
|
|
1915
2071
|
dark?: boolean | undefined;
|
|
1916
2072
|
ratio?: number | undefined;
|
|
@@ -1957,6 +2113,19 @@ export declare const initializeContextFromFiles: (files: FileObject, raiseError:
|
|
|
1957
2113
|
src: string;
|
|
1958
2114
|
alt?: string | undefined;
|
|
1959
2115
|
fit?: "contain" | "cover" | undefined;
|
|
2116
|
+
} | {
|
|
2117
|
+
type: "imageRef";
|
|
2118
|
+
ref: string;
|
|
2119
|
+
alt?: string | undefined;
|
|
2120
|
+
fit?: "contain" | "cover" | undefined;
|
|
2121
|
+
} | {
|
|
2122
|
+
type: "chart";
|
|
2123
|
+
chartData: Record<string, unknown>;
|
|
2124
|
+
title?: string | undefined;
|
|
2125
|
+
} | {
|
|
2126
|
+
type: "mermaid";
|
|
2127
|
+
code: string;
|
|
2128
|
+
title?: string | undefined;
|
|
1960
2129
|
})[] | undefined;
|
|
1961
2130
|
dark?: boolean | undefined;
|
|
1962
2131
|
ratio?: number | undefined;
|
|
@@ -2010,6 +2179,19 @@ export declare const initializeContextFromFiles: (files: FileObject, raiseError:
|
|
|
2010
2179
|
src: string;
|
|
2011
2180
|
alt?: string | undefined;
|
|
2012
2181
|
fit?: "contain" | "cover" | undefined;
|
|
2182
|
+
} | {
|
|
2183
|
+
type: "imageRef";
|
|
2184
|
+
ref: string;
|
|
2185
|
+
alt?: string | undefined;
|
|
2186
|
+
fit?: "contain" | "cover" | undefined;
|
|
2187
|
+
} | {
|
|
2188
|
+
type: "chart";
|
|
2189
|
+
chartData: Record<string, unknown>;
|
|
2190
|
+
title?: string | undefined;
|
|
2191
|
+
} | {
|
|
2192
|
+
type: "mermaid";
|
|
2193
|
+
code: string;
|
|
2194
|
+
title?: string | undefined;
|
|
2013
2195
|
})[] | undefined;
|
|
2014
2196
|
accentColor?: "success" | "primary" | "accent" | "warning" | "danger" | "info" | "highlight" | undefined;
|
|
2015
2197
|
}[];
|
package/lib/utils/html_render.js
CHANGED
|
@@ -1,17 +1,55 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import nodePath from "node:path";
|
|
4
|
+
import crypto from "node:crypto";
|
|
1
5
|
import { marked } from "marked";
|
|
2
6
|
import puppeteer from "puppeteer";
|
|
3
7
|
const isCI = process.env.CI === "true";
|
|
8
|
+
/** Determine the appropriate waitUntil strategy based on HTML content */
|
|
9
|
+
const resolveWaitUntil = (html) => {
|
|
10
|
+
const hasExternalImages = html.includes("<img") && /src=["']https?:\/\//.test(html);
|
|
11
|
+
const hasLocalImages = html.includes("<img") && /src=["']file:\/\//.test(html);
|
|
12
|
+
if (hasExternalImages)
|
|
13
|
+
return "networkidle0";
|
|
14
|
+
if (hasLocalImages)
|
|
15
|
+
return "load";
|
|
16
|
+
return "domcontentloaded";
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* Load HTML into a Puppeteer page.
|
|
20
|
+
* When the HTML references file:// URLs, write it to a temp file
|
|
21
|
+
* and navigate via page.goto (setContent uses about:blank origin
|
|
22
|
+
* which blocks file:// loading).
|
|
23
|
+
*/
|
|
24
|
+
const loadHtmlIntoPage = async (page, html, timeout_ms) => {
|
|
25
|
+
const waitUntil = resolveWaitUntil(html);
|
|
26
|
+
const hasFileUrls = /file:\/\//.test(html);
|
|
27
|
+
if (hasFileUrls) {
|
|
28
|
+
const tmpFile = nodePath.join(os.tmpdir(), `mulmocast_render_${crypto.randomUUID()}.html`);
|
|
29
|
+
fs.writeFileSync(tmpFile, html);
|
|
30
|
+
try {
|
|
31
|
+
await page.goto(`file://${tmpFile}`, { waitUntil, timeout: timeout_ms });
|
|
32
|
+
}
|
|
33
|
+
finally {
|
|
34
|
+
try {
|
|
35
|
+
fs.unlinkSync(tmpFile);
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
/* ignore cleanup errors */
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
await page.setContent(html, { waitUntil, timeout: timeout_ms });
|
|
44
|
+
}
|
|
45
|
+
};
|
|
4
46
|
export const renderHTMLToImage = async (html, outputPath, width, height, isMermaid = false, omitBackground = false) => {
|
|
5
47
|
// Use Puppeteer to render HTML to an image
|
|
6
48
|
const browser = await puppeteer.launch({
|
|
7
|
-
args: isCI ? ["--no-sandbox"] : [],
|
|
49
|
+
args: isCI ? ["--no-sandbox", "--allow-file-access-from-files"] : ["--allow-file-access-from-files"],
|
|
8
50
|
});
|
|
9
51
|
const page = await browser.newPage();
|
|
10
|
-
|
|
11
|
-
// Use networkidle0 only for external images, otherwise use domcontentloaded for faster rendering
|
|
12
|
-
const hasExternalImages = html.includes("<img") && /src=["']https?:\/\//.test(html);
|
|
13
|
-
const waitUntil = hasExternalImages ? "networkidle0" : "domcontentloaded";
|
|
14
|
-
await page.setContent(html, { waitUntil, timeout: 30000 });
|
|
52
|
+
await loadHtmlIntoPage(page, html, 30000);
|
|
15
53
|
// Adjust page settings if needed (like width, height, etc.)
|
|
16
54
|
await page.setViewport({ width, height });
|
|
17
55
|
// height:100% ensures background fills viewport; only reset html, let body styles come from custom CSS
|
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
import { ImageProcessorParams } from "../../types/index.js";
|
|
2
|
+
import type { SlideLayout, ContentBlock } from "../../slide/index.js";
|
|
2
3
|
export declare const imageType = "slide";
|
|
4
|
+
/**
|
|
5
|
+
* Collect all content block arrays from a slide layout.
|
|
6
|
+
* Only layouts that embed ContentBlock[] are handled:
|
|
7
|
+
* columns, comparison, grid, split, matrix
|
|
8
|
+
*/
|
|
9
|
+
export declare const collectContentArrays: (slide: SlideLayout) => ContentBlock[][];
|
|
10
|
+
/**
|
|
11
|
+
* Deep-clone a slide layout and resolve `imageRef` content blocks
|
|
12
|
+
* into `image` blocks using the resolved imageRefs map and a path-to-URL converter.
|
|
13
|
+
* Default converter produces data URLs (for self-contained HTML).
|
|
14
|
+
* Pass `toFileUrl` for Puppeteer rendering (avoids huge inline base64).
|
|
15
|
+
*/
|
|
16
|
+
export declare const resolveSlideImageRefs: (slide: SlideLayout, imageRefs: Record<string, string>, converter?: (filePath: string) => string) => SlideLayout;
|
|
3
17
|
export declare const process: (params: ImageProcessorParams) => Promise<string | undefined>;
|
|
4
18
|
export declare const path: (params: ImageProcessorParams) => string;
|
|
5
19
|
export declare const html: (params: ImageProcessorParams) => Promise<string | undefined>;
|
|
@@ -1,7 +1,93 @@
|
|
|
1
|
+
import nodePath from "node:path";
|
|
2
|
+
import { pathToFileURL } from "node:url";
|
|
1
3
|
import { generateSlideHTML } from "../../slide/index.js";
|
|
2
4
|
import { renderHTMLToImage } from "../html_render.js";
|
|
3
5
|
import { parrotingImagePath } from "./utils.js";
|
|
6
|
+
import { pathToDataUrl } from "../../methods/mulmo_media_source.js";
|
|
7
|
+
import { imageAction, imageFileTarget, unknownMediaType } from "../error_cause.js";
|
|
4
8
|
export const imageType = "slide";
|
|
9
|
+
const slideImageRefError = (refKey) => ({
|
|
10
|
+
type: unknownMediaType,
|
|
11
|
+
action: imageAction,
|
|
12
|
+
target: imageFileTarget,
|
|
13
|
+
agentName: "slidePlugin",
|
|
14
|
+
refKey,
|
|
15
|
+
});
|
|
16
|
+
/** Convert a file path to a file:// URL string */
|
|
17
|
+
const toFileUrl = (filePath) => {
|
|
18
|
+
return pathToFileURL(nodePath.resolve(filePath)).href;
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Collect all content block arrays from a slide layout.
|
|
22
|
+
* Only layouts that embed ContentBlock[] are handled:
|
|
23
|
+
* columns, comparison, grid, split, matrix
|
|
24
|
+
*/
|
|
25
|
+
export const collectContentArrays = (slide) => {
|
|
26
|
+
const result = [];
|
|
27
|
+
switch (slide.layout) {
|
|
28
|
+
case "columns":
|
|
29
|
+
slide.columns.forEach((col) => {
|
|
30
|
+
if (col.content)
|
|
31
|
+
result.push(col.content);
|
|
32
|
+
});
|
|
33
|
+
break;
|
|
34
|
+
case "comparison":
|
|
35
|
+
if (slide.left.content)
|
|
36
|
+
result.push(slide.left.content);
|
|
37
|
+
if (slide.right.content)
|
|
38
|
+
result.push(slide.right.content);
|
|
39
|
+
break;
|
|
40
|
+
case "grid":
|
|
41
|
+
slide.items.forEach((item) => {
|
|
42
|
+
if (item.content)
|
|
43
|
+
result.push(item.content);
|
|
44
|
+
});
|
|
45
|
+
break;
|
|
46
|
+
case "split":
|
|
47
|
+
if (slide.left?.content)
|
|
48
|
+
result.push(slide.left.content);
|
|
49
|
+
if (slide.right?.content)
|
|
50
|
+
result.push(slide.right.content);
|
|
51
|
+
break;
|
|
52
|
+
case "matrix":
|
|
53
|
+
slide.cells.forEach((cell) => {
|
|
54
|
+
if (cell.content)
|
|
55
|
+
result.push(cell.content);
|
|
56
|
+
});
|
|
57
|
+
break;
|
|
58
|
+
default:
|
|
59
|
+
// title, bigQuote, stats, timeline, table, funnel — no content blocks
|
|
60
|
+
break;
|
|
61
|
+
}
|
|
62
|
+
return result;
|
|
63
|
+
};
|
|
64
|
+
/**
|
|
65
|
+
* Deep-clone a slide layout and resolve `imageRef` content blocks
|
|
66
|
+
* into `image` blocks using the resolved imageRefs map and a path-to-URL converter.
|
|
67
|
+
* Default converter produces data URLs (for self-contained HTML).
|
|
68
|
+
* Pass `toFileUrl` for Puppeteer rendering (avoids huge inline base64).
|
|
69
|
+
*/
|
|
70
|
+
export const resolveSlideImageRefs = (slide, imageRefs, converter = pathToDataUrl) => {
|
|
71
|
+
const cloned = JSON.parse(JSON.stringify(slide));
|
|
72
|
+
const contentArrays = collectContentArrays(cloned);
|
|
73
|
+
contentArrays.forEach((blocks) => {
|
|
74
|
+
blocks.forEach((block, index) => {
|
|
75
|
+
if (block.type !== "imageRef")
|
|
76
|
+
return;
|
|
77
|
+
const filePath = imageRefs[block.ref];
|
|
78
|
+
if (!filePath) {
|
|
79
|
+
throw new Error(`Unknown image ref key: "${block.ref}"`, { cause: slideImageRefError(block.ref) });
|
|
80
|
+
}
|
|
81
|
+
blocks[index] = {
|
|
82
|
+
type: "image",
|
|
83
|
+
src: converter(filePath),
|
|
84
|
+
...(block.alt !== undefined && { alt: block.alt }),
|
|
85
|
+
...(block.fit !== undefined && { fit: block.fit }),
|
|
86
|
+
};
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
return cloned;
|
|
90
|
+
};
|
|
5
91
|
const resolveTheme = (params) => {
|
|
6
92
|
const { beat, context } = params;
|
|
7
93
|
if (!beat.image || beat.image.type !== imageType) {
|
|
@@ -14,12 +100,24 @@ const resolveTheme = (params) => {
|
|
|
14
100
|
}
|
|
15
101
|
return theme;
|
|
16
102
|
};
|
|
103
|
+
const resolveSlide = (params, converter = pathToDataUrl) => {
|
|
104
|
+
const { beat, imageRefs } = params;
|
|
105
|
+
if (!beat.image || beat.image.type !== imageType) {
|
|
106
|
+
throw new Error("resolveSlide called on non-slide beat");
|
|
107
|
+
}
|
|
108
|
+
const slide = beat.image.slide;
|
|
109
|
+
if (imageRefs && Object.keys(imageRefs).length > 0) {
|
|
110
|
+
return resolveSlideImageRefs(slide, imageRefs, converter);
|
|
111
|
+
}
|
|
112
|
+
return slide;
|
|
113
|
+
};
|
|
17
114
|
const processSlide = async (params) => {
|
|
18
115
|
const { beat, imagePath, canvasSize } = params;
|
|
19
116
|
if (!beat.image || beat.image.type !== imageType)
|
|
20
117
|
return;
|
|
21
118
|
const theme = resolveTheme(params);
|
|
22
|
-
const
|
|
119
|
+
const slide = resolveSlide(params, toFileUrl);
|
|
120
|
+
const html = generateSlideHTML(theme, slide);
|
|
23
121
|
await renderHTMLToImage(html, imagePath, canvasSize.width, canvasSize.height);
|
|
24
122
|
return imagePath;
|
|
25
123
|
};
|
|
@@ -28,7 +126,8 @@ const dumpHtml = async (params) => {
|
|
|
28
126
|
if (!beat.image || beat.image.type !== imageType)
|
|
29
127
|
return;
|
|
30
128
|
const theme = resolveTheme(params);
|
|
31
|
-
|
|
129
|
+
const slide = resolveSlide(params);
|
|
130
|
+
return generateSlideHTML(theme, slide);
|
|
32
131
|
};
|
|
33
132
|
export const process = processSlide;
|
|
34
133
|
export const path = parrotingImagePath;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mulmocast",
|
|
3
|
-
"version": "2.1
|
|
3
|
+
"version": "2.2.1",
|
|
4
4
|
"description": "",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.node.js",
|
|
@@ -17,7 +17,11 @@
|
|
|
17
17
|
"default": "./lib/data/index.js"
|
|
18
18
|
},
|
|
19
19
|
"./assets/*": "./assets/*",
|
|
20
|
-
"./scripts/*": "./scripts/*"
|
|
20
|
+
"./scripts/*": "./scripts/*",
|
|
21
|
+
"./tools/complete_script": {
|
|
22
|
+
"types": "./lib/tools/complete_script.d.ts",
|
|
23
|
+
"default": "./lib/tools/complete_script.js"
|
|
24
|
+
}
|
|
21
25
|
},
|
|
22
26
|
"bin": {
|
|
23
27
|
"mulmo": "lib/cli/bin.js",
|
|
@@ -30,6 +34,9 @@
|
|
|
30
34
|
"./assets/audio/silent60sec.mp3",
|
|
31
35
|
"./assets/html/",
|
|
32
36
|
"./assets/images/",
|
|
37
|
+
"./assets/schemas/",
|
|
38
|
+
"./assets/slide_themes/",
|
|
39
|
+
"./assets/styles/",
|
|
33
40
|
"./assets/templates/"
|
|
34
41
|
],
|
|
35
42
|
"directories": {
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$mulmocast": { "version": "1.1" },
|
|
3
|
+
"lang": "en",
|
|
4
|
+
"title": "Chart & Mermaid Content Blocks Demo",
|
|
5
|
+
"slideParams": {
|
|
6
|
+
"theme": {
|
|
7
|
+
"colors": {
|
|
8
|
+
"bg": "0F172A",
|
|
9
|
+
"bgCard": "1E293B",
|
|
10
|
+
"bgCardAlt": "334155",
|
|
11
|
+
"text": "F8FAFC",
|
|
12
|
+
"textMuted": "CBD5E1",
|
|
13
|
+
"textDim": "64748B",
|
|
14
|
+
"primary": "3B82F6",
|
|
15
|
+
"accent": "8B5CF6",
|
|
16
|
+
"success": "22C55E",
|
|
17
|
+
"warning": "F59E0B",
|
|
18
|
+
"danger": "EF4444",
|
|
19
|
+
"info": "14B8A6",
|
|
20
|
+
"highlight": "EC4899"
|
|
21
|
+
},
|
|
22
|
+
"fonts": { "title": "Georgia", "body": "Calibri", "mono": "Consolas" }
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
"beats": [
|
|
26
|
+
{
|
|
27
|
+
"text": "This slide shows a bar chart inside a columns layout.",
|
|
28
|
+
"image": {
|
|
29
|
+
"type": "slide",
|
|
30
|
+
"slide": {
|
|
31
|
+
"layout": "columns",
|
|
32
|
+
"title": "Quarterly Revenue",
|
|
33
|
+
"subtitle": "FY2025 performance overview",
|
|
34
|
+
"columns": [
|
|
35
|
+
{
|
|
36
|
+
"title": "Revenue by Quarter",
|
|
37
|
+
"accentColor": "primary",
|
|
38
|
+
"content": [
|
|
39
|
+
{
|
|
40
|
+
"type": "chart",
|
|
41
|
+
"title": "Revenue ($ millions)",
|
|
42
|
+
"chartData": {
|
|
43
|
+
"type": "bar",
|
|
44
|
+
"data": {
|
|
45
|
+
"labels": ["Q1", "Q2", "Q3", "Q4"],
|
|
46
|
+
"datasets": [
|
|
47
|
+
{
|
|
48
|
+
"label": "Revenue",
|
|
49
|
+
"data": [12, 19, 15, 24],
|
|
50
|
+
"backgroundColor": ["#3B82F6", "#8B5CF6", "#22C55E", "#F59E0B"]
|
|
51
|
+
}
|
|
52
|
+
]
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
]
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
"title": "Key Takeaways",
|
|
60
|
+
"accentColor": "success",
|
|
61
|
+
"content": [
|
|
62
|
+
{ "type": "metric", "value": "$70M", "label": "Total Revenue", "color": "primary" },
|
|
63
|
+
{ "type": "bullets", "items": ["Q4 strongest quarter", "24% QoQ growth in Q4", "Exceeded annual target by 8%"] }
|
|
64
|
+
]
|
|
65
|
+
}
|
|
66
|
+
]
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
"text": "This slide demonstrates a mermaid diagram in a split layout.",
|
|
72
|
+
"image": {
|
|
73
|
+
"type": "slide",
|
|
74
|
+
"slide": {
|
|
75
|
+
"layout": "split",
|
|
76
|
+
"left": {
|
|
77
|
+
"title": "System Architecture",
|
|
78
|
+
"accentColor": "accent",
|
|
79
|
+
"content": [
|
|
80
|
+
{
|
|
81
|
+
"type": "mermaid",
|
|
82
|
+
"title": "Data Flow",
|
|
83
|
+
"code": "graph TD\n A[Client] --> B[API Gateway]\n B --> C[Auth Service]\n B --> D[App Service]\n D --> E[Database]\n D --> F[Cache]"
|
|
84
|
+
}
|
|
85
|
+
]
|
|
86
|
+
},
|
|
87
|
+
"right": {
|
|
88
|
+
"title": "Components",
|
|
89
|
+
"accentColor": "info",
|
|
90
|
+
"content": [
|
|
91
|
+
{
|
|
92
|
+
"type": "bullets",
|
|
93
|
+
"items": [
|
|
94
|
+
"API Gateway handles routing",
|
|
95
|
+
"Auth Service manages tokens",
|
|
96
|
+
"App Service processes requests",
|
|
97
|
+
"Database stores persistent data",
|
|
98
|
+
"Cache layer for performance"
|
|
99
|
+
]
|
|
100
|
+
}
|
|
101
|
+
]
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
"text": "This slide combines both chart and mermaid in a comparison layout.",
|
|
108
|
+
"image": {
|
|
109
|
+
"type": "slide",
|
|
110
|
+
"slide": {
|
|
111
|
+
"layout": "comparison",
|
|
112
|
+
"title": "Pipeline Analysis",
|
|
113
|
+
"left": {
|
|
114
|
+
"title": "Conversion Funnel",
|
|
115
|
+
"accentColor": "primary",
|
|
116
|
+
"content": [
|
|
117
|
+
{
|
|
118
|
+
"type": "chart",
|
|
119
|
+
"chartData": {
|
|
120
|
+
"type": "doughnut",
|
|
121
|
+
"data": {
|
|
122
|
+
"labels": ["Leads", "Qualified", "Proposals", "Won"],
|
|
123
|
+
"datasets": [
|
|
124
|
+
{
|
|
125
|
+
"data": [500, 200, 80, 35],
|
|
126
|
+
"backgroundColor": ["#3B82F6", "#8B5CF6", "#22C55E", "#F59E0B"]
|
|
127
|
+
}
|
|
128
|
+
]
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
]
|
|
133
|
+
},
|
|
134
|
+
"right": {
|
|
135
|
+
"title": "Process Flow",
|
|
136
|
+
"accentColor": "accent",
|
|
137
|
+
"content": [
|
|
138
|
+
{
|
|
139
|
+
"type": "mermaid",
|
|
140
|
+
"code": "graph LR\n A[Lead] --> B[Qualify]\n B --> C[Propose]\n C --> D[Close]\n D --> E[Onboard]"
|
|
141
|
+
}
|
|
142
|
+
]
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
]
|
|
148
|
+
}
|