mulmocast 2.4.6 → 2.4.8
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/html/caption.html +1 -1
- package/lib/actions/captions.d.ts +2 -0
- package/lib/actions/captions.js +13 -4
- package/lib/types/schema.d.ts +9 -0
- package/lib/types/schema.js +1 -0
- package/lib/utils/context.d.ts +5 -0
- package/lib/utils/image_plugins/html_tailwind.d.ts +10 -0
- package/lib/utils/image_plugins/html_tailwind.js +20 -5
- package/package.json +6 -6
- package/scripts/test/test_image_ref_scheme.json +84 -0
package/assets/html/caption.html
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { MulmoStudioContext, PublicAPIArgs } from "../types/index.js";
|
|
2
2
|
import type { GraphData } from "graphai";
|
|
3
|
+
export declare const stripHtmlTags: (text: string) => string;
|
|
4
|
+
export declare const calculateTimingRatios: (splitTexts: string[]) => number[];
|
|
3
5
|
export declare const caption_graph_data: GraphData;
|
|
4
6
|
export declare const captions: (context: MulmoStudioContext, args?: PublicAPIArgs) => Promise<MulmoStudioContext>;
|
package/lib/actions/captions.js
CHANGED
|
@@ -44,13 +44,21 @@ const getSplitTexts = (text, texts, textSplit) => {
|
|
|
44
44
|
}
|
|
45
45
|
return [text];
|
|
46
46
|
};
|
|
47
|
-
//
|
|
48
|
-
const
|
|
49
|
-
|
|
47
|
+
// HTML tags commonly used in caption texts
|
|
48
|
+
const CAPTION_HTML_TAGS = "span|br|b|i|em|strong|u|s|div|p|a|sub|sup|mark";
|
|
49
|
+
const captionTagRegex = new RegExp(`</?(?:${CAPTION_HTML_TAGS})(?:\\s[^>]*)?\\/?>`, "gi");
|
|
50
|
+
// Strip known HTML tags to get plain text length (for timing calculation, not sanitization)
|
|
51
|
+
export const stripHtmlTags = (text) => {
|
|
52
|
+
return text.replace(captionTagRegex, "");
|
|
53
|
+
};
|
|
54
|
+
// Calculate timing ratios based on text length (HTML tags excluded)
|
|
55
|
+
export const calculateTimingRatios = (splitTexts) => {
|
|
56
|
+
const plainTexts = splitTexts.map(stripHtmlTags);
|
|
57
|
+
const totalLength = plainTexts.reduce((sum, t) => sum + t.length, 0);
|
|
50
58
|
if (totalLength === 0) {
|
|
51
59
|
return splitTexts.map(() => 1 / splitTexts.length);
|
|
52
60
|
}
|
|
53
|
-
return
|
|
61
|
+
return plainTexts.map((t) => t.length / totalLength);
|
|
54
62
|
};
|
|
55
63
|
// Convert ratios to cumulative ratios: [0.3, 0.5, 0.2] -> [0, 0.3, 0.8, 1.0]
|
|
56
64
|
const calculateCumulativeRatios = (ratios) => {
|
|
@@ -89,6 +97,7 @@ const generateBeatCaptions = async (beat, context, index) => {
|
|
|
89
97
|
width: `${canvasSize.width}`,
|
|
90
98
|
height: `${canvasSize.height}`,
|
|
91
99
|
styles: (mergedCaptionParams.styles ?? []).join(";\n"),
|
|
100
|
+
bottomOffset: `${mergedCaptionParams.bottomOffset ?? 0}`,
|
|
92
101
|
});
|
|
93
102
|
await renderHTMLToImage(htmlData, imagePath, canvasSize.width, canvasSize.height, false, true);
|
|
94
103
|
return {
|
package/lib/types/schema.d.ts
CHANGED
|
@@ -327,6 +327,7 @@ export declare const mulmoCaptionParamsSchema: z.ZodObject<{
|
|
|
327
327
|
type: z.ZodLiteral<"delimiters">;
|
|
328
328
|
delimiters: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
329
329
|
}, z.core.$strip>], "type">>;
|
|
330
|
+
bottomOffset: z.ZodOptional<z.ZodNumber>;
|
|
330
331
|
}, z.core.$strict>;
|
|
331
332
|
export declare const mulmoChartMediaSchema: z.ZodObject<{
|
|
332
333
|
type: z.ZodLiteral<"chart">;
|
|
@@ -6288,6 +6289,7 @@ export declare const mulmoBeatSchema: z.ZodObject<{
|
|
|
6288
6289
|
type: z.ZodLiteral<"delimiters">;
|
|
6289
6290
|
delimiters: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
6290
6291
|
}, z.core.$strip>], "type">>;
|
|
6292
|
+
bottomOffset: z.ZodOptional<z.ZodNumber>;
|
|
6291
6293
|
}, z.core.$strict>>;
|
|
6292
6294
|
imageNames: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
6293
6295
|
imagePrompt: z.ZodOptional<z.ZodString>;
|
|
@@ -6747,6 +6749,7 @@ export declare const mulmoPresentationStyleSchema: z.ZodObject<{
|
|
|
6747
6749
|
type: z.ZodLiteral<"delimiters">;
|
|
6748
6750
|
delimiters: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
6749
6751
|
}, z.core.$strip>], "type">>;
|
|
6752
|
+
bottomOffset: z.ZodOptional<z.ZodNumber>;
|
|
6750
6753
|
}, z.core.$strict>>;
|
|
6751
6754
|
audioParams: z.ZodDefault<z.ZodObject<{
|
|
6752
6755
|
padding: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
|
|
@@ -7202,6 +7205,7 @@ export declare const mulmoScriptSchema: z.ZodObject<{
|
|
|
7202
7205
|
type: z.ZodLiteral<"delimiters">;
|
|
7203
7206
|
delimiters: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
7204
7207
|
}, z.core.$strip>], "type">>;
|
|
7208
|
+
bottomOffset: z.ZodOptional<z.ZodNumber>;
|
|
7205
7209
|
}, z.core.$strict>>;
|
|
7206
7210
|
audioParams: z.ZodDefault<z.ZodObject<{
|
|
7207
7211
|
padding: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
|
|
@@ -10083,6 +10087,7 @@ export declare const mulmoScriptSchema: z.ZodObject<{
|
|
|
10083
10087
|
type: z.ZodLiteral<"delimiters">;
|
|
10084
10088
|
delimiters: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
10085
10089
|
}, z.core.$strip>], "type">>;
|
|
10090
|
+
bottomOffset: z.ZodOptional<z.ZodNumber>;
|
|
10086
10091
|
}, z.core.$strict>>;
|
|
10087
10092
|
imageNames: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
10088
10093
|
imagePrompt: z.ZodOptional<z.ZodString>;
|
|
@@ -10617,6 +10622,7 @@ export declare const mulmoStudioSchema: z.ZodObject<{
|
|
|
10617
10622
|
type: z.ZodLiteral<"delimiters">;
|
|
10618
10623
|
delimiters: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
10619
10624
|
}, z.core.$strip>], "type">>;
|
|
10625
|
+
bottomOffset: z.ZodOptional<z.ZodNumber>;
|
|
10620
10626
|
}, z.core.$strict>>;
|
|
10621
10627
|
audioParams: z.ZodDefault<z.ZodObject<{
|
|
10622
10628
|
padding: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
|
|
@@ -13498,6 +13504,7 @@ export declare const mulmoStudioSchema: z.ZodObject<{
|
|
|
13498
13504
|
type: z.ZodLiteral<"delimiters">;
|
|
13499
13505
|
delimiters: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
13500
13506
|
}, z.core.$strip>], "type">>;
|
|
13507
|
+
bottomOffset: z.ZodOptional<z.ZodNumber>;
|
|
13501
13508
|
}, z.core.$strict>>;
|
|
13502
13509
|
imageNames: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
13503
13510
|
imagePrompt: z.ZodOptional<z.ZodString>;
|
|
@@ -13968,6 +13975,7 @@ export declare const mulmoPromptTemplateSchema: z.ZodObject<{
|
|
|
13968
13975
|
type: z.ZodLiteral<"delimiters">;
|
|
13969
13976
|
delimiters: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
13970
13977
|
}, z.core.$strip>], "type">>;
|
|
13978
|
+
bottomOffset: z.ZodOptional<z.ZodNumber>;
|
|
13971
13979
|
}, z.core.$strict>>;
|
|
13972
13980
|
audioParams: z.ZodDefault<z.ZodObject<{
|
|
13973
13981
|
padding: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
|
|
@@ -14417,6 +14425,7 @@ export declare const mulmoPromptTemplateFileSchema: z.ZodObject<{
|
|
|
14417
14425
|
type: z.ZodLiteral<"delimiters">;
|
|
14418
14426
|
delimiters: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
14419
14427
|
}, z.core.$strip>], "type">>;
|
|
14428
|
+
bottomOffset: z.ZodOptional<z.ZodNumber>;
|
|
14420
14429
|
}, z.core.$strict>>;
|
|
14421
14430
|
audioParams: z.ZodDefault<z.ZodObject<{
|
|
14422
14431
|
padding: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
|
package/lib/types/schema.js
CHANGED
|
@@ -174,6 +174,7 @@ export const mulmoCaptionParamsSchema = z
|
|
|
174
174
|
styles: z.array(z.string()).optional(), // css styles
|
|
175
175
|
captionSplit: captionSplitSchema.optional(), // how to determine caption timing
|
|
176
176
|
textSplit: textSplitSchema.optional(), // how to split text into segments (default: none)
|
|
177
|
+
bottomOffset: z.number().min(0).max(100).optional(), // bottom offset in percentage (e.g., 20 = 20% from bottom)
|
|
177
178
|
})
|
|
178
179
|
.strict();
|
|
179
180
|
export const mulmoChartMediaSchema = z
|
package/lib/utils/context.d.ts
CHANGED
|
@@ -1960,6 +1960,7 @@ export declare const createStudioData: (_mulmoScript: MulmoScript, fileName: str
|
|
|
1960
1960
|
type: "delimiters";
|
|
1961
1961
|
delimiters?: string[] | undefined;
|
|
1962
1962
|
} | undefined;
|
|
1963
|
+
bottomOffset?: number | undefined;
|
|
1963
1964
|
} | undefined;
|
|
1964
1965
|
imageNames?: string[] | undefined;
|
|
1965
1966
|
imagePrompt?: string | undefined;
|
|
@@ -2050,6 +2051,7 @@ export declare const createStudioData: (_mulmoScript: MulmoScript, fileName: str
|
|
|
2050
2051
|
type: "delimiters";
|
|
2051
2052
|
delimiters?: string[] | undefined;
|
|
2052
2053
|
} | undefined;
|
|
2054
|
+
bottomOffset?: number | undefined;
|
|
2053
2055
|
} | undefined;
|
|
2054
2056
|
title?: string | undefined;
|
|
2055
2057
|
description?: string | undefined;
|
|
@@ -4052,6 +4054,7 @@ export declare const initializeContextFromFiles: (files: FileObject, raiseError:
|
|
|
4052
4054
|
type: "delimiters";
|
|
4053
4055
|
delimiters?: string[] | undefined;
|
|
4054
4056
|
} | undefined;
|
|
4057
|
+
bottomOffset?: number | undefined;
|
|
4055
4058
|
} | undefined;
|
|
4056
4059
|
imageNames?: string[] | undefined;
|
|
4057
4060
|
imagePrompt?: string | undefined;
|
|
@@ -4142,6 +4145,7 @@ export declare const initializeContextFromFiles: (files: FileObject, raiseError:
|
|
|
4142
4145
|
type: "delimiters";
|
|
4143
4146
|
delimiters?: string[] | undefined;
|
|
4144
4147
|
} | undefined;
|
|
4148
|
+
bottomOffset?: number | undefined;
|
|
4145
4149
|
} | undefined;
|
|
4146
4150
|
title?: string | undefined;
|
|
4147
4151
|
description?: string | undefined;
|
|
@@ -4534,6 +4538,7 @@ export declare const initializeContextFromFiles: (files: FileObject, raiseError:
|
|
|
4534
4538
|
type: "delimiters";
|
|
4535
4539
|
delimiters?: string[] | undefined;
|
|
4536
4540
|
} | undefined;
|
|
4541
|
+
bottomOffset?: number | undefined;
|
|
4537
4542
|
} | undefined;
|
|
4538
4543
|
};
|
|
4539
4544
|
sessionState: {
|
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
import { ImageProcessorParams } from "../../types/index.js";
|
|
2
2
|
export declare const imageType = "html_tailwind";
|
|
3
|
+
/**
|
|
4
|
+
* Resolve image:name references to file:// absolute paths using imageRefs.
|
|
5
|
+
* e.g., src="image:bg_office" → src="file:///abs/path/to/bg_office.png"
|
|
6
|
+
*/
|
|
7
|
+
export declare const resolveImageRefs: (html: string, imageRefs: Record<string, string>) => string;
|
|
8
|
+
/**
|
|
9
|
+
* Resolve relative paths in src attributes to file:// absolute paths.
|
|
10
|
+
* Paths starting with http://, https://, file://, data:, image:, or / are left unchanged.
|
|
11
|
+
*/
|
|
12
|
+
export declare const resolveRelativeImagePaths: (html: string, baseDirPath: string) => string;
|
|
3
13
|
export declare const process: (params: ImageProcessorParams) => Promise<string | undefined>;
|
|
4
14
|
export declare const path: (params: ImageProcessorParams) => string;
|
|
5
15
|
export declare const html: (params: ImageProcessorParams) => Promise<string | undefined>;
|
|
@@ -6,12 +6,25 @@ import { renderHTMLToImage, interpolate, renderHTMLToFrames } from "../html_rend
|
|
|
6
6
|
import { framesToVideo } from "../ffmpeg_utils.js";
|
|
7
7
|
import { parrotingImagePath } from "./utils.js";
|
|
8
8
|
export const imageType = "html_tailwind";
|
|
9
|
+
/**
|
|
10
|
+
* Resolve image:name references to file:// absolute paths using imageRefs.
|
|
11
|
+
* e.g., src="image:bg_office" → src="file:///abs/path/to/bg_office.png"
|
|
12
|
+
*/
|
|
13
|
+
export const resolveImageRefs = (html, imageRefs) => {
|
|
14
|
+
return html.replace(/(\bsrc\s*=\s*)(["'])image:([^"']+)\2/gi, (match, prefix, quote, name) => {
|
|
15
|
+
const resolvedPath = imageRefs[name];
|
|
16
|
+
if (!resolvedPath) {
|
|
17
|
+
return match;
|
|
18
|
+
}
|
|
19
|
+
return `${prefix}${quote}file://${resolvedPath}${quote}`;
|
|
20
|
+
});
|
|
21
|
+
};
|
|
9
22
|
/**
|
|
10
23
|
* Resolve relative paths in src attributes to file:// absolute paths.
|
|
11
|
-
* Paths starting with http://, https://, file://, data:, or / are left unchanged.
|
|
24
|
+
* Paths starting with http://, https://, file://, data:, image:, or / are left unchanged.
|
|
12
25
|
*/
|
|
13
|
-
const resolveRelativeImagePaths = (html, baseDirPath) => {
|
|
14
|
-
return html.replace(/(\bsrc\s*=\s*)(["'])((?!https?:\/\/|file:\/\/|data:|\/)[^"']+)\2/gi, (_, prefix, quote, relativePath) => {
|
|
26
|
+
export const resolveRelativeImagePaths = (html, baseDirPath) => {
|
|
27
|
+
return html.replace(/(\bsrc\s*=\s*)(["'])((?!https?:\/\/|file:\/\/|data:|image:|\/)[^"']+)\2/gi, (_, prefix, quote, relativePath) => {
|
|
15
28
|
const absolutePath = nodePath.resolve(baseDirPath, relativePath);
|
|
16
29
|
return `${prefix}${quote}file://${absolutePath}${quote}`;
|
|
17
30
|
});
|
|
@@ -64,7 +77,8 @@ const processHtmlTailwindAnimated = async (params) => {
|
|
|
64
77
|
fps: String(fps),
|
|
65
78
|
custom_style: "",
|
|
66
79
|
});
|
|
67
|
-
const
|
|
80
|
+
const resolvedRefs = resolveImageRefs(rawHtmlData, params.imageRefs ?? {});
|
|
81
|
+
const htmlData = resolveRelativeImagePaths(resolvedRefs, context.fileDirs.mulmoFileDirPath);
|
|
68
82
|
// imagePath is set to the .mp4 path by imagePluginAgent for animated beats
|
|
69
83
|
const videoPath = imagePath;
|
|
70
84
|
// Create frames directory next to the video file
|
|
@@ -90,7 +104,8 @@ const processHtmlTailwindStatic = async (params) => {
|
|
|
90
104
|
html_body: html,
|
|
91
105
|
user_script: buildUserScript(script),
|
|
92
106
|
});
|
|
93
|
-
const
|
|
107
|
+
const resolvedRefs = resolveImageRefs(rawHtmlData, params.imageRefs ?? {});
|
|
108
|
+
const htmlData = resolveRelativeImagePaths(resolvedRefs, context.fileDirs.mulmoFileDirPath);
|
|
94
109
|
await renderHTMLToImage(htmlData, imagePath, canvasSize.width, canvasSize.height);
|
|
95
110
|
return imagePath;
|
|
96
111
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mulmocast",
|
|
3
|
-
"version": "2.4.
|
|
3
|
+
"version": "2.4.8",
|
|
4
4
|
"description": "",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.node.js",
|
|
@@ -87,7 +87,7 @@
|
|
|
87
87
|
"homepage": "https://github.com/receptron/mulmocast-cli#readme",
|
|
88
88
|
"dependencies": {
|
|
89
89
|
"@google-cloud/text-to-speech": "^6.4.0",
|
|
90
|
-
"@google/genai": "^1.
|
|
90
|
+
"@google/genai": "^1.44.0",
|
|
91
91
|
"@graphai/anthropic_agent": "^2.0.12",
|
|
92
92
|
"@graphai/browserless_agent": "^2.0.2",
|
|
93
93
|
"@graphai/gemini_agent": "^2.0.5",
|
|
@@ -108,10 +108,10 @@
|
|
|
108
108
|
"fluent-ffmpeg": "^2.1.3",
|
|
109
109
|
"graphai": "^2.0.16",
|
|
110
110
|
"jsdom": "^28.1.0",
|
|
111
|
-
"marked": "^17.0.
|
|
111
|
+
"marked": "^17.0.4",
|
|
112
112
|
"mulmocast-vision": "^1.0.8",
|
|
113
113
|
"ora": "^9.3.0",
|
|
114
|
-
"puppeteer": "^24.
|
|
114
|
+
"puppeteer": "^24.38.0",
|
|
115
115
|
"replicate": "^1.4.0",
|
|
116
116
|
"yaml": "^2.8.2",
|
|
117
117
|
"yargs": "^18.0.0",
|
|
@@ -128,8 +128,8 @@
|
|
|
128
128
|
"eslint": "^10.0.2",
|
|
129
129
|
"eslint-config-prettier": "^10.1.8",
|
|
130
130
|
"eslint-plugin-prettier": "^5.5.5",
|
|
131
|
-
"eslint-plugin-sonarjs": "^4.0.
|
|
132
|
-
"globals": "^17.
|
|
131
|
+
"eslint-plugin-sonarjs": "^4.0.1",
|
|
132
|
+
"globals": "^17.4.0",
|
|
133
133
|
"prettier": "^3.8.1",
|
|
134
134
|
"tsx": "^4.21.0",
|
|
135
135
|
"typescript": "6.0.0-beta",
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$mulmocast": { "version": "1.1" },
|
|
3
|
+
"lang": "ja",
|
|
4
|
+
"canvasSize": { "width": 1080, "height": 1920 },
|
|
5
|
+
"title": "image: スキームテスト",
|
|
6
|
+
"speechParams": {
|
|
7
|
+
"speakers": {
|
|
8
|
+
"Presenter": { "voiceId": "shimmer" }
|
|
9
|
+
}
|
|
10
|
+
},
|
|
11
|
+
"audioParams": {
|
|
12
|
+
"bgm": {
|
|
13
|
+
"kind": "url",
|
|
14
|
+
"url": "https://github.com/receptron/mulmocast-media/raw/refs/heads/main/bgms/theme001.mp3"
|
|
15
|
+
},
|
|
16
|
+
"bgmVolume": 0.12
|
|
17
|
+
},
|
|
18
|
+
"imageParams": {
|
|
19
|
+
"provider": "google",
|
|
20
|
+
"model": "gemini-3.1-flash-image-preview",
|
|
21
|
+
"images": {
|
|
22
|
+
"bg_office": {
|
|
23
|
+
"type": "imagePrompt",
|
|
24
|
+
"prompt": "Empty modern tech office with abandoned desks and computer screens still glowing, dramatic morning sunlight, photorealistic, vertical composition 9:16"
|
|
25
|
+
},
|
|
26
|
+
"bg_city": {
|
|
27
|
+
"type": "imagePrompt",
|
|
28
|
+
"prompt": "Futuristic city skyline at sunset with neon lights reflecting on glass buildings, vibrant colors, photorealistic, vertical composition 9:16"
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
"beats": [
|
|
33
|
+
{
|
|
34
|
+
"text": "image:スキームのテストです。背景画像がimageParamsで生成した画像に置き換わります。",
|
|
35
|
+
"speaker": "Presenter",
|
|
36
|
+
"image": {
|
|
37
|
+
"type": "html_tailwind",
|
|
38
|
+
"html": [
|
|
39
|
+
"<div class='h-full w-full overflow-hidden relative bg-black'>",
|
|
40
|
+
" <div id='wrap' style='position:absolute;inset:0;overflow:hidden'>",
|
|
41
|
+
" <img src='image:bg_office' style='width:100%;height:100%;object-fit:cover;filter:brightness(0.7)' />",
|
|
42
|
+
" </div>",
|
|
43
|
+
" <div style='position:absolute;top:50%;left:40px;right:40px;transform:translateY(-50%);text-align:center'>",
|
|
44
|
+
" <div style='display:inline-block;background:rgba(239,68,68,0.85);padding:12px 32px;border-radius:12px'>",
|
|
45
|
+
" <span style='color:white;font-size:80px;font-weight:900'>image: test</span>",
|
|
46
|
+
" </div>",
|
|
47
|
+
" <div style='color:white;font-size:48px;font-weight:900;margin-top:20px;text-shadow:0 4px 16px rgba(0,0,0,0.9)'>bg_office を参照</div>",
|
|
48
|
+
" </div>",
|
|
49
|
+
"</div>"
|
|
50
|
+
],
|
|
51
|
+
"script": [
|
|
52
|
+
"const animation = new MulmoAnimation();",
|
|
53
|
+
"animation.animate('#wrap', { scale: [1.0, 1.15] }, { start: 0, end: 'auto', easing: 'linear' });"
|
|
54
|
+
],
|
|
55
|
+
"animation": true
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
"text": "二枚目の背景です。別のimageRefsキーを参照しています。",
|
|
60
|
+
"speaker": "Presenter",
|
|
61
|
+
"image": {
|
|
62
|
+
"type": "html_tailwind",
|
|
63
|
+
"html": [
|
|
64
|
+
"<div class='h-full w-full overflow-hidden relative bg-black'>",
|
|
65
|
+
" <div id='wrap' style='position:absolute;inset:0;overflow:hidden'>",
|
|
66
|
+
" <img src='image:bg_city' style='width:100%;height:100%;object-fit:cover;filter:brightness(0.6)' />",
|
|
67
|
+
" </div>",
|
|
68
|
+
" <div style='position:absolute;top:50%;left:40px;right:40px;transform:translateY(-50%);text-align:center'>",
|
|
69
|
+
" <div style='display:inline-block;background:rgba(59,130,246,0.85);padding:12px 32px;border-radius:12px'>",
|
|
70
|
+
" <span style='color:white;font-size:80px;font-weight:900'>image: test</span>",
|
|
71
|
+
" </div>",
|
|
72
|
+
" <div style='color:white;font-size:48px;font-weight:900;margin-top:20px;text-shadow:0 4px 16px rgba(0,0,0,0.9)'>bg_city を参照</div>",
|
|
73
|
+
" </div>",
|
|
74
|
+
"</div>"
|
|
75
|
+
],
|
|
76
|
+
"script": [
|
|
77
|
+
"const animation = new MulmoAnimation();",
|
|
78
|
+
"animation.animate('#wrap', { translateX: [0, -30] }, { start: 0, end: 'auto', easing: 'linear' });"
|
|
79
|
+
],
|
|
80
|
+
"animation": true
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
]
|
|
84
|
+
}
|