mulmocast 1.2.16 → 1.2.17

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.
@@ -1,7 +1,7 @@
1
1
  {
2
- "title": "Ghibli comic style",
3
- "description": "Template for Ghibli-style comic presentation.",
4
- "systemPrompt": "Another AI will generate comic strips for each beat based on the text description of that beat. Mention the reference in one of beats, if it exists. Use the JSON below as a template.",
2
+ "title": "Ghibli-style comic.",
3
+ "description": "Ghibli-style comic.",
4
+ "systemPrompt": "Another AI will generate comic strips for each beat based on the imagePrompt of that beat. Mention the reference in one of beats, if it exists. Use the JSON below as a template.",
5
5
  "presentationStyle": {
6
6
  "$mulmocast": {
7
7
  "version": "1.1",
@@ -0,0 +1,30 @@
1
+ {
2
+ "title": "Ghibli-style comic strips with nano banana.",
3
+ "description": "Ghibli-style comic strips with nano banana.",
4
+ "systemPrompt": "Another AI will generate comic strips for each beat based on the text description of that beat. Mention the reference in one of beats, if it exists. Use the JSON below as a template.",
5
+ "presentationStyle": {
6
+ "$mulmocast": {
7
+ "version": "1.1",
8
+ "credit": "closing"
9
+ },
10
+ "canvasSize": {
11
+ "width": 1536,
12
+ "height": 1024
13
+ },
14
+ "imageParams": {
15
+ "provider": "google",
16
+ "model": "gemini-2.5-flash-image-preview",
17
+ "style": "<style>Ghibli style multi-panel comic strips in landscape mode. Use speech bubbles with short, natural dialogue (1–6 words). Keep text minimal, like real comics. Let the art convey the story and emotions. Use the input image as the presenter.</style>",
18
+ "images": {
19
+ "presenter": {
20
+ "type": "image",
21
+ "source": {
22
+ "kind": "url",
23
+ "url": "https://raw.githubusercontent.com/receptron/mulmocast-media/refs/heads/main/characters/ghibli_presenter.png"
24
+ }
25
+ }
26
+ }
27
+ }
28
+ },
29
+ "scriptName": "text_only_template.json"
30
+ }
@@ -13,6 +13,8 @@ export declare const generateBeatImage: (inputs: {
13
13
  args?: PublicAPIArgs & {
14
14
  forceMovie?: boolean;
15
15
  forceImage?: boolean;
16
+ forceLipSync?: boolean;
17
+ forceSoundEffect?: boolean;
16
18
  };
17
19
  }) => Promise<void>;
18
20
  export {};
@@ -52,6 +52,8 @@ const beat_graph_data = {
52
52
  __mapIndex: {},
53
53
  forceMovie: { value: false },
54
54
  forceImage: { value: false },
55
+ forceLipSync: { value: false },
56
+ forceSoundEffect: { value: false },
55
57
  preprocessor: {
56
58
  agent: imagePreprocessAgent,
57
59
  inputs: {
@@ -214,7 +216,7 @@ const beat_graph_data = {
214
216
  duration: ":preprocessor.beatDuration",
215
217
  },
216
218
  cache: {
217
- force: [":context.force"],
219
+ force: [":context.force", ":forceSoundEffect"],
218
220
  file: ":preprocessor.soundEffectFile",
219
221
  index: ":__mapIndex",
220
222
  id: ":beat.id",
@@ -261,7 +263,7 @@ const beat_graph_data = {
261
263
  duration: ":preprocessor.beatDuration",
262
264
  },
263
265
  cache: {
264
- force: [":context.force"],
266
+ force: [":context.force", ":forceLipSync"],
265
267
  file: ":preprocessor.lipSyncFile",
266
268
  index: ":__mapIndex",
267
269
  id: ":beat.id",
@@ -428,7 +430,7 @@ export const images = async (context, args) => {
428
430
  // public api
429
431
  export const generateBeatImage = async (inputs) => {
430
432
  const { index, context, args } = inputs;
431
- const { settings, callbacks, forceMovie, forceImage } = args ?? {};
433
+ const { settings, callbacks, forceMovie, forceImage, forceLipSync, forceSoundEffect } = args ?? {};
432
434
  const options = await graphOption(context, settings);
433
435
  const injections = await prepareGenerateImages(context);
434
436
  const graph = new GraphAI(beat_graph_data, defaultAgents, options);
@@ -441,6 +443,8 @@ export const generateBeatImage = async (inputs) => {
441
443
  graph.injectValue("beat", context.studio.script.beats[index]);
442
444
  graph.injectValue("forceMovie", forceMovie ?? false);
443
445
  graph.injectValue("forceImage", forceImage ?? false);
446
+ graph.injectValue("forceLipSync", forceLipSync ?? false);
447
+ graph.injectValue("forceSoundEffect", forceSoundEffect ?? false);
444
448
  if (callbacks) {
445
449
  callbacks.forEach((callback) => {
446
450
  graph.registerCallback(callback);
@@ -0,0 +1,4 @@
1
+ import { AgentFunction, AgentFunctionInfo } from "graphai";
2
+ export declare const puppeteerCrawlerAgent: AgentFunction;
3
+ declare const puppeteerCrawlerAgentInfo: AgentFunctionInfo;
4
+ export default puppeteerCrawlerAgentInfo;
@@ -0,0 +1,94 @@
1
+ import { GraphAILogger } from "graphai";
2
+ import puppeteer from "puppeteer";
3
+ import { Readability } from "@mozilla/readability";
4
+ import { JSDOM } from "jsdom";
5
+ const NAV_TIMEOUT = 45_000;
6
+ const normalize = (s) => s
7
+ .replace(/\r\n/g, "\n")
8
+ .replace(/[\n\t]{2,}/g, "\n")
9
+ .trim();
10
+ const waitStable = async (page, ms = 1200, step = 200) => {
11
+ let last = -1;
12
+ let stable = 0;
13
+ while (stable < ms) {
14
+ const len = await page.evaluate(() => document.body?.innerText?.length || 0);
15
+ stable = len === last ? stable + step : 0;
16
+ last = len;
17
+ await new Promise((r) => setTimeout(r, step));
18
+ }
19
+ };
20
+ const fetchArticle = async (url) => {
21
+ const browser = await puppeteer.launch({
22
+ headless: true,
23
+ args: ["--no-sandbox", "--disable-setuid-sandbox", "--disable-dev-shm-usage"],
24
+ });
25
+ const page = await browser.newPage();
26
+ await page.setUserAgent("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36");
27
+ await page.setViewport({ width: 1366, height: 900 });
28
+ try {
29
+ await page.goto(url, { waitUntil: "networkidle2", timeout: NAV_TIMEOUT });
30
+ await Promise.race([page.waitForSelector("article, main, [role=main], .article, .post", { timeout: 8000 }), new Promise((r) => setTimeout(r, 8000))]);
31
+ await waitStable(page, 1200);
32
+ const html = await page.content();
33
+ const dom = new JSDOM(html, { url: page.url() });
34
+ const reader = new Readability(dom.window.document);
35
+ const a = reader.parse();
36
+ const title = a?.title || (await page.title()) || null;
37
+ const text = normalize(a?.textContent || "");
38
+ let finalText = text;
39
+ if (finalText.length < 100) {
40
+ const raw = await page.evaluate(() => {
41
+ const el = document.querySelector("article, main, [role=main], .article, .post") || document.body;
42
+ return el?.textContent || "";
43
+ });
44
+ finalText = normalize(raw);
45
+ }
46
+ return {
47
+ url,
48
+ title,
49
+ byline: a?.byline || null,
50
+ excerpt: a?.excerpt || null,
51
+ length: a?.length ?? (finalText?.length || null),
52
+ textContent: finalText || null,
53
+ };
54
+ }
55
+ finally {
56
+ await page.close().catch(() => { });
57
+ await browser.close().catch(() => { });
58
+ }
59
+ };
60
+ export const puppeteerCrawlerAgent = async ({ namedInputs }) => {
61
+ const { url } = namedInputs;
62
+ GraphAILogger.log(url);
63
+ try {
64
+ const data = await fetchArticle(url);
65
+ GraphAILogger.log(JSON.stringify({ ok: true, ...data }));
66
+ return {
67
+ data,
68
+ content: data.textContent,
69
+ };
70
+ }
71
+ catch (e) {
72
+ const errorMessage = e instanceof Error ? e.message : String(e);
73
+ GraphAILogger.log(JSON.stringify({ ok: false, url, error: errorMessage }));
74
+ return { content: errorMessage };
75
+ }
76
+ };
77
+ const puppeteerCrawlerAgentInfo = {
78
+ name: "puppeteerCrawlerAgent",
79
+ agent: puppeteerCrawlerAgent,
80
+ mock: puppeteerCrawlerAgent,
81
+ samples: [
82
+ {
83
+ params: {},
84
+ inputs: {},
85
+ result: {},
86
+ },
87
+ ],
88
+ description: "Puppeteer Crawler Agent",
89
+ category: ["net"],
90
+ repository: "https://github.com/receptron/mulmocast-cli",
91
+ author: "Receptron team",
92
+ license: "MIT",
93
+ };
94
+ export default puppeteerCrawlerAgentInfo;
@@ -37,6 +37,7 @@ export declare const promptTemplates: ({
37
37
  };
38
38
  style: string;
39
39
  provider?: undefined;
40
+ model?: undefined;
40
41
  };
41
42
  movieParams: {
42
43
  provider: string;
@@ -106,6 +107,7 @@ export declare const promptTemplates: ({
106
107
  };
107
108
  style: string;
108
109
  provider?: undefined;
110
+ model?: undefined;
109
111
  };
110
112
  movieParams: {
111
113
  model: string;
@@ -178,6 +180,7 @@ export declare const promptTemplates: ({
178
180
  };
179
181
  provider: string;
180
182
  style?: undefined;
183
+ model?: undefined;
181
184
  };
182
185
  movieParams: {
183
186
  provider: string;
@@ -231,6 +234,7 @@ export declare const promptTemplates: ({
231
234
  style: string;
232
235
  images?: undefined;
233
236
  provider?: undefined;
237
+ model?: undefined;
234
238
  };
235
239
  movieParams: {
236
240
  provider: string;
@@ -297,6 +301,74 @@ export declare const promptTemplates: ({
297
301
  };
298
302
  style: string;
299
303
  provider?: undefined;
304
+ model?: undefined;
305
+ };
306
+ movieParams: {
307
+ provider: string;
308
+ model?: undefined;
309
+ };
310
+ soundEffectParams: {
311
+ provider: string;
312
+ };
313
+ speechParams: {
314
+ speakers: {
315
+ Presenter: {
316
+ displayName: {
317
+ en: string;
318
+ };
319
+ voiceId: string;
320
+ lang?: undefined;
321
+ speechOptions?: undefined;
322
+ };
323
+ Announcer?: undefined;
324
+ Student?: undefined;
325
+ Teacher?: undefined;
326
+ };
327
+ };
328
+ };
329
+ scriptName: string;
330
+ systemPrompt: string;
331
+ title: string;
332
+ } | {
333
+ description: string;
334
+ filename: string;
335
+ presentationStyle: {
336
+ $mulmocast: {
337
+ credit: string;
338
+ version: string;
339
+ };
340
+ audioParams: {
341
+ audioVolume: number;
342
+ bgmVolume: number;
343
+ closingPadding: number;
344
+ introPadding: number;
345
+ outroPadding: number;
346
+ padding: number;
347
+ suppressSpeech: boolean;
348
+ bgm?: undefined;
349
+ };
350
+ canvasSize: {
351
+ height: number;
352
+ width: number;
353
+ };
354
+ imageParams: {
355
+ images: {
356
+ presenter: {
357
+ source: {
358
+ kind: string;
359
+ url: string;
360
+ };
361
+ type: string;
362
+ };
363
+ girl?: undefined;
364
+ ani?: undefined;
365
+ optimus?: undefined;
366
+ "[CHARACTER_1_ID]"?: undefined;
367
+ "[CHARACTER_2_ID]"?: undefined;
368
+ };
369
+ model: string;
370
+ provider: string;
371
+ style: string;
300
372
  };
301
373
  movieParams: {
302
374
  provider: string;
@@ -369,6 +441,7 @@ export declare const promptTemplates: ({
369
441
  };
370
442
  style: string;
371
443
  provider?: undefined;
444
+ model?: undefined;
372
445
  };
373
446
  movieParams: {
374
447
  provider: string;
@@ -422,6 +495,7 @@ export declare const promptTemplates: ({
422
495
  style: string;
423
496
  images?: undefined;
424
497
  provider?: undefined;
498
+ model?: undefined;
425
499
  };
426
500
  movieParams: {
427
501
  provider: string;
@@ -486,6 +560,7 @@ export declare const promptTemplates: ({
486
560
  style: string;
487
561
  images?: undefined;
488
562
  provider?: undefined;
563
+ model?: undefined;
489
564
  };
490
565
  movieParams: {
491
566
  provider: string;
@@ -552,6 +627,7 @@ export declare const promptTemplates: ({
552
627
  };
553
628
  style: string;
554
629
  provider?: undefined;
630
+ model?: undefined;
555
631
  };
556
632
  movieParams: {
557
633
  provider: string;
@@ -608,6 +684,7 @@ export declare const promptTemplates: ({
608
684
  style: string;
609
685
  images?: undefined;
610
686
  provider?: undefined;
687
+ model?: undefined;
611
688
  };
612
689
  movieParams: {
613
690
  provider: string;
@@ -323,7 +323,7 @@ export const promptTemplates = [
323
323
  title: "Dr. Slump Style",
324
324
  },
325
325
  {
326
- description: "Template for Ghibli-style comic presentation.",
326
+ description: "Ghibli-style comic.",
327
327
  filename: "ghibli_comic",
328
328
  presentationStyle: {
329
329
  $mulmocast: {
@@ -373,8 +373,64 @@ export const promptTemplates = [
373
373
  },
374
374
  },
375
375
  scriptName: "image_prompts_template.json",
376
+ systemPrompt: "Another AI will generate comic strips for each beat based on the imagePrompt of that beat. Mention the reference in one of beats, if it exists. Use the JSON below as a template.",
377
+ title: "Ghibli-style comic.",
378
+ },
379
+ {
380
+ description: "Ghibli-style comic strips with nano banana.",
381
+ filename: "ghibli_comic_strips",
382
+ presentationStyle: {
383
+ $mulmocast: {
384
+ credit: "closing",
385
+ version: "1.1",
386
+ },
387
+ audioParams: {
388
+ audioVolume: 1,
389
+ bgmVolume: 0.2,
390
+ closingPadding: 0.8,
391
+ introPadding: 1,
392
+ outroPadding: 1,
393
+ padding: 0.3,
394
+ suppressSpeech: false,
395
+ },
396
+ canvasSize: {
397
+ height: 1024,
398
+ width: 1536,
399
+ },
400
+ imageParams: {
401
+ images: {
402
+ presenter: {
403
+ source: {
404
+ kind: "url",
405
+ url: "https://raw.githubusercontent.com/receptron/mulmocast-media/refs/heads/main/characters/ghibli_presenter.png",
406
+ },
407
+ type: "image",
408
+ },
409
+ },
410
+ model: "gemini-2.5-flash-image-preview",
411
+ provider: "google",
412
+ style: "<style>Ghibli style multi-panel comic strips in landscape mode. Use speech bubbles with short, natural dialogue (1–6 words). Keep text minimal, like real comics. Let the art convey the story and emotions. Use the input image as the presenter.</style>",
413
+ },
414
+ movieParams: {
415
+ provider: "replicate",
416
+ },
417
+ soundEffectParams: {
418
+ provider: "replicate",
419
+ },
420
+ speechParams: {
421
+ speakers: {
422
+ Presenter: {
423
+ displayName: {
424
+ en: "Presenter",
425
+ },
426
+ voiceId: "shimmer",
427
+ },
428
+ },
429
+ },
430
+ },
431
+ scriptName: "text_only_template.json",
376
432
  systemPrompt: "Another AI will generate comic strips for each beat based on the text description of that beat. Mention the reference in one of beats, if it exists. Use the JSON below as a template.",
377
- title: "Ghibli comic style",
433
+ title: "Ghibli-style comic strips with nano banana.",
378
434
  },
379
435
  {
380
436
  description: "Template for Ghost in the shell style comic presentation.",
@@ -8,6 +8,7 @@ export declare const templateDataSet: {
8
8
  comic_strips: string;
9
9
  drslump_comic: string;
10
10
  ghibli_comic: string;
11
+ ghibli_comic_strips: string;
11
12
  ghost_comic: string;
12
13
  html: string;
13
14
  onepiece_comic: string;
@@ -31,10 +31,14 @@ export const templateDataSet = {
31
31
  "```JSON\n" +
32
32
  `{"$mulmocast":{"version":"1.1","credit":"closing"},"title":"[TITLE: Brief, engaging title for the topic]","lang":"en","references":[{"url":"[SOURCE_URL: URL of the source material]","title":"[SOURCE_TITLE: Title of the referenced article, or paper]","type":"[SOURCE_TYPE: article, paper]"}],"beats":[{"text":"[OPENING_BEAT: Introduce the topic with a hook. Reference the source material and set up why this topic matters. Usually 2-3 sentences that grab attention and provide context.]","imagePrompt":"[IMAGE_PROMPT: A prompt for the image to be generated for this beat.]"},{"text":"[MAIN_CONCEPT: Define or explain the core concept/idea. This should be the central focus of your narrative. Keep it clear and accessible.]","imagePrompt":"[IMAGE_PROMPT: A prompt for the image to be generated for this beat.]"},{"text":"[SUPPORTING_DETAIL_1: Additional context, examples, or elaboration that helps illustrate the main concept. This could include how it works, why it's important, or real-world applications.]","imagePrompt":"[IMAGE_PROMPT: A prompt for the image to be generated for this beat.]"},{"text":"[SUPPORTING_DETAIL_2: Continue with more examples, deeper explanation, or different aspects of the topic if needed.]","imagePrompt":"[IMAGE_PROMPT: A prompt for the image to be generated for this beat.]"},{"text":"[ADDITIONAL_BEATS: Add more beats as necessary to fully explore the topic. Complex topics may require 6-10+ beats to cover adequately. Each beat should advance the narrative or provide valuable information.]","imagePrompt":"[IMAGE_PROMPT: A prompt for the image to be generated for this beat.]"},{"text":"[CONCLUSION/IMPACT: Wrap up with the significance, implications, or key takeaway. Help the audience understand why this matters to them.]","imagePrompt":"[IMAGE_PROMPT: A prompt for the image to be generated for this beat.]"}],"canvasSize":{"width":1536,"height":1024},"imageParams":{"style":"<style>Dragon Ball/Dr. Slump aesthetic.</style>","images":{"girl":{"type":"image","source":{"kind":"url","url":"https://raw.githubusercontent.com/receptron/mulmocast-media/refs/heads/main/characters/slump_presenter.png"}}}}}\n` +
33
33
  "```",
34
- ghibli_comic: "Another AI will generate comic strips for each beat based on the text description of that beat. Mention the reference in one of beats, if it exists. Use the JSON below as a template.\n" +
34
+ ghibli_comic: "Another AI will generate comic strips for each beat based on the imagePrompt of that beat. Mention the reference in one of beats, if it exists. Use the JSON below as a template.\n" +
35
35
  "```JSON\n" +
36
36
  `{"$mulmocast":{"version":"1.1","credit":"closing"},"title":"[TITLE: Brief, engaging title for the topic]","lang":"en","references":[{"url":"[SOURCE_URL: URL of the source material]","title":"[SOURCE_TITLE: Title of the referenced article, or paper]","type":"[SOURCE_TYPE: article, paper]"}],"beats":[{"text":"[OPENING_BEAT: Introduce the topic with a hook. Reference the source material and set up why this topic matters. Usually 2-3 sentences that grab attention and provide context.]","imagePrompt":"[IMAGE_PROMPT: A prompt for the image to be generated for this beat.]"},{"text":"[MAIN_CONCEPT: Define or explain the core concept/idea. This should be the central focus of your narrative. Keep it clear and accessible.]","imagePrompt":"[IMAGE_PROMPT: A prompt for the image to be generated for this beat.]"},{"text":"[SUPPORTING_DETAIL_1: Additional context, examples, or elaboration that helps illustrate the main concept. This could include how it works, why it's important, or real-world applications.]","imagePrompt":"[IMAGE_PROMPT: A prompt for the image to be generated for this beat.]"},{"text":"[SUPPORTING_DETAIL_2: Continue with more examples, deeper explanation, or different aspects of the topic if needed.]","imagePrompt":"[IMAGE_PROMPT: A prompt for the image to be generated for this beat.]"},{"text":"[ADDITIONAL_BEATS: Add more beats as necessary to fully explore the topic. Complex topics may require 6-10+ beats to cover adequately. Each beat should advance the narrative or provide valuable information.]","imagePrompt":"[IMAGE_PROMPT: A prompt for the image to be generated for this beat.]"},{"text":"[CONCLUSION/IMPACT: Wrap up with the significance, implications, or key takeaway. Help the audience understand why this matters to them.]","imagePrompt":"[IMAGE_PROMPT: A prompt for the image to be generated for this beat.]"}],"canvasSize":{"width":1536,"height":1024},"imageParams":{"style":"<style>Ghibli style</style>","images":{"presenter":{"type":"image","source":{"kind":"url","url":"https://raw.githubusercontent.com/receptron/mulmocast-media/refs/heads/main/characters/ghibli_presenter.png"}}}}}\n` +
37
37
  "```",
38
+ ghibli_comic_strips: "Another AI will generate comic strips for each beat based on the text description of that beat. Mention the reference in one of beats, if it exists. Use the JSON below as a template.\n" +
39
+ "```JSON\n" +
40
+ `{"$mulmocast":{"version":"1.1","credit":"closing"},"title":"[TITLE: Brief, engaging title for the topic]","lang":"en","references":[{"url":"[SOURCE_URL: URL of the source material]","title":"[SOURCE_TITLE: Title of the referenced article, or paper]","type":"[SOURCE_TYPE: article, paper]"}],"beats":[{"text":"[OPENING_BEAT: Introduce the topic with a hook. Reference the source material and set up why this topic matters. Usually 2-3 sentences that grab attention and provide context.]"},{"text":"[MAIN_CONCEPT: Define or explain the core concept/idea. This should be the central focus of your narrative. Keep it clear and accessible.]"},{"text":"[SUPPORTING_DETAIL_1: Additional context, examples, or elaboration that helps illustrate the main concept. This could include how it works, why it's important, or real-world applications.]"},{"text":"[SUPPORTING_DETAIL_2: Continue with more examples, deeper explanation, or different aspects of the topic if needed.]"},{"text":"[ADDITIONAL_BEATS: Add more beats as necessary to fully explore the topic. Complex topics may require 6-10+ beats to cover adequately. Each beat should advance the narrative or provide valuable information.]"},{"text":"[CONCLUSION/IMPACT: Wrap up with the significance, implications, or key takeaway. Help the audience understand why this matters to them.]"}],"canvasSize":{"width":1536,"height":1024},"imageParams":{"provider":"google","model":"gemini-2.5-flash-image-preview","style":"<style>Ghibli style multi-panel comic strips in landscape mode. Use speech bubbles with short, natural dialogue (1–6 words). Keep text minimal, like real comics. Let the art convey the story and emotions. Use the input image as the presenter.</style>","images":{"presenter":{"type":"image","source":{"kind":"url","url":"https://raw.githubusercontent.com/receptron/mulmocast-media/refs/heads/main/characters/ghibli_presenter.png"}}}}}\n` +
41
+ "```",
38
42
  ghost_comic: "Another AI will generate images for each beat based on the image prompt of that beat. Mention the reference in one of beats, if it exists. Use the JSON below as a template.\n" +
39
43
  "```JSON\n" +
40
44
  `{"$mulmocast":{"version":"1.1","credit":"closing"},"title":"[TITLE: Brief, engaging title for the topic]","lang":"en","references":[{"url":"[SOURCE_URL: URL of the source material]","title":"[SOURCE_TITLE: Title of the referenced article, or paper]","type":"[SOURCE_TYPE: article, paper]"}],"beats":[{"text":"[OPENING_BEAT: Introduce the topic with a hook. Reference the source material and set up why this topic matters. Usually 2-3 sentences that grab attention and provide context.]","imagePrompt":"[IMAGE_PROMPT: A prompt for the image to be generated for this beat.]"},{"text":"[MAIN_CONCEPT: Define or explain the core concept/idea. This should be the central focus of your narrative. Keep it clear and accessible.]","imagePrompt":"[IMAGE_PROMPT: A prompt for the image to be generated for this beat.]"},{"text":"[SUPPORTING_DETAIL_1: Additional context, examples, or elaboration that helps illustrate the main concept. This could include how it works, why it's important, or real-world applications.]","imagePrompt":"[IMAGE_PROMPT: A prompt for the image to be generated for this beat.]"},{"text":"[SUPPORTING_DETAIL_2: Continue with more examples, deeper explanation, or different aspects of the topic if needed.]","imagePrompt":"[IMAGE_PROMPT: A prompt for the image to be generated for this beat.]"},{"text":"[ADDITIONAL_BEATS: Add more beats as necessary to fully explore the topic. Complex topics may require 6-10+ beats to cover adequately. Each beat should advance the narrative or provide valuable information.]","imagePrompt":"[IMAGE_PROMPT: A prompt for the image to be generated for this beat.]"},{"text":"[CONCLUSION/IMPACT: Wrap up with the significance, implications, or key takeaway. Help the audience understand why this matters to them.]","imagePrompt":"[IMAGE_PROMPT: A prompt for the image to be generated for this beat.]"}],"canvasSize":{"width":1536,"height":1024},"imageParams":{"style":"<style>Ghost in the shell aesthetic.</style>","images":{"presenter":{"type":"image","source":{"kind":"url","url":"https://raw.githubusercontent.com/receptron/mulmocast-media/refs/heads/main/characters/ghost_presenter.png"}},"optimus":{"type":"image","source":{"kind":"url","url":"https://raw.githubusercontent.com/receptron/mulmocast-media/refs/heads/main/characters/optimus.png"}}}}}\n` +
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mulmocast",
3
- "version": "1.2.16",
3
+ "version": "1.2.17",
4
4
  "description": "",
5
5
  "type": "module",
6
6
  "main": "lib/index.node.js",
@@ -79,11 +79,13 @@
79
79
  "@inquirer/input": "^4.2.2",
80
80
  "@inquirer/select": "^4.3.2",
81
81
  "@modelcontextprotocol/sdk": "^1.17.4",
82
+ "@mozilla/readability": "^0.6.0",
82
83
  "@tavily/core": "^0.5.11",
83
84
  "clipboardy": "^4.0.0",
84
85
  "dotenv": "^17.2.1",
85
86
  "fluent-ffmpeg": "^2.1.3",
86
87
  "graphai": "^2.0.14",
88
+ "jsdom": "^26.1.0",
87
89
  "marked": "^16.2.0",
88
90
  "ora": "^8.2.0",
89
91
  "puppeteer": "^24.17.0",
@@ -96,6 +98,7 @@
96
98
  "devDependencies": {
97
99
  "@receptron/test_utils": "^2.0.3",
98
100
  "@types/fluent-ffmpeg": "^2.1.26",
101
+ "@types/jsdom": "^21.1.7",
99
102
  "@types/yargs": "^17.0.33",
100
103
  "eslint": "^9.34.0",
101
104
  "eslint-config-prettier": "^10.1.8",