mulmocast 1.2.28 → 1.2.30
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/lib/actions/html.d.ts +3 -0
- package/lib/actions/html.js +62 -0
- package/lib/actions/image_agents.d.ts +4 -1
- package/lib/actions/image_agents.js +10 -1
- package/lib/actions/images.d.ts +6 -1
- package/lib/actions/images.js +2 -0
- package/lib/actions/index.d.ts +2 -0
- package/lib/actions/index.js +2 -0
- package/lib/actions/markdown.d.ts +3 -0
- package/lib/actions/markdown.js +61 -0
- package/lib/agents/image_replicate_agent.js +8 -33
- package/lib/agents/movie_replicate_agent.js +9 -7
- package/lib/cli/bin.js +4 -0
- package/lib/cli/commands/html/builder.d.ts +16 -0
- package/lib/cli/commands/html/builder.js +5 -0
- package/lib/cli/commands/html/handler.d.ts +4 -0
- package/lib/cli/commands/html/handler.js +11 -0
- package/lib/cli/commands/html/index.d.ts +4 -0
- package/lib/cli/commands/html/index.js +4 -0
- package/lib/cli/commands/markdown/builder.d.ts +16 -0
- package/lib/cli/commands/markdown/builder.js +5 -0
- package/lib/cli/commands/markdown/handler.d.ts +4 -0
- package/lib/cli/commands/markdown/handler.js +11 -0
- package/lib/cli/commands/markdown/index.d.ts +4 -0
- package/lib/cli/commands/markdown/index.js +4 -0
- package/lib/methods/mulmo_beat.d.ts +6 -1
- package/lib/types/schema.d.ts +26 -0
- package/lib/types/schema.js +4 -0
- package/lib/types/type.d.ts +1 -1
- package/lib/utils/context.d.ts +6 -0
- package/lib/utils/context.js +2 -0
- package/lib/utils/image_plugins/index.d.ts +7 -11
- package/lib/utils/image_plugins/index.js +1 -12
- package/lib/utils/image_plugins/markdown.d.ts +1 -0
- package/lib/utils/image_plugins/markdown.js +8 -1
- package/lib/utils/image_plugins/mermaid.d.ts +1 -0
- package/lib/utils/image_plugins/mermaid.js +9 -0
- package/lib/utils/image_plugins/text_slide.d.ts +1 -0
- package/lib/utils/image_plugins/text_slide.js +12 -1
- package/package.json +11 -9
- package/scripts/test/test_image_refs.json +10 -0
- package/scripts/test/test_markdown.json +60 -0
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import { isNull } from "graphai";
|
|
4
|
+
import { localizedText } from "../utils/utils.js";
|
|
5
|
+
import { writingMessage } from "../utils/file.js";
|
|
6
|
+
import { MulmoStudioContextMethods } from "../methods/mulmo_studio_context.js";
|
|
7
|
+
const generateHtmlContent = (context, imageWidth) => {
|
|
8
|
+
const { studio, multiLingual, lang = "en" } = context;
|
|
9
|
+
const title = studio.script.title || "MulmoCast Content";
|
|
10
|
+
const description = studio.script.description || "";
|
|
11
|
+
let html = `<h1>${title}</h1>\n\n`;
|
|
12
|
+
if (description) {
|
|
13
|
+
html += `${description}\n\n`;
|
|
14
|
+
}
|
|
15
|
+
studio.script.beats.forEach((beat, index) => {
|
|
16
|
+
const text = localizedText(beat, multiLingual?.[index], lang);
|
|
17
|
+
const studioBeat = studio.beats[index];
|
|
18
|
+
if (text.trim() || studioBeat?.html || studioBeat?.imageFile) {
|
|
19
|
+
if (studioBeat?.html) {
|
|
20
|
+
html += `${studioBeat.html}\n\n`;
|
|
21
|
+
}
|
|
22
|
+
else if (studioBeat?.imageFile && isNull(studioBeat.html)) {
|
|
23
|
+
const imagePath = path.relative(context.fileDirs.outDirPath, studioBeat.imageFile);
|
|
24
|
+
const altText = `Beat ${index + 1}`;
|
|
25
|
+
if (imageWidth) {
|
|
26
|
+
// Use HTML img tag for width control
|
|
27
|
+
html += `<img src="${imagePath}" alt="${altText}" width="${imageWidth}" />\n\n`;
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
// Use standard html image syntax
|
|
31
|
+
html += `<img src="${imagePath}" alt="${altText}" />\n\n`;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
if (text.trim()) {
|
|
35
|
+
html += `${text}\n\n`;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
return html;
|
|
40
|
+
};
|
|
41
|
+
export const htmlFilePath = (context) => {
|
|
42
|
+
const { studio, fileDirs, lang = "en" } = context;
|
|
43
|
+
// Add language suffix only when target language is different from script's original language
|
|
44
|
+
const langSuffix = studio.script.lang !== lang ? `_${lang}` : "";
|
|
45
|
+
const filename = `${studio.filename}${langSuffix}.html`;
|
|
46
|
+
return path.join(fileDirs.outDirPath, filename);
|
|
47
|
+
};
|
|
48
|
+
const generateHtml = async (context, imageWidth) => {
|
|
49
|
+
const outputHtmlPath = htmlFilePath(context);
|
|
50
|
+
const htmlContent = generateHtmlContent(context, imageWidth);
|
|
51
|
+
fs.writeFileSync(outputHtmlPath, htmlContent, "utf8");
|
|
52
|
+
writingMessage(outputHtmlPath);
|
|
53
|
+
};
|
|
54
|
+
export const html = async (context, imageWidth) => {
|
|
55
|
+
try {
|
|
56
|
+
MulmoStudioContextMethods.setSessionState(context, "html", true);
|
|
57
|
+
await generateHtml(context, imageWidth);
|
|
58
|
+
}
|
|
59
|
+
finally {
|
|
60
|
+
MulmoStudioContextMethods.setSessionState(context, "html", false);
|
|
61
|
+
}
|
|
62
|
+
};
|
|
@@ -12,9 +12,9 @@ export declare const imagePreprocessAgent: (namedInputs: {
|
|
|
12
12
|
htmlImageSystemPrompt: string;
|
|
13
13
|
} | {
|
|
14
14
|
imagePath: string | undefined;
|
|
15
|
+
movieFile: string | undefined;
|
|
15
16
|
referenceImageForMovie: string | undefined;
|
|
16
17
|
imageParams: MulmoImageParams;
|
|
17
|
-
movieFile: string | undefined;
|
|
18
18
|
soundEffectFile?: string;
|
|
19
19
|
soundEffectPrompt?: string;
|
|
20
20
|
soundEffectModel?: string;
|
|
@@ -35,6 +35,7 @@ export declare const imagePreprocessAgent: (namedInputs: {
|
|
|
35
35
|
agent: string;
|
|
36
36
|
movieParams: MulmoMovieParams;
|
|
37
37
|
};
|
|
38
|
+
markdown?: string;
|
|
38
39
|
htmlPrompt?: undefined;
|
|
39
40
|
htmlImageFile?: undefined;
|
|
40
41
|
htmlPath?: undefined;
|
|
@@ -64,6 +65,7 @@ export declare const imagePreprocessAgent: (namedInputs: {
|
|
|
64
65
|
agent: string;
|
|
65
66
|
movieParams: MulmoMovieParams;
|
|
66
67
|
};
|
|
68
|
+
markdown?: string;
|
|
67
69
|
htmlPrompt?: undefined;
|
|
68
70
|
htmlImageFile?: undefined;
|
|
69
71
|
htmlPath?: undefined;
|
|
@@ -96,6 +98,7 @@ export declare const imagePreprocessAgent: (namedInputs: {
|
|
|
96
98
|
agent: string;
|
|
97
99
|
movieParams: MulmoMovieParams;
|
|
98
100
|
};
|
|
101
|
+
markdown?: string;
|
|
99
102
|
htmlPrompt?: undefined;
|
|
100
103
|
htmlImageFile?: undefined;
|
|
101
104
|
htmlPath?: undefined;
|
|
@@ -60,8 +60,17 @@ export const imagePreprocessAgent = async (namedInputs) => {
|
|
|
60
60
|
if (beat.image) {
|
|
61
61
|
const plugin = MulmoBeatMethods.getPlugin(beat);
|
|
62
62
|
const pluginPath = plugin.path({ beat, context, imagePath, ...htmlStyle(context, beat) });
|
|
63
|
+
if (plugin.markdown) {
|
|
64
|
+
returnValue.markdown = plugin.markdown({ beat, context, imagePath, ...htmlStyle(context, beat) });
|
|
65
|
+
}
|
|
66
|
+
const isTypeMovie = beat.image.type === "movie";
|
|
63
67
|
// undefined prompt indicates that image generation is not needed
|
|
64
|
-
return {
|
|
68
|
+
return {
|
|
69
|
+
...returnValue,
|
|
70
|
+
imagePath: isTypeMovie ? undefined : pluginPath,
|
|
71
|
+
movieFile: isTypeMovie ? pluginPath : undefined,
|
|
72
|
+
referenceImageForMovie: pluginPath,
|
|
73
|
+
};
|
|
65
74
|
}
|
|
66
75
|
if (beat.moviePrompt && !beat.imagePrompt) {
|
|
67
76
|
return { ...returnValue, imagePath, imageFromMovie: true }; // no image prompt, only movie prompt
|
package/lib/actions/images.d.ts
CHANGED
|
@@ -35,9 +35,9 @@ export declare const beat_graph_data: {
|
|
|
35
35
|
htmlImageSystemPrompt: string;
|
|
36
36
|
} | {
|
|
37
37
|
imagePath: string | undefined;
|
|
38
|
+
movieFile: string | undefined;
|
|
38
39
|
referenceImageForMovie: string | undefined;
|
|
39
40
|
imageParams: MulmoImageParams;
|
|
40
|
-
movieFile: string | undefined;
|
|
41
41
|
soundEffectFile?: string;
|
|
42
42
|
soundEffectPrompt?: string;
|
|
43
43
|
soundEffectModel?: string;
|
|
@@ -58,6 +58,7 @@ export declare const beat_graph_data: {
|
|
|
58
58
|
agent: string;
|
|
59
59
|
movieParams: import("../types/type.js").MulmoMovieParams;
|
|
60
60
|
};
|
|
61
|
+
markdown?: string;
|
|
61
62
|
htmlPrompt?: undefined;
|
|
62
63
|
htmlImageFile?: undefined;
|
|
63
64
|
htmlPath?: undefined;
|
|
@@ -87,6 +88,7 @@ export declare const beat_graph_data: {
|
|
|
87
88
|
agent: string;
|
|
88
89
|
movieParams: import("../types/type.js").MulmoMovieParams;
|
|
89
90
|
};
|
|
91
|
+
markdown?: string;
|
|
90
92
|
htmlPrompt?: undefined;
|
|
91
93
|
htmlImageFile?: undefined;
|
|
92
94
|
htmlPath?: undefined;
|
|
@@ -119,6 +121,7 @@ export declare const beat_graph_data: {
|
|
|
119
121
|
agent: string;
|
|
120
122
|
movieParams: import("../types/type.js").MulmoMovieParams;
|
|
121
123
|
};
|
|
124
|
+
markdown?: string;
|
|
122
125
|
htmlPrompt?: undefined;
|
|
123
126
|
htmlImageFile?: undefined;
|
|
124
127
|
htmlPath?: undefined;
|
|
@@ -359,6 +362,7 @@ export declare const beat_graph_data: {
|
|
|
359
362
|
lipSyncFile: string;
|
|
360
363
|
hasMovieAudio: string;
|
|
361
364
|
htmlImageFile: string;
|
|
365
|
+
markdown: string;
|
|
362
366
|
};
|
|
363
367
|
output: {
|
|
364
368
|
imageFile: string;
|
|
@@ -367,6 +371,7 @@ export declare const beat_graph_data: {
|
|
|
367
371
|
lipSyncFile: string;
|
|
368
372
|
hasMovieAudio: string;
|
|
369
373
|
htmlImageFile: string;
|
|
374
|
+
markdown: string;
|
|
370
375
|
};
|
|
371
376
|
isResult: boolean;
|
|
372
377
|
};
|
package/lib/actions/images.js
CHANGED
|
@@ -285,6 +285,7 @@ export const beat_graph_data = {
|
|
|
285
285
|
lipSyncFile: ":preprocessor.lipSyncFile",
|
|
286
286
|
hasMovieAudio: ":audioChecker.hasMovieAudio",
|
|
287
287
|
htmlImageFile: ":preprocessor.htmlImageFile",
|
|
288
|
+
markdown: ":preprocessor.markdown",
|
|
288
289
|
},
|
|
289
290
|
output: {
|
|
290
291
|
imageFile: ".imageFile",
|
|
@@ -293,6 +294,7 @@ export const beat_graph_data = {
|
|
|
293
294
|
lipSyncFile: ".lipSyncFile",
|
|
294
295
|
hasMovieAudio: ".hasMovieAudio",
|
|
295
296
|
htmlImageFile: ".htmlImageFile",
|
|
297
|
+
markdown: ".markdown",
|
|
296
298
|
},
|
|
297
299
|
isResult: true,
|
|
298
300
|
},
|
package/lib/actions/index.d.ts
CHANGED
package/lib/actions/index.js
CHANGED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import { localizedText } from "../utils/utils.js";
|
|
3
|
+
import { writingMessage } from "../utils/file.js";
|
|
4
|
+
import { MulmoStudioContextMethods } from "../methods/mulmo_studio_context.js";
|
|
5
|
+
import path from "path";
|
|
6
|
+
const generateMarkdownContent = (context, imageWidth) => {
|
|
7
|
+
const { studio, multiLingual, lang = "en" } = context;
|
|
8
|
+
const title = studio.script.title || "MulmoCast Content";
|
|
9
|
+
const description = studio.script.description || "";
|
|
10
|
+
let markdown = `# ${title}\n\n`;
|
|
11
|
+
if (description) {
|
|
12
|
+
markdown += `${description}\n\n`;
|
|
13
|
+
}
|
|
14
|
+
studio.script.beats.forEach((beat, index) => {
|
|
15
|
+
const text = localizedText(beat, multiLingual?.[index], lang);
|
|
16
|
+
const studioBeat = studio.beats[index];
|
|
17
|
+
if (text.trim() || studioBeat?.markdown || studioBeat?.imageFile) {
|
|
18
|
+
if (studioBeat?.markdown) {
|
|
19
|
+
markdown += `${studioBeat.markdown}\n\n`;
|
|
20
|
+
}
|
|
21
|
+
else if (studioBeat?.imageFile) {
|
|
22
|
+
const imagePath = path.relative(context.fileDirs.outDirPath, studioBeat.imageFile);
|
|
23
|
+
if (imageWidth) {
|
|
24
|
+
// Use HTML img tag for width control
|
|
25
|
+
const altText = `Beat ${index + 1}`;
|
|
26
|
+
markdown += `<img src="${imagePath}" alt="${altText}" width="${imageWidth}" />\n\n`;
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
// Use standard markdown image syntax
|
|
30
|
+
markdown += `\n\n`;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
if (text.trim()) {
|
|
34
|
+
markdown += `${text}\n\n`;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
return markdown;
|
|
39
|
+
};
|
|
40
|
+
export const markdownFilePath = (context) => {
|
|
41
|
+
const { studio, fileDirs, lang = "en" } = context;
|
|
42
|
+
// Add language suffix only when target language is different from script's original language
|
|
43
|
+
const langSuffix = studio.script.lang !== lang ? `_${lang}` : "";
|
|
44
|
+
const filename = `${studio.filename}${langSuffix}.md`;
|
|
45
|
+
return path.join(fileDirs.outDirPath, filename);
|
|
46
|
+
};
|
|
47
|
+
const generateMarkdown = async (context, imageWidth) => {
|
|
48
|
+
const outputMarkdownPath = markdownFilePath(context);
|
|
49
|
+
const markdownContent = generateMarkdownContent(context, imageWidth);
|
|
50
|
+
fs.writeFileSync(outputMarkdownPath, markdownContent, "utf8");
|
|
51
|
+
writingMessage(outputMarkdownPath);
|
|
52
|
+
};
|
|
53
|
+
export const markdown = async (context, imageWidth) => {
|
|
54
|
+
try {
|
|
55
|
+
MulmoStudioContextMethods.setSessionState(context, "markdown", true);
|
|
56
|
+
await generateMarkdown(context, imageWidth);
|
|
57
|
+
}
|
|
58
|
+
finally {
|
|
59
|
+
MulmoStudioContextMethods.setSessionState(context, "markdown", false);
|
|
60
|
+
}
|
|
61
|
+
};
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
+
import { readFileSync } from "fs";
|
|
1
2
|
import { GraphAILogger } from "graphai";
|
|
2
3
|
import Replicate from "replicate";
|
|
3
4
|
import { getAspectRatio } from "./movie_replicate_agent.js";
|
|
4
5
|
import { provider2ImageAgent } from "../utils/provider2agent.js";
|
|
5
6
|
export const imageReplicateAgent = async ({ namedInputs, params, config, }) => {
|
|
6
|
-
const { prompt } = namedInputs;
|
|
7
|
+
const { prompt, referenceImages } = namedInputs;
|
|
7
8
|
const { canvasSize } = params;
|
|
8
9
|
const model = params.model ?? provider2ImageAgent.replicate.defaultModel;
|
|
9
10
|
const apiKey = config?.apiKey;
|
|
@@ -15,40 +16,14 @@ export const imageReplicateAgent = async ({ namedInputs, params, config, }) => {
|
|
|
15
16
|
});
|
|
16
17
|
const input = {
|
|
17
18
|
prompt,
|
|
18
|
-
|
|
19
|
-
height: canvasSize.height,
|
|
19
|
+
aspect_ratio: getAspectRatio(canvasSize),
|
|
20
20
|
};
|
|
21
|
-
if (
|
|
22
|
-
input.
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
input.height = Math.round(input.height * ratio);
|
|
27
|
-
}
|
|
28
|
-
if (input.height < 1024) {
|
|
29
|
-
const ratio = 1024 / input.height;
|
|
30
|
-
input.width = Math.round(input.width * ratio);
|
|
31
|
-
input.height = 1024;
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
else if (model === "qwen/qwen-image") {
|
|
35
|
-
input.aspect_ratio = getAspectRatio(canvasSize);
|
|
36
|
-
}
|
|
37
|
-
// Add image if provided (for image-to-image generation)
|
|
38
|
-
/*
|
|
39
|
-
if (imagePath) {
|
|
40
|
-
const buffer = readFileSync(imagePath);
|
|
41
|
-
const base64Image = `data:image/png;base64,${buffer.toString("base64")}`;
|
|
42
|
-
const start_image = provider2MovieAgent.replicate.modelParams[model]?.start_image;
|
|
43
|
-
if (start_image === "first_frame_image" || start_image === "image" || start_image === "start_image") {
|
|
44
|
-
input[start_image] = base64Image;
|
|
45
|
-
} else if (start_image === undefined) {
|
|
46
|
-
throw new Error(`Model ${model} does not support image-to-video generation`);
|
|
47
|
-
} else {
|
|
48
|
-
input.image = base64Image;
|
|
49
|
-
}
|
|
21
|
+
if (referenceImages && referenceImages.length > 0) {
|
|
22
|
+
input.image_input = referenceImages.map((image) => {
|
|
23
|
+
const buffer = readFileSync(image);
|
|
24
|
+
return `data:image/png;base64,${buffer.toString("base64")}`;
|
|
25
|
+
});
|
|
50
26
|
}
|
|
51
|
-
*/
|
|
52
27
|
try {
|
|
53
28
|
const output = await replicate.run(model, { input });
|
|
54
29
|
// Download the generated video
|
|
@@ -54,15 +54,17 @@ async function generateMovie(model, apiKey, prompt, imagePath, aspectRatio, dura
|
|
|
54
54
|
}
|
|
55
55
|
}
|
|
56
56
|
export const getAspectRatio = (canvasSize) => {
|
|
57
|
-
|
|
57
|
+
const ratio = canvasSize.width / canvasSize.height;
|
|
58
|
+
const tolerance = 0.1;
|
|
59
|
+
if (ratio > 4 / 3 + tolerance)
|
|
58
60
|
return "16:9";
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
}
|
|
63
|
-
else {
|
|
61
|
+
if (ratio > 4 / 3 - tolerance)
|
|
62
|
+
return "4:3";
|
|
63
|
+
if (ratio > 3 / 4 + tolerance)
|
|
64
64
|
return "1:1";
|
|
65
|
-
|
|
65
|
+
if (ratio > 3 / 4 - tolerance)
|
|
66
|
+
return "3:4";
|
|
67
|
+
return "9:16";
|
|
66
68
|
};
|
|
67
69
|
export const movieReplicateAgent = async ({ namedInputs, params, config, }) => {
|
|
68
70
|
const { prompt, imagePath } = namedInputs;
|
package/lib/cli/bin.js
CHANGED
|
@@ -10,6 +10,8 @@ import * as audioCmd from "./commands/audio/index.js";
|
|
|
10
10
|
import * as imagesCmd from "./commands/image/index.js";
|
|
11
11
|
import * as movieCmd from "./commands/movie/index.js";
|
|
12
12
|
import * as pdfCmd from "./commands/pdf/index.js";
|
|
13
|
+
import * as markdownCmd from "./commands/markdown/index.js";
|
|
14
|
+
import * as htmlCmd from "./commands/html/index.js";
|
|
13
15
|
import * as toolCmd from "./commands/tool/index.js";
|
|
14
16
|
import { GraphAILogger } from "graphai";
|
|
15
17
|
const __filename = fileURLToPath(import.meta.url);
|
|
@@ -32,6 +34,8 @@ export const main = async () => {
|
|
|
32
34
|
.command(imagesCmd)
|
|
33
35
|
.command(movieCmd)
|
|
34
36
|
.command(pdfCmd)
|
|
37
|
+
.command(markdownCmd)
|
|
38
|
+
.command(htmlCmd)
|
|
35
39
|
.command(toolCmd)
|
|
36
40
|
.demandCommand()
|
|
37
41
|
.strict()
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { Argv } from "yargs";
|
|
2
|
+
export declare const builder: (yargs: Argv) => Argv<{
|
|
3
|
+
o: string | undefined;
|
|
4
|
+
} & {
|
|
5
|
+
b: string | undefined;
|
|
6
|
+
} & {
|
|
7
|
+
l: string | undefined;
|
|
8
|
+
} & {
|
|
9
|
+
f: boolean;
|
|
10
|
+
} & {
|
|
11
|
+
p: string | undefined;
|
|
12
|
+
} & {
|
|
13
|
+
file: string | undefined;
|
|
14
|
+
} & {
|
|
15
|
+
image_width: string | undefined;
|
|
16
|
+
}>;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { images, html } from "../../../actions/index.js";
|
|
2
|
+
import { initializeContext, runTranslateIfNeeded } from "../../helpers.js";
|
|
3
|
+
export const handler = async (argv) => {
|
|
4
|
+
const context = await initializeContext(argv);
|
|
5
|
+
if (!context) {
|
|
6
|
+
process.exit(1);
|
|
7
|
+
}
|
|
8
|
+
await runTranslateIfNeeded(context);
|
|
9
|
+
await images(context);
|
|
10
|
+
await html(context, argv.image_width);
|
|
11
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { Argv } from "yargs";
|
|
2
|
+
export declare const builder: (yargs: Argv) => Argv<{
|
|
3
|
+
o: string | undefined;
|
|
4
|
+
} & {
|
|
5
|
+
b: string | undefined;
|
|
6
|
+
} & {
|
|
7
|
+
l: string | undefined;
|
|
8
|
+
} & {
|
|
9
|
+
f: boolean;
|
|
10
|
+
} & {
|
|
11
|
+
p: string | undefined;
|
|
12
|
+
} & {
|
|
13
|
+
file: string | undefined;
|
|
14
|
+
} & {
|
|
15
|
+
image_width: string | undefined;
|
|
16
|
+
}>;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { images, markdown } from "../../../actions/index.js";
|
|
2
|
+
import { initializeContext, runTranslateIfNeeded } from "../../helpers.js";
|
|
3
|
+
export const handler = async (argv) => {
|
|
4
|
+
const context = await initializeContext(argv);
|
|
5
|
+
if (!context) {
|
|
6
|
+
process.exit(1);
|
|
7
|
+
}
|
|
8
|
+
await runTranslateIfNeeded(context);
|
|
9
|
+
await images(context);
|
|
10
|
+
await markdown(context, argv.image_width);
|
|
11
|
+
};
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import { MulmoBeat } from "../types/index.js";
|
|
2
2
|
export declare const MulmoBeatMethods: {
|
|
3
3
|
getHtmlPrompt(beat: MulmoBeat): string | undefined;
|
|
4
|
-
getPlugin(beat: MulmoBeat):
|
|
4
|
+
getPlugin(beat: MulmoBeat): {
|
|
5
|
+
imageType: string;
|
|
6
|
+
process: (params: import("../types/type.js").ImageProcessorParams) => Promise<string | undefined> | void;
|
|
7
|
+
path: (params: import("../types/type.js").ImageProcessorParams) => string | undefined;
|
|
8
|
+
markdown?: (params: import("../types/type.js").ImageProcessorParams) => string | undefined;
|
|
9
|
+
};
|
|
5
10
|
getImageReferenceForImageGenerator(beat: MulmoBeat, imageRefs: Record<string, string>): string[];
|
|
6
11
|
};
|
package/lib/types/schema.d.ts
CHANGED
|
@@ -5777,8 +5777,12 @@ export declare const mulmoStudioBeatSchema: z.ZodObject<{
|
|
|
5777
5777
|
lipSyncFile: z.ZodOptional<z.ZodString>;
|
|
5778
5778
|
captionFile: z.ZodOptional<z.ZodString>;
|
|
5779
5779
|
htmlImageFile: z.ZodOptional<z.ZodString>;
|
|
5780
|
+
markdown: z.ZodOptional<z.ZodString>;
|
|
5781
|
+
html: z.ZodOptional<z.ZodString>;
|
|
5780
5782
|
}, "strict", z.ZodTypeAny, {
|
|
5781
5783
|
duration?: number | undefined;
|
|
5784
|
+
markdown?: string | undefined;
|
|
5785
|
+
html?: string | undefined;
|
|
5782
5786
|
id?: string | undefined;
|
|
5783
5787
|
startAt?: number | undefined;
|
|
5784
5788
|
hash?: string | undefined;
|
|
@@ -5795,6 +5799,8 @@ export declare const mulmoStudioBeatSchema: z.ZodObject<{
|
|
|
5795
5799
|
htmlImageFile?: string | undefined;
|
|
5796
5800
|
}, {
|
|
5797
5801
|
duration?: number | undefined;
|
|
5802
|
+
markdown?: string | undefined;
|
|
5803
|
+
html?: string | undefined;
|
|
5798
5804
|
id?: string | undefined;
|
|
5799
5805
|
startAt?: number | undefined;
|
|
5800
5806
|
hash?: string | undefined;
|
|
@@ -6027,18 +6033,24 @@ export declare const mulmoSessionStateSchema: z.ZodObject<{
|
|
|
6027
6033
|
multiLingual: z.ZodBoolean;
|
|
6028
6034
|
caption: z.ZodBoolean;
|
|
6029
6035
|
pdf: z.ZodBoolean;
|
|
6036
|
+
markdown: z.ZodBoolean;
|
|
6037
|
+
html: z.ZodBoolean;
|
|
6030
6038
|
}, "strip", z.ZodTypeAny, {
|
|
6031
6039
|
image: boolean;
|
|
6032
6040
|
video: boolean;
|
|
6033
6041
|
audio: boolean;
|
|
6042
|
+
markdown: boolean;
|
|
6034
6043
|
pdf: boolean;
|
|
6044
|
+
html: boolean;
|
|
6035
6045
|
multiLingual: boolean;
|
|
6036
6046
|
caption: boolean;
|
|
6037
6047
|
}, {
|
|
6038
6048
|
image: boolean;
|
|
6039
6049
|
video: boolean;
|
|
6040
6050
|
audio: boolean;
|
|
6051
|
+
markdown: boolean;
|
|
6041
6052
|
pdf: boolean;
|
|
6053
|
+
html: boolean;
|
|
6042
6054
|
multiLingual: boolean;
|
|
6043
6055
|
caption: boolean;
|
|
6044
6056
|
}>;
|
|
@@ -6078,7 +6090,9 @@ export declare const mulmoSessionStateSchema: z.ZodObject<{
|
|
|
6078
6090
|
image: boolean;
|
|
6079
6091
|
video: boolean;
|
|
6080
6092
|
audio: boolean;
|
|
6093
|
+
markdown: boolean;
|
|
6081
6094
|
pdf: boolean;
|
|
6095
|
+
html: boolean;
|
|
6082
6096
|
multiLingual: boolean;
|
|
6083
6097
|
caption: boolean;
|
|
6084
6098
|
};
|
|
@@ -6098,7 +6112,9 @@ export declare const mulmoSessionStateSchema: z.ZodObject<{
|
|
|
6098
6112
|
image: boolean;
|
|
6099
6113
|
video: boolean;
|
|
6100
6114
|
audio: boolean;
|
|
6115
|
+
markdown: boolean;
|
|
6101
6116
|
pdf: boolean;
|
|
6117
|
+
html: boolean;
|
|
6102
6118
|
multiLingual: boolean;
|
|
6103
6119
|
caption: boolean;
|
|
6104
6120
|
};
|
|
@@ -8479,8 +8495,12 @@ export declare const mulmoStudioSchema: z.ZodObject<{
|
|
|
8479
8495
|
lipSyncFile: z.ZodOptional<z.ZodString>;
|
|
8480
8496
|
captionFile: z.ZodOptional<z.ZodString>;
|
|
8481
8497
|
htmlImageFile: z.ZodOptional<z.ZodString>;
|
|
8498
|
+
markdown: z.ZodOptional<z.ZodString>;
|
|
8499
|
+
html: z.ZodOptional<z.ZodString>;
|
|
8482
8500
|
}, "strict", z.ZodTypeAny, {
|
|
8483
8501
|
duration?: number | undefined;
|
|
8502
|
+
markdown?: string | undefined;
|
|
8503
|
+
html?: string | undefined;
|
|
8484
8504
|
id?: string | undefined;
|
|
8485
8505
|
startAt?: number | undefined;
|
|
8486
8506
|
hash?: string | undefined;
|
|
@@ -8497,6 +8517,8 @@ export declare const mulmoStudioSchema: z.ZodObject<{
|
|
|
8497
8517
|
htmlImageFile?: string | undefined;
|
|
8498
8518
|
}, {
|
|
8499
8519
|
duration?: number | undefined;
|
|
8520
|
+
markdown?: string | undefined;
|
|
8521
|
+
html?: string | undefined;
|
|
8500
8522
|
id?: string | undefined;
|
|
8501
8523
|
startAt?: number | undefined;
|
|
8502
8524
|
hash?: string | undefined;
|
|
@@ -8515,6 +8537,8 @@ export declare const mulmoStudioSchema: z.ZodObject<{
|
|
|
8515
8537
|
}, "strict", z.ZodTypeAny, {
|
|
8516
8538
|
beats: {
|
|
8517
8539
|
duration?: number | undefined;
|
|
8540
|
+
markdown?: string | undefined;
|
|
8541
|
+
html?: string | undefined;
|
|
8518
8542
|
id?: string | undefined;
|
|
8519
8543
|
startAt?: number | undefined;
|
|
8520
8544
|
hash?: string | undefined;
|
|
@@ -8862,6 +8886,8 @@ export declare const mulmoStudioSchema: z.ZodObject<{
|
|
|
8862
8886
|
}, {
|
|
8863
8887
|
beats: {
|
|
8864
8888
|
duration?: number | undefined;
|
|
8889
|
+
markdown?: string | undefined;
|
|
8890
|
+
html?: string | undefined;
|
|
8865
8891
|
id?: string | undefined;
|
|
8866
8892
|
startAt?: number | undefined;
|
|
8867
8893
|
hash?: string | undefined;
|
package/lib/types/schema.js
CHANGED
|
@@ -404,6 +404,8 @@ export const mulmoStudioBeatSchema = z
|
|
|
404
404
|
lipSyncFile: z.string().optional(), // path to the lip sync file
|
|
405
405
|
captionFile: z.string().optional(), // path to the caption image
|
|
406
406
|
htmlImageFile: z.string().optional(), // path to the html image
|
|
407
|
+
markdown: z.string().optional(), // markdown string (alternative to image)
|
|
408
|
+
html: z.string().optional(), // html string (alternative to image)
|
|
407
409
|
})
|
|
408
410
|
.strict();
|
|
409
411
|
export const mulmoStudioMultiLingualDataSchema = z.object({
|
|
@@ -424,6 +426,8 @@ export const mulmoSessionStateSchema = z.object({
|
|
|
424
426
|
multiLingual: z.boolean(),
|
|
425
427
|
caption: z.boolean(),
|
|
426
428
|
pdf: z.boolean(),
|
|
429
|
+
markdown: z.boolean(),
|
|
430
|
+
html: z.boolean(),
|
|
427
431
|
}),
|
|
428
432
|
inBeatSession: z.object({
|
|
429
433
|
audio: z.record(z.string(), z.boolean()),
|
package/lib/types/type.d.ts
CHANGED
|
@@ -85,7 +85,7 @@ export type Text2HtmlAgentInfo = {
|
|
|
85
85
|
};
|
|
86
86
|
export type BeatMediaType = "movie" | "image";
|
|
87
87
|
export type StoryToScriptGenerateMode = (typeof storyToScriptGenerateMode)[keyof typeof storyToScriptGenerateMode];
|
|
88
|
-
export type SessionType = "audio" | "image" | "video" | "multiLingual" | "caption" | "pdf";
|
|
88
|
+
export type SessionType = "audio" | "image" | "video" | "multiLingual" | "caption" | "pdf" | "markdown" | "html";
|
|
89
89
|
export type BeatSessionType = "audio" | "image" | "multiLingual" | "caption" | "movie" | "html" | "imageReference" | "soundEffect" | "lipSync";
|
|
90
90
|
export type SessionProgressEvent = {
|
|
91
91
|
kind: "session";
|
package/lib/utils/context.d.ts
CHANGED
|
@@ -2,6 +2,8 @@ import type { MulmoStudioBeat, MulmoScript, MulmoPresentationStyle, MulmoStudioM
|
|
|
2
2
|
export declare const createStudioData: (_mulmoScript: MulmoScript, fileName: string, videoCaptionLang?: string, presentationStyle?: MulmoPresentationStyle | null) => {
|
|
3
3
|
beats: {
|
|
4
4
|
duration?: number | undefined;
|
|
5
|
+
markdown?: string | undefined;
|
|
6
|
+
html?: string | undefined;
|
|
5
7
|
id?: string | undefined;
|
|
6
8
|
startAt?: number | undefined;
|
|
7
9
|
hash?: string | undefined;
|
|
@@ -354,6 +356,8 @@ export declare const initializeContextFromFiles: (files: FileObject, raiseError:
|
|
|
354
356
|
studio: {
|
|
355
357
|
beats: {
|
|
356
358
|
duration?: number | undefined;
|
|
359
|
+
markdown?: string | undefined;
|
|
360
|
+
html?: string | undefined;
|
|
357
361
|
id?: string | undefined;
|
|
358
362
|
startAt?: number | undefined;
|
|
359
363
|
hash?: string | undefined;
|
|
@@ -831,6 +835,8 @@ export declare const initializeContextFromFiles: (files: FileObject, raiseError:
|
|
|
831
835
|
multiLingual: boolean;
|
|
832
836
|
caption: boolean;
|
|
833
837
|
pdf: boolean;
|
|
838
|
+
markdown: boolean;
|
|
839
|
+
html: boolean;
|
|
834
840
|
};
|
|
835
841
|
inBeatSession: {
|
|
836
842
|
audio: {};
|
package/lib/utils/context.js
CHANGED
|
@@ -1,11 +1,7 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
import * as pluginBeat from "./beat.js";
|
|
9
|
-
import * as pluginVoiceOver from "./voice_over.js";
|
|
10
|
-
import * as pluginVision from "./vision.js";
|
|
11
|
-
export declare const findImagePlugin: (imageType?: string) => typeof pluginTextSlide | typeof pluginMarkdown | typeof pluginChart | typeof pluginMermaid | typeof pluginHtmlTailwind | typeof pluginImage | typeof pluginMovie | typeof pluginBeat | typeof pluginVoiceOver | typeof pluginVision | undefined;
|
|
1
|
+
import { ImageProcessorParams } from "../../types/index.js";
|
|
2
|
+
export declare const findImagePlugin: (imageType?: string) => {
|
|
3
|
+
imageType: string;
|
|
4
|
+
process: (params: ImageProcessorParams) => Promise<string | undefined> | void;
|
|
5
|
+
path: (params: ImageProcessorParams) => string | undefined;
|
|
6
|
+
markdown?: (params: ImageProcessorParams) => string | undefined;
|
|
7
|
+
} | undefined;
|
|
@@ -8,18 +8,7 @@ import * as pluginMovie from "./movie.js";
|
|
|
8
8
|
import * as pluginBeat from "./beat.js";
|
|
9
9
|
import * as pluginVoiceOver from "./voice_over.js";
|
|
10
10
|
import * as pluginVision from "./vision.js";
|
|
11
|
-
const imagePlugins = [
|
|
12
|
-
pluginTextSlide,
|
|
13
|
-
pluginMarkdown,
|
|
14
|
-
pluginImage,
|
|
15
|
-
pluginChart,
|
|
16
|
-
pluginMermaid,
|
|
17
|
-
pluginMovie,
|
|
18
|
-
pluginHtmlTailwind,
|
|
19
|
-
pluginBeat,
|
|
20
|
-
pluginVoiceOver,
|
|
21
|
-
pluginVision,
|
|
22
|
-
];
|
|
11
|
+
const imagePlugins = [pluginTextSlide, pluginMarkdown, pluginImage, pluginChart, pluginMermaid, pluginMovie, pluginHtmlTailwind, pluginBeat, pluginVoiceOver, pluginVision];
|
|
23
12
|
export const findImagePlugin = (imageType) => {
|
|
24
13
|
return imagePlugins.find((plugin) => plugin.imageType === imageType);
|
|
25
14
|
};
|
|
@@ -2,3 +2,4 @@ import { ImageProcessorParams } from "../../types/index.js";
|
|
|
2
2
|
export declare const imageType = "markdown";
|
|
3
3
|
export declare const process: (params: ImageProcessorParams) => Promise<string | undefined>;
|
|
4
4
|
export declare const path: (params: ImageProcessorParams) => string;
|
|
5
|
+
export declare const markdown: (params: ImageProcessorParams) => string | undefined;
|
|
@@ -5,9 +5,16 @@ const processMarkdown = async (params) => {
|
|
|
5
5
|
const { beat, imagePath, textSlideStyle, canvasSize } = params;
|
|
6
6
|
if (!beat.image || beat.image.type !== imageType)
|
|
7
7
|
return;
|
|
8
|
-
const markdown =
|
|
8
|
+
const markdown = dumpMarkdown(params) ?? "";
|
|
9
9
|
await renderMarkdownToImage(markdown, textSlideStyle, imagePath, canvasSize.width, canvasSize.height);
|
|
10
10
|
return imagePath;
|
|
11
11
|
};
|
|
12
|
+
const dumpMarkdown = (params) => {
|
|
13
|
+
const { beat } = params;
|
|
14
|
+
if (!beat.image || beat.image.type !== imageType)
|
|
15
|
+
return;
|
|
16
|
+
return Array.isArray(beat.image.markdown) ? beat.image.markdown.join("\n") : beat.image.markdown;
|
|
17
|
+
};
|
|
12
18
|
export const process = processMarkdown;
|
|
13
19
|
export const path = parrotingImagePath;
|
|
20
|
+
export const markdown = dumpMarkdown;
|
|
@@ -2,3 +2,4 @@ import { ImageProcessorParams } from "../../types/index.js";
|
|
|
2
2
|
export declare const imageType = "mermaid";
|
|
3
3
|
export declare const process: (params: ImageProcessorParams) => Promise<string | undefined>;
|
|
4
4
|
export declare const path: (params: ImageProcessorParams) => string;
|
|
5
|
+
export declare const markdown: (params: ImageProcessorParams) => string | undefined;
|
|
@@ -19,5 +19,14 @@ const processMermaid = async (params) => {
|
|
|
19
19
|
}
|
|
20
20
|
return imagePath;
|
|
21
21
|
};
|
|
22
|
+
const dumpMarkdown = (params) => {
|
|
23
|
+
const { beat } = params;
|
|
24
|
+
if (!beat.image || beat.image.type !== imageType)
|
|
25
|
+
return;
|
|
26
|
+
if (beat.image.code.kind !== "text")
|
|
27
|
+
return; // support only text for now
|
|
28
|
+
return `\`\`\`mermaid\n${beat.image.code.text}\n\`\`\``;
|
|
29
|
+
};
|
|
22
30
|
export const process = processMermaid;
|
|
23
31
|
export const path = parrotingImagePath;
|
|
32
|
+
export const markdown = dumpMarkdown;
|
|
@@ -2,3 +2,4 @@ import { ImageProcessorParams } from "../../types/index.js";
|
|
|
2
2
|
export declare const imageType = "textSlide";
|
|
3
3
|
export declare const process: (params: ImageProcessorParams) => Promise<string | undefined>;
|
|
4
4
|
export declare const path: (params: ImageProcessorParams) => string;
|
|
5
|
+
export declare const markdown: (params: ImageProcessorParams) => string | undefined;
|
|
@@ -6,7 +6,7 @@ const processTextSlide = async (params) => {
|
|
|
6
6
|
if (!beat.image || beat.image.type !== imageType)
|
|
7
7
|
return;
|
|
8
8
|
const slide = beat.image.slide;
|
|
9
|
-
const markdown =
|
|
9
|
+
const markdown = dumpMarkdown(params) ?? "";
|
|
10
10
|
const topMargin = (() => {
|
|
11
11
|
if (slide.bullets?.length && slide.bullets.length > 0) {
|
|
12
12
|
return "";
|
|
@@ -17,5 +17,16 @@ const processTextSlide = async (params) => {
|
|
|
17
17
|
await renderMarkdownToImage(markdown, textSlideStyle + topMargin, imagePath, canvasSize.width, canvasSize.height);
|
|
18
18
|
return imagePath;
|
|
19
19
|
};
|
|
20
|
+
const dumpMarkdown = (params) => {
|
|
21
|
+
const { beat } = params;
|
|
22
|
+
if (!beat.image || beat.image.type !== imageType)
|
|
23
|
+
return;
|
|
24
|
+
const slide = beat.image.slide;
|
|
25
|
+
const titleString = slide.title ? `# ${slide.title}\n` : "";
|
|
26
|
+
const subtitleString = slide.subtitle ? `## ${slide.subtitle}\n` : "";
|
|
27
|
+
const bulletsString = (slide.bullets ?? []).map((text) => `- ${text}`).join("\n");
|
|
28
|
+
return `${titleString}${subtitleString}${bulletsString}`;
|
|
29
|
+
};
|
|
20
30
|
export const process = processTextSlide;
|
|
21
31
|
export const path = parrotingImagePath;
|
|
32
|
+
export const markdown = dumpMarkdown;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mulmocast",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.30",
|
|
4
4
|
"description": "",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.node.js",
|
|
@@ -49,6 +49,8 @@
|
|
|
49
49
|
"scripting": "npx tsx ./src/cli/bin.ts tool scripting",
|
|
50
50
|
"prompt": "npx tsx ./src/cli/bin.ts tool prompt",
|
|
51
51
|
"schema": "npx tsx ./src/cli/bin.ts tool schema",
|
|
52
|
+
"markdown": "npx tsx ./src/cli/bin.ts markdown",
|
|
53
|
+
"html": "npx tsx ./src/cli/bin.ts html",
|
|
52
54
|
"story_to_script": "npx tsx ./src/cli/bin.ts tool story_to_script",
|
|
53
55
|
"whisper": "npx tsx ./src/cli/bin.ts tool whisper",
|
|
54
56
|
"latest": "yarn upgrade-interactive --latest",
|
|
@@ -67,7 +69,7 @@
|
|
|
67
69
|
"homepage": "https://github.com/receptron/mulmocast-cli#readme",
|
|
68
70
|
"dependencies": {
|
|
69
71
|
"@google-cloud/text-to-speech": "^6.3.0",
|
|
70
|
-
"@google/genai": "^1.
|
|
72
|
+
"@google/genai": "^1.19.0",
|
|
71
73
|
"@graphai/anthropic_agent": "^2.0.11",
|
|
72
74
|
"@graphai/browserless_agent": "^2.0.1",
|
|
73
75
|
"@graphai/gemini_agent": "^2.0.1",
|
|
@@ -77,20 +79,20 @@
|
|
|
77
79
|
"@graphai/stream_agent_filter": "^2.0.2",
|
|
78
80
|
"@graphai/vanilla": "^2.0.12",
|
|
79
81
|
"@graphai/vanilla_node_agents": "^2.0.4",
|
|
80
|
-
"@inquirer/input": "^4.2.
|
|
81
|
-
"@inquirer/select": "^4.3.
|
|
82
|
-
"@modelcontextprotocol/sdk": "^1.
|
|
82
|
+
"@inquirer/input": "^4.2.4",
|
|
83
|
+
"@inquirer/select": "^4.3.4",
|
|
84
|
+
"@modelcontextprotocol/sdk": "^1.18.0",
|
|
83
85
|
"@mozilla/readability": "^0.6.0",
|
|
84
86
|
"@tavily/core": "^0.5.11",
|
|
85
87
|
"clipboardy": "^4.0.0",
|
|
86
88
|
"dotenv": "^17.2.2",
|
|
87
89
|
"fluent-ffmpeg": "^2.1.3",
|
|
88
90
|
"graphai": "^2.0.14",
|
|
89
|
-
"jsdom": "^
|
|
90
|
-
"marked": "^16.
|
|
91
|
-
"mulmocast-vision": "^0.
|
|
91
|
+
"jsdom": "^27.0.0",
|
|
92
|
+
"marked": "^16.3.0",
|
|
93
|
+
"mulmocast-vision": "^1.0.2",
|
|
92
94
|
"ora": "^8.2.0",
|
|
93
|
-
"puppeteer": "^24.
|
|
95
|
+
"puppeteer": "^24.20.0",
|
|
94
96
|
"replicate": "^1.1.0",
|
|
95
97
|
"yaml": "^2.8.1",
|
|
96
98
|
"yargs": "^18.0.0",
|
|
@@ -55,6 +55,16 @@
|
|
|
55
55
|
"provider": "google",
|
|
56
56
|
"model": "gemini-2.5-flash-image-preview"
|
|
57
57
|
}
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
"id": "seedream-4",
|
|
61
|
+
"text": "Hello World with a witch and a broom with Seadream-4",
|
|
62
|
+
"imagePrompt": "Saying hello to the world",
|
|
63
|
+
"imageNames": ["witch", "broom"],
|
|
64
|
+
"imageParams": {
|
|
65
|
+
"provider": "replicate",
|
|
66
|
+
"model": "bytedance/seedream-4"
|
|
67
|
+
}
|
|
58
68
|
}
|
|
59
69
|
]
|
|
60
70
|
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$mulmocast": {
|
|
3
|
+
"version": "1.1"
|
|
4
|
+
},
|
|
5
|
+
"lang": "en",
|
|
6
|
+
"title": "Test Markdown",
|
|
7
|
+
"description": "This is a test markdown file.",
|
|
8
|
+
"beats": [
|
|
9
|
+
{
|
|
10
|
+
"text": "Hello World",
|
|
11
|
+
"image": {
|
|
12
|
+
"type": "markdown",
|
|
13
|
+
"markdown": []
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
"text": "Hello World",
|
|
18
|
+
"image": {
|
|
19
|
+
"type": "markdown",
|
|
20
|
+
"markdown": ["## Chapter 1"]
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"image": {
|
|
25
|
+
"type": "markdown",
|
|
26
|
+
"markdown": ["## Chapter 2", "- Hello", "- World"]
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
"image": {
|
|
31
|
+
"type": "textSlide",
|
|
32
|
+
"slide": {
|
|
33
|
+
"title": "Chapter 3",
|
|
34
|
+
"subtitle": "Subtitle",
|
|
35
|
+
"bullets": ["Hello", "World"]
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
"image": {
|
|
41
|
+
"type": "textSlide",
|
|
42
|
+
"slide": {
|
|
43
|
+
"title": "",
|
|
44
|
+
"subtitle": "Chapter 4",
|
|
45
|
+
"bullets": ["Hello", "World"]
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
"image": {
|
|
51
|
+
"type": "mermaid",
|
|
52
|
+
"title": "Business Process Flow",
|
|
53
|
+
"code": {
|
|
54
|
+
"kind": "text",
|
|
55
|
+
"text": "graph LR\n A[Market Research] --> B[Product Planning]\n B --> C[Development]\n C --> D[Testing]\n D --> E[Manufacturing]\n E --> F[Marketing]\n F --> G[Sales]\n G --> H[Customer Support]\n H --> A"
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
]
|
|
60
|
+
}
|