mulmocast 2.2.4 → 2.2.6
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/cli/commands/tool/info/builder.js +1 -1
- package/lib/cli/commands/tool/info/handler.js +20 -2
- package/lib/cli/commands/tool/info/index.d.ts +1 -1
- package/lib/cli/commands/tool/info/index.js +1 -1
- package/lib/slide/blocks.js +21 -28
- package/lib/slide/index.d.ts +3 -2
- package/lib/slide/index.js +1 -1
- package/lib/slide/layouts/big_quote.js +4 -4
- package/lib/slide/layouts/columns.js +3 -5
- package/lib/slide/layouts/comparison.js +3 -5
- package/lib/slide/layouts/funnel.js +3 -5
- package/lib/slide/layouts/grid.js +3 -8
- package/lib/slide/layouts/matrix.js +2 -2
- package/lib/slide/layouts/split.js +3 -3
- package/lib/slide/layouts/stats.js +5 -18
- package/lib/slide/layouts/table.js +2 -4
- package/lib/slide/layouts/timeline.js +3 -13
- package/lib/slide/layouts/title.js +3 -3
- package/lib/slide/render.d.ts +15 -1
- package/lib/slide/render.js +45 -3
- package/lib/slide/schema.d.ts +103 -0
- package/lib/slide/schema.js +32 -0
- package/lib/slide/utils.d.ts +27 -1
- package/lib/slide/utils.js +38 -10
- package/lib/types/schema.d.ts +410 -0
- package/lib/types/schema.js +2 -1
- package/lib/types/slide.d.ts +103 -0
- package/lib/types/slide.js +32 -0
- package/lib/utils/context.d.ts +155 -0
- package/lib/utils/image_plugins/slide.js +50 -2
- package/package.json +1 -1
- package/scripts/test/branding/banner.jpg +0 -0
- package/scripts/test/branding/logo.svg +3 -0
- package/scripts/test/test_slide_branding.json +107 -0
package/lib/slide/schema.d.ts
CHANGED
|
@@ -4351,6 +4351,66 @@ export declare const funnelSlideSchema: z.ZodObject<{
|
|
|
4351
4351
|
}, z.core.$strip>>;
|
|
4352
4352
|
layout: z.ZodLiteral<"funnel">;
|
|
4353
4353
|
}, z.core.$strip>;
|
|
4354
|
+
export declare const slideBrandingLogoSchema: z.ZodObject<{
|
|
4355
|
+
source: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
4356
|
+
kind: z.ZodLiteral<"url">;
|
|
4357
|
+
url: z.ZodURL;
|
|
4358
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
4359
|
+
kind: z.ZodLiteral<"base64">;
|
|
4360
|
+
data: z.ZodString;
|
|
4361
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
4362
|
+
kind: z.ZodLiteral<"path">;
|
|
4363
|
+
path: z.ZodString;
|
|
4364
|
+
}, z.core.$strict>], "kind">;
|
|
4365
|
+
position: z.ZodDefault<z.ZodEnum<{
|
|
4366
|
+
"top-left": "top-left";
|
|
4367
|
+
"top-right": "top-right";
|
|
4368
|
+
"bottom-left": "bottom-left";
|
|
4369
|
+
"bottom-right": "bottom-right";
|
|
4370
|
+
}>>;
|
|
4371
|
+
width: z.ZodDefault<z.ZodNumber>;
|
|
4372
|
+
}, z.core.$strict>;
|
|
4373
|
+
export declare const slideBrandingSchema: z.ZodObject<{
|
|
4374
|
+
logo: z.ZodOptional<z.ZodObject<{
|
|
4375
|
+
source: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
4376
|
+
kind: z.ZodLiteral<"url">;
|
|
4377
|
+
url: z.ZodURL;
|
|
4378
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
4379
|
+
kind: z.ZodLiteral<"base64">;
|
|
4380
|
+
data: z.ZodString;
|
|
4381
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
4382
|
+
kind: z.ZodLiteral<"path">;
|
|
4383
|
+
path: z.ZodString;
|
|
4384
|
+
}, z.core.$strict>], "kind">;
|
|
4385
|
+
position: z.ZodDefault<z.ZodEnum<{
|
|
4386
|
+
"top-left": "top-left";
|
|
4387
|
+
"top-right": "top-right";
|
|
4388
|
+
"bottom-left": "bottom-left";
|
|
4389
|
+
"bottom-right": "bottom-right";
|
|
4390
|
+
}>>;
|
|
4391
|
+
width: z.ZodDefault<z.ZodNumber>;
|
|
4392
|
+
}, z.core.$strict>>;
|
|
4393
|
+
backgroundImage: z.ZodOptional<z.ZodObject<{
|
|
4394
|
+
source: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
4395
|
+
kind: z.ZodLiteral<"url">;
|
|
4396
|
+
url: z.ZodURL;
|
|
4397
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
4398
|
+
kind: z.ZodLiteral<"base64">;
|
|
4399
|
+
data: z.ZodString;
|
|
4400
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
4401
|
+
kind: z.ZodLiteral<"path">;
|
|
4402
|
+
path: z.ZodString;
|
|
4403
|
+
}, z.core.$strict>], "kind">;
|
|
4404
|
+
size: z.ZodOptional<z.ZodEnum<{
|
|
4405
|
+
contain: "contain";
|
|
4406
|
+
cover: "cover";
|
|
4407
|
+
fill: "fill";
|
|
4408
|
+
auto: "auto";
|
|
4409
|
+
}>>;
|
|
4410
|
+
opacity: z.ZodOptional<z.ZodNumber>;
|
|
4411
|
+
bgOpacity: z.ZodOptional<z.ZodNumber>;
|
|
4412
|
+
}, z.core.$strip>>;
|
|
4413
|
+
}, z.core.$strict>;
|
|
4354
4414
|
export declare const slideLayoutSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
4355
4415
|
title: z.ZodString;
|
|
4356
4416
|
subtitle: z.ZodOptional<z.ZodString>;
|
|
@@ -8985,6 +9045,47 @@ export declare const mulmoSlideMediaSchema: z.ZodObject<{
|
|
|
8985
9045
|
layout: z.ZodLiteral<"funnel">;
|
|
8986
9046
|
}, z.core.$strip>], "layout">;
|
|
8987
9047
|
reference: z.ZodOptional<z.ZodString>;
|
|
9048
|
+
branding: z.ZodOptional<z.ZodNullable<z.ZodObject<{
|
|
9049
|
+
logo: z.ZodOptional<z.ZodObject<{
|
|
9050
|
+
source: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
9051
|
+
kind: z.ZodLiteral<"url">;
|
|
9052
|
+
url: z.ZodURL;
|
|
9053
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
9054
|
+
kind: z.ZodLiteral<"base64">;
|
|
9055
|
+
data: z.ZodString;
|
|
9056
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
9057
|
+
kind: z.ZodLiteral<"path">;
|
|
9058
|
+
path: z.ZodString;
|
|
9059
|
+
}, z.core.$strict>], "kind">;
|
|
9060
|
+
position: z.ZodDefault<z.ZodEnum<{
|
|
9061
|
+
"top-left": "top-left";
|
|
9062
|
+
"top-right": "top-right";
|
|
9063
|
+
"bottom-left": "bottom-left";
|
|
9064
|
+
"bottom-right": "bottom-right";
|
|
9065
|
+
}>>;
|
|
9066
|
+
width: z.ZodDefault<z.ZodNumber>;
|
|
9067
|
+
}, z.core.$strict>>;
|
|
9068
|
+
backgroundImage: z.ZodOptional<z.ZodObject<{
|
|
9069
|
+
source: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
9070
|
+
kind: z.ZodLiteral<"url">;
|
|
9071
|
+
url: z.ZodURL;
|
|
9072
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
9073
|
+
kind: z.ZodLiteral<"base64">;
|
|
9074
|
+
data: z.ZodString;
|
|
9075
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
9076
|
+
kind: z.ZodLiteral<"path">;
|
|
9077
|
+
path: z.ZodString;
|
|
9078
|
+
}, z.core.$strict>], "kind">;
|
|
9079
|
+
size: z.ZodOptional<z.ZodEnum<{
|
|
9080
|
+
contain: "contain";
|
|
9081
|
+
cover: "cover";
|
|
9082
|
+
fill: "fill";
|
|
9083
|
+
auto: "auto";
|
|
9084
|
+
}>>;
|
|
9085
|
+
opacity: z.ZodOptional<z.ZodNumber>;
|
|
9086
|
+
bgOpacity: z.ZodOptional<z.ZodNumber>;
|
|
9087
|
+
}, z.core.$strip>>;
|
|
9088
|
+
}, z.core.$strict>>>;
|
|
8988
9089
|
}, z.core.$strict>;
|
|
8989
9090
|
export type AccentColorKey = z.infer<typeof accentColorKeySchema>;
|
|
8990
9091
|
export type SlideThemeColors = z.infer<typeof slideThemeColorsSchema>;
|
|
@@ -9027,4 +9128,6 @@ export type TableCellValue = z.infer<typeof tableCellValueSchema>;
|
|
|
9027
9128
|
export type TableSlide = z.infer<typeof tableSlideSchema>;
|
|
9028
9129
|
export type FunnelStage = z.infer<typeof funnelStageSchema>;
|
|
9029
9130
|
export type FunnelSlide = z.infer<typeof funnelSlideSchema>;
|
|
9131
|
+
export type SlideBrandingLogo = z.infer<typeof slideBrandingLogoSchema>;
|
|
9132
|
+
export type SlideBranding = z.infer<typeof slideBrandingSchema>;
|
|
9030
9133
|
export type MulmoSlideMedia = z.infer<typeof mulmoSlideMediaSchema>;
|
package/lib/slide/schema.js
CHANGED
|
@@ -358,6 +358,37 @@ export const funnelSlideSchema = z.object({
|
|
|
358
358
|
callout: calloutBarSchema.optional(),
|
|
359
359
|
});
|
|
360
360
|
// ═══════════════════════════════════════════════════════════
|
|
361
|
+
// Branding — logo & background image overlay
|
|
362
|
+
// ═══════════════════════════════════════════════════════════
|
|
363
|
+
/**
|
|
364
|
+
* Media source for branding assets (self-contained definition to avoid
|
|
365
|
+
* circular dependency with src/types/schema.ts).
|
|
366
|
+
*/
|
|
367
|
+
const slideMediaSourceSchema = z.discriminatedUnion("kind", [
|
|
368
|
+
z.object({ kind: z.literal("url"), url: z.url() }).strict(),
|
|
369
|
+
z.object({ kind: z.literal("base64"), data: z.string().min(1) }).strict(),
|
|
370
|
+
z.object({ kind: z.literal("path"), path: z.string().min(1) }).strict(),
|
|
371
|
+
]);
|
|
372
|
+
const slideBackgroundImageSourceSchema = z.object({
|
|
373
|
+
source: slideMediaSourceSchema,
|
|
374
|
+
size: z.enum(["cover", "contain", "fill", "auto"]).optional(),
|
|
375
|
+
opacity: z.number().min(0).max(1).optional(),
|
|
376
|
+
bgOpacity: z.number().min(0).max(1).optional(),
|
|
377
|
+
});
|
|
378
|
+
export const slideBrandingLogoSchema = z
|
|
379
|
+
.object({
|
|
380
|
+
source: slideMediaSourceSchema,
|
|
381
|
+
position: z.enum(["top-left", "top-right", "bottom-left", "bottom-right"]).default("top-right"),
|
|
382
|
+
width: z.number().positive().default(120),
|
|
383
|
+
})
|
|
384
|
+
.strict();
|
|
385
|
+
export const slideBrandingSchema = z
|
|
386
|
+
.object({
|
|
387
|
+
logo: slideBrandingLogoSchema.optional(),
|
|
388
|
+
backgroundImage: slideBackgroundImageSourceSchema.optional(),
|
|
389
|
+
})
|
|
390
|
+
.strict();
|
|
391
|
+
// ═══════════════════════════════════════════════════════════
|
|
361
392
|
// Slide Union & Media Schema
|
|
362
393
|
// ═══════════════════════════════════════════════════════════
|
|
363
394
|
export const slideLayoutSchema = z.discriminatedUnion("layout", [
|
|
@@ -380,5 +411,6 @@ export const mulmoSlideMediaSchema = z
|
|
|
380
411
|
theme: slideThemeSchema.optional(),
|
|
381
412
|
slide: slideLayoutSchema,
|
|
382
413
|
reference: z.string().optional(),
|
|
414
|
+
branding: slideBrandingSchema.nullable().optional(),
|
|
383
415
|
})
|
|
384
416
|
.strict();
|
package/lib/slide/utils.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { SlideTheme, SlideLayout } from "./schema.js";
|
|
1
|
+
import type { SlideTheme, SlideLayout, CalloutBar } from "./schema.js";
|
|
2
2
|
/** Escape HTML special characters */
|
|
3
3
|
export declare const escapeHtml: (s: string) => string;
|
|
4
4
|
/** Escape HTML and convert newlines to <br> */
|
|
@@ -13,6 +13,18 @@ export declare const renderInlineMarkup: (s: string) => string;
|
|
|
13
13
|
export declare const sanitizeHex: (s: string) => string;
|
|
14
14
|
/** Accent color key → Tailwind class segment: "primary" → "d-primary" */
|
|
15
15
|
export declare const c: (key: string) => string;
|
|
16
|
+
/** Resolve accent color key with "primary" as fallback */
|
|
17
|
+
export declare const resolveAccent: (color: string | undefined) => string;
|
|
18
|
+
/** Resolve item-level color with slide-level fallback then "primary" */
|
|
19
|
+
export declare const resolveItemColor: (itemColor: string | undefined, slideAccent: string | undefined) => string;
|
|
20
|
+
/** Render a horizontal accent bar (3px full-width). Pass extraClass for width/margin variants. */
|
|
21
|
+
export declare const accentBar: (colorKey: string, extraClass?: string) => string;
|
|
22
|
+
/** Render an optional block title (chart, mermaid, table) */
|
|
23
|
+
export declare const blockTitle: (title: string | undefined) => string;
|
|
24
|
+
/** Resolve change indicator color: "success" for positive (+), "danger" for negative */
|
|
25
|
+
export declare const resolveChangeColor: (change: string) => string;
|
|
26
|
+
/** Render the optional callout bar at the bottom of a slide, or empty string */
|
|
27
|
+
export declare const renderOptionalCallout: (callout: CalloutBar | undefined) => string;
|
|
16
28
|
/** Build the Tailwind config JSON string for theme colors and fonts */
|
|
17
29
|
export declare const buildTailwindConfig: (theme: SlideTheme) => string;
|
|
18
30
|
/** Render a numbered circle badge */
|
|
@@ -29,6 +41,13 @@ export declare const renderCalloutBar: (obj: {
|
|
|
29
41
|
align?: string;
|
|
30
42
|
leftBar?: boolean;
|
|
31
43
|
}) => string;
|
|
44
|
+
/** Render header text elements (stepLabel + title + subtitle) without wrapping div */
|
|
45
|
+
export declare const renderHeaderText: (data: {
|
|
46
|
+
accentColor?: string;
|
|
47
|
+
stepLabel?: string;
|
|
48
|
+
title: string;
|
|
49
|
+
subtitle?: string;
|
|
50
|
+
}) => string;
|
|
32
51
|
/** Render the common slide header (accent bar + title + subtitle) */
|
|
33
52
|
export declare const slideHeader: (data: {
|
|
34
53
|
accentColor?: string;
|
|
@@ -36,6 +55,13 @@ export declare const slideHeader: (data: {
|
|
|
36
55
|
title: string;
|
|
37
56
|
subtitle?: string;
|
|
38
57
|
}) => string;
|
|
58
|
+
/** Render accent bar + vertically-centered wrapper with header text (used by stats, timeline) */
|
|
59
|
+
export declare const centeredSlideHeader: (data: {
|
|
60
|
+
accentColor?: string;
|
|
61
|
+
stepLabel?: string;
|
|
62
|
+
title: string;
|
|
63
|
+
subtitle?: string;
|
|
64
|
+
}) => string;
|
|
39
65
|
/** Generate a unique ID with the given prefix (e.g. "chart-0", "mermaid-1") */
|
|
40
66
|
export declare const generateSlideId: (prefix: string) => string;
|
|
41
67
|
/** Reset the ID counter (for testing) */
|
package/lib/slide/utils.js
CHANGED
|
@@ -45,6 +45,27 @@ export const sanitizeHex = (s) => {
|
|
|
45
45
|
export const c = (key) => {
|
|
46
46
|
return `d-${sanitizeCssClass(key)}`;
|
|
47
47
|
};
|
|
48
|
+
// ═══════════════════════════════════════════════════════════
|
|
49
|
+
// Shared micro-helpers for HTML generation
|
|
50
|
+
// ═══════════════════════════════════════════════════════════
|
|
51
|
+
/** Default accent color used when none is specified */
|
|
52
|
+
const DEFAULT_ACCENT = "primary";
|
|
53
|
+
/** Resolve accent color key with "primary" as fallback */
|
|
54
|
+
export const resolveAccent = (color) => color || DEFAULT_ACCENT;
|
|
55
|
+
/** Resolve item-level color with slide-level fallback then "primary" */
|
|
56
|
+
export const resolveItemColor = (itemColor, slideAccent) => itemColor || slideAccent || DEFAULT_ACCENT;
|
|
57
|
+
/** Render a horizontal accent bar (3px full-width). Pass extraClass for width/margin variants. */
|
|
58
|
+
export const accentBar = (colorKey, extraClass) => `<div class="h-[3px] bg-${c(colorKey)} shrink-0 ${extraClass || ""}"></div>`;
|
|
59
|
+
/** Render an optional block title (chart, mermaid, table) */
|
|
60
|
+
export const blockTitle = (title) => title ? `<p class="text-sm font-bold text-d-text font-body mb-2">${renderInlineMarkup(title)}</p>` : "";
|
|
61
|
+
/** Resolve change indicator color: "success" for positive (+), "danger" for negative */
|
|
62
|
+
export const resolveChangeColor = (change) => (/\+/.test(change) ? "success" : "danger");
|
|
63
|
+
/** Render the optional callout bar at the bottom of a slide, or empty string */
|
|
64
|
+
export const renderOptionalCallout = (callout) => {
|
|
65
|
+
if (!callout)
|
|
66
|
+
return "";
|
|
67
|
+
return `<div class="mt-auto pb-4">${renderCalloutBar(callout)}</div>`;
|
|
68
|
+
};
|
|
48
69
|
const colorKeyMap = {
|
|
49
70
|
bg: "bg",
|
|
50
71
|
bgCard: "card",
|
|
@@ -97,7 +118,7 @@ export const iconSquare = (icon, colorKey) => {
|
|
|
97
118
|
/** Render a card wrapper with accent top bar */
|
|
98
119
|
export const cardWrap = (accentColor, innerHtml, extraClass) => {
|
|
99
120
|
return `<div class="bg-d-card rounded-lg shadow-lg overflow-hidden flex flex-col min-h-0 ${sanitizeCssClass(extraClass || "")}">
|
|
100
|
-
|
|
121
|
+
${accentBar(accentColor)}
|
|
101
122
|
<div class="p-5 flex flex-col flex-1 min-h-0 overflow-hidden">
|
|
102
123
|
${innerHtml}
|
|
103
124
|
</div>
|
|
@@ -116,22 +137,29 @@ export const renderCalloutBar = (obj) => {
|
|
|
116
137
|
<div class="px-4 py-3 text-sm font-body flex-1">${inner}</div>
|
|
117
138
|
</div>`;
|
|
118
139
|
};
|
|
119
|
-
/** Render
|
|
120
|
-
export const
|
|
121
|
-
const accent = data.accentColor
|
|
140
|
+
/** Render header text elements (stepLabel + title + subtitle) without wrapping div */
|
|
141
|
+
export const renderHeaderText = (data) => {
|
|
142
|
+
const accent = resolveAccent(data.accentColor);
|
|
122
143
|
const lines = [];
|
|
123
|
-
lines.push(`<div class="h-[3px] bg-${c(accent)} shrink-0"></div>`);
|
|
124
|
-
lines.push(`<div class="px-12 pt-5 shrink-0">`);
|
|
125
144
|
if (data.stepLabel) {
|
|
126
|
-
lines.push(
|
|
145
|
+
lines.push(`<p class="text-sm font-bold text-${c(accent)} font-body">${renderInlineMarkup(data.stepLabel)}</p>`);
|
|
127
146
|
}
|
|
128
|
-
lines.push(
|
|
147
|
+
lines.push(`<h2 class="text-[42px] leading-tight font-title font-bold text-d-text">${renderInlineMarkup(data.title)}</h2>`);
|
|
129
148
|
if (data.subtitle) {
|
|
130
|
-
lines.push(
|
|
149
|
+
lines.push(`<p class="text-[15px] text-d-dim mt-2 font-body">${renderInlineMarkup(data.subtitle)}</p>`);
|
|
131
150
|
}
|
|
132
|
-
lines.push(`</div>`);
|
|
133
151
|
return lines.join("\n");
|
|
134
152
|
};
|
|
153
|
+
/** Render the common slide header (accent bar + title + subtitle) */
|
|
154
|
+
export const slideHeader = (data) => {
|
|
155
|
+
const accent = resolveAccent(data.accentColor);
|
|
156
|
+
return [accentBar(accent), `<div class="px-12 pt-5 shrink-0">`, renderHeaderText(data), `</div>`].join("\n");
|
|
157
|
+
};
|
|
158
|
+
/** Render accent bar + vertically-centered wrapper with header text (used by stats, timeline) */
|
|
159
|
+
export const centeredSlideHeader = (data) => {
|
|
160
|
+
const accent = resolveAccent(data.accentColor);
|
|
161
|
+
return [accentBar(accent), `<div class="flex-1 flex flex-col justify-center px-12 min-h-0">`, renderHeaderText(data)].join("\n");
|
|
162
|
+
};
|
|
135
163
|
// ═══════════════════════════════════════════════════════════
|
|
136
164
|
// Counter-based ID generation (unique within a single slide)
|
|
137
165
|
// ═══════════════════════════════════════════════════════════
|