mulmocast 2.12.1 → 2.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/schemas/mulmo_script.json +231 -0
- package/lib/actions/avatar.d.ts +37 -0
- package/lib/actions/avatar.js +176 -0
- package/lib/actions/image_agents.js +9 -9
- package/lib/actions/index.d.ts +1 -0
- package/lib/actions/index.js +1 -0
- package/lib/actions/movie.d.ts +1 -0
- package/lib/actions/movie.js +40 -2
- package/lib/agents/combine_audio_files_agent.js +2 -2
- package/lib/cli/commands/movie/handler.js +2 -2
- package/lib/mcp/server.js +3 -3
- package/lib/methods/mulmo_beat.d.ts +1 -0
- package/lib/methods/mulmo_beat.js +3 -0
- package/lib/methods/mulmo_beat_node.d.ts +1 -0
- package/lib/methods/mulmo_presentation_style.d.ts +5 -1
- package/lib/methods/mulmo_presentation_style.js +8 -0
- package/lib/types/schema.d.ts +405 -0
- package/lib/types/schema.js +61 -0
- package/lib/types/type.d.ts +6 -1
- package/lib/utils/context.d.ts +158 -0
- package/lib/utils/image_plugins/index.js +2 -0
- package/lib/utils/image_plugins/remotion.d.ts +16 -0
- package/lib/utils/image_plugins/remotion.js +164 -0
- package/lib/utils/remotion/claude_prompt.d.ts +33 -0
- package/lib/utils/remotion/claude_prompt.js +111 -0
- package/lib/utils/remotion/claude_runner.d.ts +4 -0
- package/lib/utils/remotion/claude_runner.js +47 -0
- package/lib/utils/remotion/commit_files.d.ts +11 -0
- package/lib/utils/remotion/commit_files.js +37 -0
- package/lib/utils/remotion/packages.d.ts +5 -0
- package/lib/utils/remotion/packages.js +20 -0
- package/lib/utils/remotion/render.d.ts +19 -0
- package/lib/utils/remotion/render.js +63 -0
- package/lib/utils/remotion/system_prompt.d.ts +3 -0
- package/lib/utils/remotion/system_prompt.js +62 -0
- package/package.json +108 -20
- package/scripts/test/test_avatar.json +46 -0
- package/scripts/test/test_remotion.json +38 -0
package/lib/types/schema.js
CHANGED
|
@@ -34,6 +34,37 @@ export const speechOptionsSchema = z
|
|
|
34
34
|
const speakerIdSchema = z.string();
|
|
35
35
|
export const defaultSpeaker = "Presenter";
|
|
36
36
|
export const text2SpeechProviderSchema = z.enum(Object.keys(provider2TTSAgent)).default(defaultProviders.tts);
|
|
37
|
+
// Talking avatars (AvatarScript): a speaker can speak with an avatar shown over the beat visuals.
|
|
38
|
+
const avatarPercentSchema = z.string().regex(/^-?\d+(\.\d+)?%$/, 'a percentage such as "84%"');
|
|
39
|
+
export const avatarPositionSchema = z
|
|
40
|
+
.object({
|
|
41
|
+
x: avatarPercentSchema.optional().describe("Horizontal centre of the avatar, % of the canvas width. Default 84%"),
|
|
42
|
+
y: avatarPercentSchema.optional().describe("Bottom edge of the avatar, % of the canvas height. Default 100%"),
|
|
43
|
+
scale: avatarPercentSchema.optional().describe("Height of the avatar, % of the canvas height. Default 62%"),
|
|
44
|
+
})
|
|
45
|
+
.strict();
|
|
46
|
+
export const avatarEmotionSchema = z.enum(["neutral", "happy", "sad", "angry", "surprised", "relaxed"]);
|
|
47
|
+
export const speakerAvatarSchema = z
|
|
48
|
+
.object({
|
|
49
|
+
source: z.string().describe("AvatarScript avatar package (a folder with avatar.json, or a mesh-avatar-studio project), relative to the script"),
|
|
50
|
+
position: avatarPositionSchema.optional(),
|
|
51
|
+
})
|
|
52
|
+
.strict();
|
|
53
|
+
export const mulmoAvatarParamsSchema = z
|
|
54
|
+
.object({
|
|
55
|
+
position: avatarPositionSchema.optional(),
|
|
56
|
+
emotion: avatarEmotionSchema.optional().describe("The avatar's expression during this beat"),
|
|
57
|
+
motions: z
|
|
58
|
+
.array(z
|
|
59
|
+
.object({
|
|
60
|
+
motion: z.string().describe("Motion name, e.g. nod, tilt, think, giggle, surprise"),
|
|
61
|
+
at: z.string().optional().describe("Words of the beat's text where the motion starts. Default: the start of the beat"),
|
|
62
|
+
})
|
|
63
|
+
.strict())
|
|
64
|
+
.optional(),
|
|
65
|
+
hidden: z.boolean().optional().describe("Hide the speaker's avatar during this beat"),
|
|
66
|
+
})
|
|
67
|
+
.strict();
|
|
37
68
|
export const speakerDataSchema = z
|
|
38
69
|
.object({
|
|
39
70
|
displayName: z.record(langSchema, z.string()).optional(),
|
|
@@ -44,6 +75,7 @@ export const speakerDataSchema = z
|
|
|
44
75
|
model: z.string().optional().describe("TTS model to use for this speaker"),
|
|
45
76
|
baseURL: z.string().optional(), // Azure/custom endpoint URL
|
|
46
77
|
apiVersion: z.string().optional(), // Azure API version (e.g., "2025-04-01-preview")
|
|
78
|
+
avatar: speakerAvatarSchema.optional().describe("Avatar shown over the beat visuals, speaking this speaker's lines"),
|
|
47
79
|
})
|
|
48
80
|
.strict();
|
|
49
81
|
export const speakerSchema = speakerDataSchema.extend({
|
|
@@ -139,6 +171,7 @@ export const ImageMediaType = {
|
|
|
139
171
|
MoviePrompt: "moviePrompt",
|
|
140
172
|
Beat: "beat",
|
|
141
173
|
VoiceOver: "voice_over",
|
|
174
|
+
Remotion: "remotion",
|
|
142
175
|
};
|
|
143
176
|
// Beat image types whose still derives from the movie or another beat — they cannot supply
|
|
144
177
|
// their own still for the $beatImage frame sentinel.
|
|
@@ -326,6 +359,18 @@ export const mulmoVisionMediaSchema = z
|
|
|
326
359
|
data: z.record(z.string(), z.any()),
|
|
327
360
|
})
|
|
328
361
|
.strict();
|
|
362
|
+
export const mulmoRemotionMediaSchema = z
|
|
363
|
+
.object({
|
|
364
|
+
type: z.literal(ImageMediaType.Remotion),
|
|
365
|
+
prompt: z.string().min(1).describe("What the scene shows. Claude Code (claude -p) writes a Remotion component from it."),
|
|
366
|
+
fps: z.number().min(1).max(60).optional().describe("Frame rate of the rendered video. Default: 30."),
|
|
367
|
+
})
|
|
368
|
+
.strict();
|
|
369
|
+
export const mulmoRemotionParamsSchema = z
|
|
370
|
+
.object({
|
|
371
|
+
brief: z.string().optional().describe("Whole-video art direction shared by every remotion beat (palette, type, mood), so all scenes look like one film."),
|
|
372
|
+
})
|
|
373
|
+
.strict();
|
|
329
374
|
export const mulmoImageAssetSchema = z.union([
|
|
330
375
|
mulmoMarkdownMediaSchema,
|
|
331
376
|
mulmoWebMediaSchema,
|
|
@@ -341,6 +386,7 @@ export const mulmoImageAssetSchema = z.union([
|
|
|
341
386
|
mulmoVoiceOverMediaSchema,
|
|
342
387
|
mulmoVisionMediaSchema,
|
|
343
388
|
mulmoSlideMediaSchema,
|
|
389
|
+
mulmoRemotionMediaSchema,
|
|
344
390
|
]);
|
|
345
391
|
const mulmoAudioMediaSchema = z
|
|
346
392
|
.object({
|
|
@@ -567,6 +613,7 @@ export const mulmoBeatSchema = z
|
|
|
567
613
|
.optional(),
|
|
568
614
|
soundEffectParams: mulmoSoundEffectParamsSchema.optional(),
|
|
569
615
|
lipSyncParams: mulmoLipSyncParamsSchema.optional(),
|
|
616
|
+
avatarParams: mulmoAvatarParamsSchema.optional(),
|
|
570
617
|
htmlImageParams: mulmoHtmlImageParamsSchema.optional(),
|
|
571
618
|
speechOptions: speechOptionsSchema.optional(),
|
|
572
619
|
textSlideParams: textSlideParamsSchema.optional(),
|
|
@@ -626,6 +673,7 @@ export const mulmoPresentationStyleSchema = z.object({
|
|
|
626
673
|
provider: defaultProviders.soundEffect,
|
|
627
674
|
}),
|
|
628
675
|
lipSyncParams: mulmoLipSyncParamsSchema.optional(),
|
|
676
|
+
avatarParams: mulmoAvatarParamsSchema.optional(),
|
|
629
677
|
htmlImageParams: mulmoHtmlImageParamsSchema
|
|
630
678
|
.extend({
|
|
631
679
|
provider: text2HtmlImageProviderSchema,
|
|
@@ -635,6 +683,8 @@ export const mulmoPresentationStyleSchema = z.object({
|
|
|
635
683
|
textSlideParams: textSlideParamsSchema.optional(),
|
|
636
684
|
// for slide plugin
|
|
637
685
|
slideParams: mulmoSlideParamsSchema.optional(),
|
|
686
|
+
// for remotion plugin
|
|
687
|
+
remotionParams: mulmoRemotionParamsSchema.optional(),
|
|
638
688
|
captionParams: mulmoCaptionParamsSchema.optional(),
|
|
639
689
|
audioParams: audioParamsSchema.default({
|
|
640
690
|
introPadding: 1.0,
|
|
@@ -730,11 +780,22 @@ export const mulmoSessionStateSchema = z.object({
|
|
|
730
780
|
lipSync: z.record(z.string(), z.boolean()),
|
|
731
781
|
}),
|
|
732
782
|
});
|
|
783
|
+
export const mulmoAvatarTrackSchema = z
|
|
784
|
+
.object({
|
|
785
|
+
speaker: z.string(),
|
|
786
|
+
file: z.string(), // see-through, avatar-sized video (WebM VP9 with alpha)
|
|
787
|
+
width: z.number(), // the size it was rendered at, pixels
|
|
788
|
+
height: z.number(),
|
|
789
|
+
// where it is shown: absolute [start, end) seconds, and its place and size on the canvas in pixels
|
|
790
|
+
placements: z.array(z.object({ start: z.number(), end: z.number(), x: z.number(), y: z.number(), width: z.number(), height: z.number() }).strict()),
|
|
791
|
+
})
|
|
792
|
+
.strict();
|
|
733
793
|
export const mulmoStudioSchema = z
|
|
734
794
|
.object({
|
|
735
795
|
script: mulmoScriptSchema,
|
|
736
796
|
filename: z.string(),
|
|
737
797
|
beats: z.array(mulmoStudioBeatSchema).min(1),
|
|
798
|
+
avatarTracks: z.array(mulmoAvatarTrackSchema).optional(),
|
|
738
799
|
})
|
|
739
800
|
.strict();
|
|
740
801
|
export const mulmoPromptTemplateSchema = z
|
package/lib/types/type.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type CallbackFunction } from "graphai";
|
|
2
2
|
import type { UsageCollectorAPI } from "./usage.js";
|
|
3
|
-
import { langSchema, localizedTextSchema, mulmoBeatSchema, mulmoScriptSchema, mulmoStudioSchema, mulmoStudioBeatSchema, mulmoStoryboardSchema, mulmoStoryboardSceneSchema, mulmoStudioMultiLingualSchema, mulmoStudioMultiLingualArraySchema, mulmoStudioMultiLingualDataSchema, mulmoStudioMultiLingualFileSchema, speakerDictionarySchema, speakerSchema, mulmoSpeechParamsSchema, mulmoImageParamsSchema, mulmoImageParamsImagesValueSchema, mulmoImageParamsImagesSchema, mulmoFillOptionSchema, mulmoTransitionSchema, mulmoVideoFilterSchema, mulmoMovieParamsSchema, mulmoSoundEffectParamsSchema, mulmoLipSyncParamsSchema, textSlideParamsSchema, speechOptionsSchema, speakerDataSchema, mulmoCanvasDimensionSchema, mulmoPromptTemplateSchema, mulmoPromptTemplateFileSchema, text2ImageProviderSchema, text2HtmlImageProviderSchema, text2MovieProviderSchema, text2SpeechProviderSchema, mulmoPresentationStyleSchema, multiLingualTextsSchema, mulmoImageAssetSchema, mulmoMermaidMediaSchema, mulmoTextSlideMediaSchema, mulmoMarkdownMediaSchema, mulmoImageMediaSchema, mulmoChartMediaSchema, mediaSourceSchema, mediaSourceMermaidSchema, backgroundImageSchema, backgroundImageSourceSchema, mulmoSessionStateSchema, mulmoOpenAIImageModelSchema, mulmoGoogleImageModelSchema, mulmoGoogleMovieModelSchema, mulmoReplicateMovieModelSchema, mulmoImagePromptMediaSchema, mulmoImageReferenceSchema, mulmoMovieMediaSchema, mulmoMoviePromptMediaSchema, markdownLayoutSchema, row2Schema, grid2x2Schema } from "./schema.js";
|
|
3
|
+
import { langSchema, localizedTextSchema, mulmoBeatSchema, mulmoScriptSchema, mulmoStudioSchema, mulmoStudioBeatSchema, mulmoStoryboardSchema, mulmoStoryboardSceneSchema, mulmoStudioMultiLingualSchema, mulmoStudioMultiLingualArraySchema, mulmoStudioMultiLingualDataSchema, mulmoStudioMultiLingualFileSchema, speakerDictionarySchema, speakerSchema, mulmoSpeechParamsSchema, mulmoImageParamsSchema, mulmoImageParamsImagesValueSchema, mulmoImageParamsImagesSchema, mulmoFillOptionSchema, mulmoTransitionSchema, mulmoVideoFilterSchema, mulmoMovieParamsSchema, mulmoSoundEffectParamsSchema, mulmoLipSyncParamsSchema, textSlideParamsSchema, speechOptionsSchema, speakerDataSchema, mulmoCanvasDimensionSchema, mulmoPromptTemplateSchema, mulmoPromptTemplateFileSchema, text2ImageProviderSchema, text2HtmlImageProviderSchema, text2MovieProviderSchema, text2SpeechProviderSchema, mulmoPresentationStyleSchema, multiLingualTextsSchema, mulmoImageAssetSchema, mulmoMermaidMediaSchema, mulmoRemotionMediaSchema, mulmoTextSlideMediaSchema, mulmoMarkdownMediaSchema, mulmoImageMediaSchema, mulmoChartMediaSchema, mediaSourceSchema, mediaSourceMermaidSchema, backgroundImageSchema, backgroundImageSourceSchema, mulmoSessionStateSchema, mulmoOpenAIImageModelSchema, mulmoGoogleImageModelSchema, mulmoGoogleMovieModelSchema, mulmoReplicateMovieModelSchema, mulmoImagePromptMediaSchema, mulmoImageReferenceSchema, mulmoMovieMediaSchema, mulmoMoviePromptMediaSchema, markdownLayoutSchema, row2Schema, grid2x2Schema, mulmoAvatarParamsSchema, avatarPositionSchema, speakerAvatarSchema, mulmoAvatarTrackSchema } from "./schema.js";
|
|
4
4
|
import { pdf_modes, pdf_sizes, storyToScriptGenerateMode } from "./const.js";
|
|
5
5
|
import type { LLM } from "./provider2agent.js";
|
|
6
6
|
import { z } from "zod";
|
|
@@ -44,6 +44,10 @@ export type MultiLingualTexts = z.infer<typeof multiLingualTextsSchema>;
|
|
|
44
44
|
export type MulmoMovieParams = z.infer<typeof mulmoMovieParamsSchema>;
|
|
45
45
|
export type MulmoSoundEffectParams = z.infer<typeof mulmoSoundEffectParamsSchema>;
|
|
46
46
|
export type MulmoLipSyncParams = z.infer<typeof mulmoLipSyncParamsSchema>;
|
|
47
|
+
export type MulmoAvatarParams = z.infer<typeof mulmoAvatarParamsSchema>;
|
|
48
|
+
export type MulmoAvatarPosition = z.infer<typeof avatarPositionSchema>;
|
|
49
|
+
export type MulmoSpeakerAvatar = z.infer<typeof speakerAvatarSchema>;
|
|
50
|
+
export type MulmoAvatarTrack = z.infer<typeof mulmoAvatarTrackSchema>;
|
|
47
51
|
export type MulmoOpenAIImageModel = z.infer<typeof mulmoOpenAIImageModelSchema>;
|
|
48
52
|
export type MulmoGoogleImageModel = z.infer<typeof mulmoGoogleImageModelSchema>;
|
|
49
53
|
export type MulmoGoogleMovieModel = z.infer<typeof mulmoGoogleMovieModelSchema>;
|
|
@@ -61,6 +65,7 @@ export type MulmoImageMedia = z.infer<typeof mulmoImageMediaSchema>;
|
|
|
61
65
|
export type MulmoMovieMedia = z.infer<typeof mulmoMovieMediaSchema>;
|
|
62
66
|
export type MulmoChartMedia = z.infer<typeof mulmoChartMediaSchema>;
|
|
63
67
|
export type MulmoMermaidMedia = z.infer<typeof mulmoMermaidMediaSchema>;
|
|
68
|
+
export type MulmoRemotionMedia = z.infer<typeof mulmoRemotionMediaSchema>;
|
|
64
69
|
export type MulmoSessionState = z.infer<typeof mulmoSessionStateSchema>;
|
|
65
70
|
export type MulmoStudioContext = {
|
|
66
71
|
fileDirs: FileObject;
|
package/lib/utils/context.d.ts
CHANGED
|
@@ -53,6 +53,14 @@ export declare const createStudioData: (_mulmoScript: MulmoScript, fileName: str
|
|
|
53
53
|
model?: string | undefined;
|
|
54
54
|
baseURL?: string | undefined;
|
|
55
55
|
apiVersion?: string | undefined;
|
|
56
|
+
avatar?: {
|
|
57
|
+
source: string;
|
|
58
|
+
position?: {
|
|
59
|
+
x?: string | undefined;
|
|
60
|
+
y?: string | undefined;
|
|
61
|
+
scale?: string | undefined;
|
|
62
|
+
} | undefined;
|
|
63
|
+
} | undefined;
|
|
56
64
|
lang?: Record<string, {
|
|
57
65
|
voiceId: string;
|
|
58
66
|
displayName?: Record<string, string> | undefined;
|
|
@@ -68,6 +76,14 @@ export declare const createStudioData: (_mulmoScript: MulmoScript, fileName: str
|
|
|
68
76
|
model?: string | undefined;
|
|
69
77
|
baseURL?: string | undefined;
|
|
70
78
|
apiVersion?: string | undefined;
|
|
79
|
+
avatar?: {
|
|
80
|
+
source: string;
|
|
81
|
+
position?: {
|
|
82
|
+
x?: string | undefined;
|
|
83
|
+
y?: string | undefined;
|
|
84
|
+
scale?: string | undefined;
|
|
85
|
+
} | undefined;
|
|
86
|
+
} | undefined;
|
|
71
87
|
}> | undefined;
|
|
72
88
|
}>;
|
|
73
89
|
};
|
|
@@ -532,6 +548,10 @@ export declare const createStudioData: (_mulmoScript: MulmoScript, fileName: str
|
|
|
532
548
|
type: "vision";
|
|
533
549
|
style: string;
|
|
534
550
|
data: Record<string, any>;
|
|
551
|
+
} | {
|
|
552
|
+
type: "remotion";
|
|
553
|
+
prompt: string;
|
|
554
|
+
fps?: number | undefined;
|
|
535
555
|
} | {
|
|
536
556
|
type: "slide";
|
|
537
557
|
slide: {
|
|
@@ -2371,6 +2391,19 @@ export declare const createStudioData: (_mulmoScript: MulmoScript, fileName: str
|
|
|
2371
2391
|
provider?: string | undefined;
|
|
2372
2392
|
model?: string | undefined;
|
|
2373
2393
|
} | undefined;
|
|
2394
|
+
avatarParams?: {
|
|
2395
|
+
position?: {
|
|
2396
|
+
x?: string | undefined;
|
|
2397
|
+
y?: string | undefined;
|
|
2398
|
+
scale?: string | undefined;
|
|
2399
|
+
} | undefined;
|
|
2400
|
+
emotion?: "neutral" | "happy" | "sad" | "angry" | "surprised" | "relaxed" | undefined;
|
|
2401
|
+
motions?: {
|
|
2402
|
+
motion: string;
|
|
2403
|
+
at?: string | undefined;
|
|
2404
|
+
}[] | undefined;
|
|
2405
|
+
hidden?: boolean | undefined;
|
|
2406
|
+
} | undefined;
|
|
2374
2407
|
htmlImageParams?: {
|
|
2375
2408
|
model?: string | undefined;
|
|
2376
2409
|
} | undefined;
|
|
@@ -2474,6 +2507,19 @@ export declare const createStudioData: (_mulmoScript: MulmoScript, fileName: str
|
|
|
2474
2507
|
provider?: string | undefined;
|
|
2475
2508
|
model?: string | undefined;
|
|
2476
2509
|
} | undefined;
|
|
2510
|
+
avatarParams?: {
|
|
2511
|
+
position?: {
|
|
2512
|
+
x?: string | undefined;
|
|
2513
|
+
y?: string | undefined;
|
|
2514
|
+
scale?: string | undefined;
|
|
2515
|
+
} | undefined;
|
|
2516
|
+
emotion?: "neutral" | "happy" | "sad" | "angry" | "surprised" | "relaxed" | undefined;
|
|
2517
|
+
motions?: {
|
|
2518
|
+
motion: string;
|
|
2519
|
+
at?: string | undefined;
|
|
2520
|
+
}[] | undefined;
|
|
2521
|
+
hidden?: boolean | undefined;
|
|
2522
|
+
} | undefined;
|
|
2477
2523
|
htmlImageParams?: {
|
|
2478
2524
|
provider: string;
|
|
2479
2525
|
model?: string | undefined;
|
|
@@ -2540,6 +2586,9 @@ export declare const createStudioData: (_mulmoScript: MulmoScript, fileName: str
|
|
|
2540
2586
|
} | undefined;
|
|
2541
2587
|
} | undefined;
|
|
2542
2588
|
} | undefined;
|
|
2589
|
+
remotionParams?: {
|
|
2590
|
+
brief?: string | undefined;
|
|
2591
|
+
} | undefined;
|
|
2543
2592
|
captionParams?: {
|
|
2544
2593
|
lang?: string | undefined;
|
|
2545
2594
|
styles?: string[] | undefined;
|
|
@@ -2588,6 +2637,20 @@ export declare const createStudioData: (_mulmoScript: MulmoScript, fileName: str
|
|
|
2588
2637
|
markdown?: string | undefined;
|
|
2589
2638
|
html?: string | undefined;
|
|
2590
2639
|
}[];
|
|
2640
|
+
avatarTracks?: {
|
|
2641
|
+
speaker: string;
|
|
2642
|
+
file: string;
|
|
2643
|
+
width: number;
|
|
2644
|
+
height: number;
|
|
2645
|
+
placements: {
|
|
2646
|
+
start: number;
|
|
2647
|
+
end: number;
|
|
2648
|
+
x: number;
|
|
2649
|
+
y: number;
|
|
2650
|
+
width: number;
|
|
2651
|
+
height: number;
|
|
2652
|
+
}[];
|
|
2653
|
+
}[] | undefined;
|
|
2591
2654
|
};
|
|
2592
2655
|
export declare const fetchScript: (isHttpPath: boolean, mulmoFilePath: string, fileOrUrl: string) => Promise<MulmoScript | null>;
|
|
2593
2656
|
export declare const getMultiLingual: (multilingualFilePath: string, beats: MulmoStudioBeat[]) => MulmoStudioMultiLingual;
|
|
@@ -2619,6 +2682,14 @@ export declare const initializeContextFromFiles: (files: FileObject, raiseError:
|
|
|
2619
2682
|
model?: string | undefined;
|
|
2620
2683
|
baseURL?: string | undefined;
|
|
2621
2684
|
apiVersion?: string | undefined;
|
|
2685
|
+
avatar?: {
|
|
2686
|
+
source: string;
|
|
2687
|
+
position?: {
|
|
2688
|
+
x?: string | undefined;
|
|
2689
|
+
y?: string | undefined;
|
|
2690
|
+
scale?: string | undefined;
|
|
2691
|
+
} | undefined;
|
|
2692
|
+
} | undefined;
|
|
2622
2693
|
lang?: Record<string, {
|
|
2623
2694
|
voiceId: string;
|
|
2624
2695
|
displayName?: Record<string, string> | undefined;
|
|
@@ -2634,6 +2705,14 @@ export declare const initializeContextFromFiles: (files: FileObject, raiseError:
|
|
|
2634
2705
|
model?: string | undefined;
|
|
2635
2706
|
baseURL?: string | undefined;
|
|
2636
2707
|
apiVersion?: string | undefined;
|
|
2708
|
+
avatar?: {
|
|
2709
|
+
source: string;
|
|
2710
|
+
position?: {
|
|
2711
|
+
x?: string | undefined;
|
|
2712
|
+
y?: string | undefined;
|
|
2713
|
+
scale?: string | undefined;
|
|
2714
|
+
} | undefined;
|
|
2715
|
+
} | undefined;
|
|
2637
2716
|
}> | undefined;
|
|
2638
2717
|
}>;
|
|
2639
2718
|
};
|
|
@@ -3098,6 +3177,10 @@ export declare const initializeContextFromFiles: (files: FileObject, raiseError:
|
|
|
3098
3177
|
type: "vision";
|
|
3099
3178
|
style: string;
|
|
3100
3179
|
data: Record<string, any>;
|
|
3180
|
+
} | {
|
|
3181
|
+
type: "remotion";
|
|
3182
|
+
prompt: string;
|
|
3183
|
+
fps?: number | undefined;
|
|
3101
3184
|
} | {
|
|
3102
3185
|
type: "slide";
|
|
3103
3186
|
slide: {
|
|
@@ -4937,6 +5020,19 @@ export declare const initializeContextFromFiles: (files: FileObject, raiseError:
|
|
|
4937
5020
|
provider?: string | undefined;
|
|
4938
5021
|
model?: string | undefined;
|
|
4939
5022
|
} | undefined;
|
|
5023
|
+
avatarParams?: {
|
|
5024
|
+
position?: {
|
|
5025
|
+
x?: string | undefined;
|
|
5026
|
+
y?: string | undefined;
|
|
5027
|
+
scale?: string | undefined;
|
|
5028
|
+
} | undefined;
|
|
5029
|
+
emotion?: "neutral" | "happy" | "sad" | "angry" | "surprised" | "relaxed" | undefined;
|
|
5030
|
+
motions?: {
|
|
5031
|
+
motion: string;
|
|
5032
|
+
at?: string | undefined;
|
|
5033
|
+
}[] | undefined;
|
|
5034
|
+
hidden?: boolean | undefined;
|
|
5035
|
+
} | undefined;
|
|
4940
5036
|
htmlImageParams?: {
|
|
4941
5037
|
model?: string | undefined;
|
|
4942
5038
|
} | undefined;
|
|
@@ -5040,6 +5136,19 @@ export declare const initializeContextFromFiles: (files: FileObject, raiseError:
|
|
|
5040
5136
|
provider?: string | undefined;
|
|
5041
5137
|
model?: string | undefined;
|
|
5042
5138
|
} | undefined;
|
|
5139
|
+
avatarParams?: {
|
|
5140
|
+
position?: {
|
|
5141
|
+
x?: string | undefined;
|
|
5142
|
+
y?: string | undefined;
|
|
5143
|
+
scale?: string | undefined;
|
|
5144
|
+
} | undefined;
|
|
5145
|
+
emotion?: "neutral" | "happy" | "sad" | "angry" | "surprised" | "relaxed" | undefined;
|
|
5146
|
+
motions?: {
|
|
5147
|
+
motion: string;
|
|
5148
|
+
at?: string | undefined;
|
|
5149
|
+
}[] | undefined;
|
|
5150
|
+
hidden?: boolean | undefined;
|
|
5151
|
+
} | undefined;
|
|
5043
5152
|
htmlImageParams?: {
|
|
5044
5153
|
provider: string;
|
|
5045
5154
|
model?: string | undefined;
|
|
@@ -5106,6 +5215,9 @@ export declare const initializeContextFromFiles: (files: FileObject, raiseError:
|
|
|
5106
5215
|
} | undefined;
|
|
5107
5216
|
} | undefined;
|
|
5108
5217
|
} | undefined;
|
|
5218
|
+
remotionParams?: {
|
|
5219
|
+
brief?: string | undefined;
|
|
5220
|
+
} | undefined;
|
|
5109
5221
|
captionParams?: {
|
|
5110
5222
|
lang?: string | undefined;
|
|
5111
5223
|
styles?: string[] | undefined;
|
|
@@ -5154,6 +5266,20 @@ export declare const initializeContextFromFiles: (files: FileObject, raiseError:
|
|
|
5154
5266
|
markdown?: string | undefined;
|
|
5155
5267
|
html?: string | undefined;
|
|
5156
5268
|
}[];
|
|
5269
|
+
avatarTracks?: {
|
|
5270
|
+
speaker: string;
|
|
5271
|
+
file: string;
|
|
5272
|
+
width: number;
|
|
5273
|
+
height: number;
|
|
5274
|
+
placements: {
|
|
5275
|
+
start: number;
|
|
5276
|
+
end: number;
|
|
5277
|
+
x: number;
|
|
5278
|
+
y: number;
|
|
5279
|
+
width: number;
|
|
5280
|
+
height: number;
|
|
5281
|
+
}[];
|
|
5282
|
+
}[] | undefined;
|
|
5157
5283
|
};
|
|
5158
5284
|
multiLingual: {
|
|
5159
5285
|
multiLingualTexts: Record<string, {
|
|
@@ -5192,6 +5318,14 @@ export declare const initializeContextFromFiles: (files: FileObject, raiseError:
|
|
|
5192
5318
|
model?: string | undefined;
|
|
5193
5319
|
baseURL?: string | undefined;
|
|
5194
5320
|
apiVersion?: string | undefined;
|
|
5321
|
+
avatar?: {
|
|
5322
|
+
source: string;
|
|
5323
|
+
position?: {
|
|
5324
|
+
x?: string | undefined;
|
|
5325
|
+
y?: string | undefined;
|
|
5326
|
+
scale?: string | undefined;
|
|
5327
|
+
} | undefined;
|
|
5328
|
+
} | undefined;
|
|
5195
5329
|
lang?: Record<string, {
|
|
5196
5330
|
voiceId: string;
|
|
5197
5331
|
displayName?: Record<string, string> | undefined;
|
|
@@ -5207,6 +5341,14 @@ export declare const initializeContextFromFiles: (files: FileObject, raiseError:
|
|
|
5207
5341
|
model?: string | undefined;
|
|
5208
5342
|
baseURL?: string | undefined;
|
|
5209
5343
|
apiVersion?: string | undefined;
|
|
5344
|
+
avatar?: {
|
|
5345
|
+
source: string;
|
|
5346
|
+
position?: {
|
|
5347
|
+
x?: string | undefined;
|
|
5348
|
+
y?: string | undefined;
|
|
5349
|
+
scale?: string | undefined;
|
|
5350
|
+
} | undefined;
|
|
5351
|
+
} | undefined;
|
|
5210
5352
|
}> | undefined;
|
|
5211
5353
|
}>;
|
|
5212
5354
|
};
|
|
@@ -5498,6 +5640,19 @@ export declare const initializeContextFromFiles: (files: FileObject, raiseError:
|
|
|
5498
5640
|
provider?: string | undefined;
|
|
5499
5641
|
model?: string | undefined;
|
|
5500
5642
|
} | undefined;
|
|
5643
|
+
avatarParams?: {
|
|
5644
|
+
position?: {
|
|
5645
|
+
x?: string | undefined;
|
|
5646
|
+
y?: string | undefined;
|
|
5647
|
+
scale?: string | undefined;
|
|
5648
|
+
} | undefined;
|
|
5649
|
+
emotion?: "neutral" | "happy" | "sad" | "angry" | "surprised" | "relaxed" | undefined;
|
|
5650
|
+
motions?: {
|
|
5651
|
+
motion: string;
|
|
5652
|
+
at?: string | undefined;
|
|
5653
|
+
}[] | undefined;
|
|
5654
|
+
hidden?: boolean | undefined;
|
|
5655
|
+
} | undefined;
|
|
5501
5656
|
htmlImageParams?: {
|
|
5502
5657
|
provider: string;
|
|
5503
5658
|
model?: string | undefined;
|
|
@@ -5564,6 +5719,9 @@ export declare const initializeContextFromFiles: (files: FileObject, raiseError:
|
|
|
5564
5719
|
} | undefined;
|
|
5565
5720
|
} | undefined;
|
|
5566
5721
|
} | undefined;
|
|
5722
|
+
remotionParams?: {
|
|
5723
|
+
brief?: string | undefined;
|
|
5724
|
+
} | undefined;
|
|
5567
5725
|
captionParams?: {
|
|
5568
5726
|
lang?: string | undefined;
|
|
5569
5727
|
styles?: string[] | undefined;
|
|
@@ -9,6 +9,7 @@ import * as pluginBeat from "./beat.js";
|
|
|
9
9
|
import * as pluginVoiceOver from "./voice_over.js";
|
|
10
10
|
import * as pluginVision from "./vision.js";
|
|
11
11
|
import * as pluginSlide from "./slide.js";
|
|
12
|
+
import * as pluginRemotion from "./remotion.js";
|
|
12
13
|
const imagePlugins = [
|
|
13
14
|
pluginTextSlide,
|
|
14
15
|
pluginMarkdown,
|
|
@@ -21,6 +22,7 @@ const imagePlugins = [
|
|
|
21
22
|
pluginVoiceOver,
|
|
22
23
|
pluginVision,
|
|
23
24
|
pluginSlide,
|
|
25
|
+
pluginRemotion,
|
|
24
26
|
];
|
|
25
27
|
export const findImagePlugin = (imageType) => {
|
|
26
28
|
return imagePlugins.find((plugin) => plugin.imageType === imageType);
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { ImageProcessorParams } from "../../types/index.js";
|
|
2
|
+
import { ComponentReviewer, ComponentWriter } from "../remotion/claude_runner.js";
|
|
3
|
+
import { SceneRenderer } from "../remotion/render.js";
|
|
4
|
+
export declare const imageType: "remotion";
|
|
5
|
+
export type RemotionDeps = {
|
|
6
|
+
ensurePackages: () => void;
|
|
7
|
+
writeComponent: ComponentWriter;
|
|
8
|
+
reviewComponent: ComponentReviewer;
|
|
9
|
+
renderScene: SceneRenderer;
|
|
10
|
+
};
|
|
11
|
+
export declare const remotionWorkDir: (imagePath: string) => string;
|
|
12
|
+
export declare const remotionStillPath: (imagePath: string) => string;
|
|
13
|
+
export declare const reviewCandidatePath: (filePath: string) => string;
|
|
14
|
+
export declare const createRemotionProcess: (deps: RemotionDeps) => (params: ImageProcessorParams) => Promise<string | undefined>;
|
|
15
|
+
export declare const process: (params: ImageProcessorParams) => Promise<string | undefined>;
|
|
16
|
+
export declare const path: (params: import("../../types/type.js").BeatPathParams) => string;
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import nodePath from "node:path";
|
|
3
|
+
import { GraphAILogger } from "graphai";
|
|
4
|
+
import { ImageMediaType } from "../../types/index.js";
|
|
5
|
+
import { localizedText } from "../utils.js";
|
|
6
|
+
import { REMOTION_COMPONENT_FILE, buildRepairPrompt, buildReviewPrompt, buildScenePrompt, remotionCacheKey, } from "../remotion/claude_prompt.js";
|
|
7
|
+
import { reviewComponentWithClaude, writeComponentWithClaude } from "../remotion/claude_runner.js";
|
|
8
|
+
import { ensureRemotionPackages, renderRemotionScene } from "../remotion/render.js";
|
|
9
|
+
import { commitFiles } from "../remotion/commit_files.js";
|
|
10
|
+
import { parrotingImagePath } from "./utils.js";
|
|
11
|
+
export const imageType = ImageMediaType.Remotion;
|
|
12
|
+
const DEFAULT_FPS = 30;
|
|
13
|
+
const MAX_REPAIR_ATTEMPTS = 2;
|
|
14
|
+
// Without audio the length is unknown; any length gives the same final frame, since the system
|
|
15
|
+
// prompt makes every animation finish before the end.
|
|
16
|
+
const STILL_ONLY_DURATION_SEC = 10;
|
|
17
|
+
const REVIEW_FRACTIONS = [0.3, 0.6, 0.95];
|
|
18
|
+
export const remotionWorkDir = (imagePath) => imagePath.replace(/(_animated)?\.(mp4|png)$/, "_remotion");
|
|
19
|
+
export const remotionStillPath = (imagePath) => imagePath.replace(/_animated\.mp4$/, ".png");
|
|
20
|
+
const loadOrWriteComponent = async (job, force, deps) => {
|
|
21
|
+
if (!force && fs.existsSync(job.codePath)) {
|
|
22
|
+
return { code: fs.readFileSync(job.codePath, "utf8"), isNew: false };
|
|
23
|
+
}
|
|
24
|
+
const code = await deps.writeComponent(buildScenePrompt(job.spec), job.workDir);
|
|
25
|
+
fs.writeFileSync(job.codePath, code);
|
|
26
|
+
return { code, isNew: true };
|
|
27
|
+
};
|
|
28
|
+
// Resolves to the code that rendered, which is a repaired version when the given one failed.
|
|
29
|
+
const renderWithRepair = async (job, code, attemptsLeft, deps, reviewStills = []) => {
|
|
30
|
+
fs.writeFileSync(nodePath.join(job.workDir, REMOTION_COMPONENT_FILE), code);
|
|
31
|
+
try {
|
|
32
|
+
const extraStills = reviewStills.map(({ frame, file }) => ({ frame, path: file }));
|
|
33
|
+
await deps.renderScene({ workDir: job.workDir, props: job.props, videoPath: job.videoPath, stillPath: job.stillPath, extraStills });
|
|
34
|
+
return code;
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
if (attemptsLeft <= 0) {
|
|
38
|
+
throw new Error(`remotion beat failed to render after repairs (code: ${job.codePath})`, { cause: error });
|
|
39
|
+
}
|
|
40
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
41
|
+
GraphAILogger.info(`remotion: render failed, asking claude -p to repair (${attemptsLeft} left): ${message.slice(0, 200)}`);
|
|
42
|
+
const repaired = await deps.writeComponent(buildRepairPrompt(job.spec, code, message), job.workDir);
|
|
43
|
+
fs.writeFileSync(job.codePath, repaired);
|
|
44
|
+
return await renderWithRepair(job, repaired, attemptsLeft - 1, deps, reviewStills);
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
const reviewStillsFor = (job) => REVIEW_FRACTIONS.map((fraction, index) => ({
|
|
48
|
+
fraction,
|
|
49
|
+
frame: Math.min(job.props.durationInFrames - 1, Math.floor(job.props.durationInFrames * fraction)),
|
|
50
|
+
file: nodePath.join(job.workDir, `review_${index}.png`),
|
|
51
|
+
}));
|
|
52
|
+
const errorMessage = (error) => (error instanceof Error ? error.message : String(error));
|
|
53
|
+
// null when the review itself failed; undefined when the reviewer approved the scene as it is.
|
|
54
|
+
const askForReview = async (job, renderedCode, reviewStills, deps) => {
|
|
55
|
+
try {
|
|
56
|
+
return await deps.reviewComponent(buildReviewPrompt(job.spec, renderedCode, reviewStills), job.workDir);
|
|
57
|
+
}
|
|
58
|
+
catch (error) {
|
|
59
|
+
GraphAILogger.info(`remotion: visual review skipped: ${errorMessage(error)}`);
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
const nodeFileOps = {
|
|
64
|
+
exists: (file) => fs.existsSync(file),
|
|
65
|
+
rename: (from, to) => fs.renameSync(from, to),
|
|
66
|
+
remove: (file) => fs.rmSync(file, { force: true }),
|
|
67
|
+
};
|
|
68
|
+
export const reviewCandidatePath = (filePath) => filePath.replace(/(\.[^./]+)$/, ".review$1");
|
|
69
|
+
// The reviewed version renders to side files that replace the first version's output only once it
|
|
70
|
+
// succeeded, so a failure leaves the scene that already rendered — files and cached code — untouched.
|
|
71
|
+
const renderReviewedVersion = async (job, improved, deps) => {
|
|
72
|
+
const candidate = {
|
|
73
|
+
...job,
|
|
74
|
+
codePath: reviewCandidatePath(job.codePath),
|
|
75
|
+
videoPath: job.videoPath && reviewCandidatePath(job.videoPath),
|
|
76
|
+
stillPath: reviewCandidatePath(job.stillPath),
|
|
77
|
+
};
|
|
78
|
+
try {
|
|
79
|
+
const renderedImprovement = await renderWithRepair(candidate, improved, MAX_REPAIR_ATTEMPTS, deps);
|
|
80
|
+
fs.writeFileSync(candidate.codePath, renderedImprovement);
|
|
81
|
+
const moves = [
|
|
82
|
+
...(job.videoPath && candidate.videoPath ? [{ from: candidate.videoPath, to: job.videoPath }] : []),
|
|
83
|
+
{ from: candidate.stillPath, to: job.stillPath },
|
|
84
|
+
{ from: candidate.codePath, to: job.codePath },
|
|
85
|
+
];
|
|
86
|
+
commitFiles(moves, nodeFileOps);
|
|
87
|
+
}
|
|
88
|
+
catch (error) {
|
|
89
|
+
GraphAILogger.info(`remotion: the reviewed version was not applied, keeping the first one: ${errorMessage(error)}`);
|
|
90
|
+
}
|
|
91
|
+
finally {
|
|
92
|
+
[candidate.codePath, candidate.videoPath, candidate.stillPath].forEach((file) => file && fs.rmSync(file, { force: true }));
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
const reviewAndImprove = async (job, renderedCode, reviewStills, deps) => {
|
|
96
|
+
const improved = await askForReview(job, renderedCode, reviewStills, deps);
|
|
97
|
+
if (improved === null)
|
|
98
|
+
return;
|
|
99
|
+
if (improved === undefined) {
|
|
100
|
+
GraphAILogger.info(`remotion: visual review approved ${job.codePath}`);
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
GraphAILogger.info(`remotion: visual review improved ${job.codePath}`);
|
|
104
|
+
await renderReviewedVersion(job, improved, deps);
|
|
105
|
+
};
|
|
106
|
+
const toFrameCount = (durationSec, fps) => {
|
|
107
|
+
const frames = Math.floor(durationSec * fps);
|
|
108
|
+
if (frames <= 0) {
|
|
109
|
+
throw new Error(`remotion: frame count is ${frames} (duration=${durationSec}, fps=${fps}). Increase duration or fps.`);
|
|
110
|
+
}
|
|
111
|
+
return frames;
|
|
112
|
+
};
|
|
113
|
+
// Lets a scene number itself and pace its place in the video; unknown when the beat is not in the script.
|
|
114
|
+
const scenePosition = (beats, index) => (index < 0 ? undefined : { index, count: beats.length });
|
|
115
|
+
// The words actually spoken over the scene: the translation when the video is rendered in another language.
|
|
116
|
+
const spokenNarration = (context, beat, index) => (index < 0 ? beat.text : localizedText(beat, context.multiLingual?.[index], context.lang, context.studio.script.lang)) || undefined;
|
|
117
|
+
const buildJob = (params, prompt, fps) => {
|
|
118
|
+
const { beat, context, imagePath, canvasSize } = params;
|
|
119
|
+
const duration = params.beatDuration ?? beat.duration;
|
|
120
|
+
const index = context.studio.script.beats.indexOf(beat);
|
|
121
|
+
const spec = {
|
|
122
|
+
prompt,
|
|
123
|
+
narration: spokenNarration(context, beat, index),
|
|
124
|
+
brief: context.presentationStyle.remotionParams?.brief || undefined,
|
|
125
|
+
position: scenePosition(context.studio.script.beats, index),
|
|
126
|
+
fps,
|
|
127
|
+
width: canvasSize.width,
|
|
128
|
+
height: canvasSize.height,
|
|
129
|
+
};
|
|
130
|
+
const workDir = remotionWorkDir(imagePath);
|
|
131
|
+
return {
|
|
132
|
+
spec,
|
|
133
|
+
workDir,
|
|
134
|
+
codePath: nodePath.join(workDir, `${remotionCacheKey(spec)}.tsx`),
|
|
135
|
+
props: { durationInFrames: toFrameCount(duration ?? STILL_ONLY_DURATION_SEC, fps), fps, width: spec.width, height: spec.height },
|
|
136
|
+
videoPath: duration !== undefined ? imagePath : undefined,
|
|
137
|
+
stillPath: remotionStillPath(imagePath),
|
|
138
|
+
};
|
|
139
|
+
};
|
|
140
|
+
export const createRemotionProcess = (deps) => async (params) => {
|
|
141
|
+
const { beat, context } = params;
|
|
142
|
+
if (!beat.image || beat.image.type !== imageType)
|
|
143
|
+
return;
|
|
144
|
+
const job = buildJob(params, beat.image.prompt, beat.image.fps ?? DEFAULT_FPS);
|
|
145
|
+
deps.ensurePackages();
|
|
146
|
+
fs.mkdirSync(job.workDir, { recursive: true });
|
|
147
|
+
const { code, isNew } = await loadOrWriteComponent(job, context.force, deps);
|
|
148
|
+
if (isNew) {
|
|
149
|
+
const reviewStills = reviewStillsFor(job);
|
|
150
|
+
const renderedCode = await renderWithRepair(job, code, MAX_REPAIR_ATTEMPTS, deps, reviewStills);
|
|
151
|
+
await reviewAndImprove(job, renderedCode, reviewStills, deps);
|
|
152
|
+
}
|
|
153
|
+
else {
|
|
154
|
+
await renderWithRepair(job, code, MAX_REPAIR_ATTEMPTS, deps);
|
|
155
|
+
}
|
|
156
|
+
return job.videoPath ?? job.stillPath;
|
|
157
|
+
};
|
|
158
|
+
export const process = createRemotionProcess({
|
|
159
|
+
ensurePackages: ensureRemotionPackages,
|
|
160
|
+
writeComponent: writeComponentWithClaude,
|
|
161
|
+
reviewComponent: reviewComponentWithClaude,
|
|
162
|
+
renderScene: renderRemotionScene,
|
|
163
|
+
});
|
|
164
|
+
export const path = parrotingImagePath;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export declare const REMOTION_COMPONENT_FILE = "Generated.tsx";
|
|
2
|
+
export declare const REMOTION_ENTRY_FILE = "index.tsx";
|
|
3
|
+
export declare const REMOTION_COMPOSITION_ID = "Beat";
|
|
4
|
+
export type RemotionSceneSpec = {
|
|
5
|
+
prompt: string;
|
|
6
|
+
narration?: string;
|
|
7
|
+
brief?: string;
|
|
8
|
+
position?: {
|
|
9
|
+
index: number;
|
|
10
|
+
count: number;
|
|
11
|
+
};
|
|
12
|
+
fps: number;
|
|
13
|
+
width: number;
|
|
14
|
+
height: number;
|
|
15
|
+
};
|
|
16
|
+
export declare const buildClaudeArgs: (userPrompt: string, systemPrompt?: string, tools?: string) => string[];
|
|
17
|
+
export declare const buildScenePrompt: (spec: RemotionSceneSpec) => string;
|
|
18
|
+
export declare const buildRepairPrompt: (spec: RemotionSceneSpec, code: string, error: string) => string;
|
|
19
|
+
export type ReviewFrame = {
|
|
20
|
+
fraction: number;
|
|
21
|
+
file: string;
|
|
22
|
+
};
|
|
23
|
+
export declare const buildReviewPrompt: (spec: RemotionSceneSpec, code: string, frames: ReviewFrame[]) => string;
|
|
24
|
+
export declare const parseReviewReply: (replyText: string) => string | undefined;
|
|
25
|
+
export declare const remotionCacheKey: (spec: RemotionSceneSpec, systemPrompt?: string) => string;
|
|
26
|
+
export declare const extractTsx: (replyText: string) => string | undefined;
|
|
27
|
+
type ClaudeJsonReply = {
|
|
28
|
+
result: string;
|
|
29
|
+
is_error?: boolean;
|
|
30
|
+
};
|
|
31
|
+
export declare const parseClaudeReply: (stdout: string) => ClaudeJsonReply;
|
|
32
|
+
export declare const buildEntrySource: () => string;
|
|
33
|
+
export {};
|