@mulmocast/deck 0.2.0 → 0.4.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/lib/blocks.js +12 -2
- package/lib/layouts/big_quote.js +4 -1
- package/lib/layouts/columns.js +3 -2
- package/lib/layouts/index.js +3 -0
- package/lib/layouts/manifesto.d.ts +2 -0
- package/lib/layouts/manifesto.js +39 -0
- package/lib/layouts/stats.js +3 -0
- package/lib/layouts/timeline.js +5 -2
- package/lib/layouts/title.js +6 -1
- package/lib/schema.d.ts +978 -10
- package/lib/schema.js +45 -1
- package/lib/utils.d.ts +25 -1
- package/lib/utils.js +30 -1
- package/package.json +1 -1
package/lib/schema.js
CHANGED
|
@@ -50,12 +50,16 @@ export const textBlockSchema = z.object({
|
|
|
50
50
|
});
|
|
51
51
|
/** Sub-bullet item: plain string or object with text */
|
|
52
52
|
const subBulletItemSchema = z.union([z.string(), z.object({ text: z.string() })]);
|
|
53
|
-
/**
|
|
53
|
+
/** Status-icon variants for bullets (renders ✓ / ✕ / ⚠ in the accent color of the variant). */
|
|
54
|
+
export const bulletIconSchema = z.enum(["ok", "no", "warn"]);
|
|
55
|
+
/** Bullet item: plain string, or object with text + optional sub-items + optional status icon. */
|
|
54
56
|
export const bulletItemSchema = z.union([
|
|
55
57
|
z.string(),
|
|
56
58
|
z.object({
|
|
57
59
|
text: z.string(),
|
|
58
60
|
items: z.array(subBulletItemSchema).optional(),
|
|
61
|
+
/** Optional status icon shown in place of the default bullet marker ("ok" → ✓, "no" → ✕, "warn" → ⚠). */
|
|
62
|
+
icon: bulletIconSchema.optional(),
|
|
59
63
|
}),
|
|
60
64
|
]);
|
|
61
65
|
export const bulletsBlockSchema = z.object({
|
|
@@ -170,6 +174,8 @@ export const cardSchema = z.object({
|
|
|
170
174
|
footer: z.string().optional(),
|
|
171
175
|
label: z.string().optional(),
|
|
172
176
|
num: z.number().optional(),
|
|
177
|
+
/** Optional accent-colored typographic prefix before the card title (e.g. "01", "Ⅱ", "★"). */
|
|
178
|
+
numLabel: z.string().optional(),
|
|
173
179
|
icon: z.string().optional(),
|
|
174
180
|
});
|
|
175
181
|
// ═══════════════════════════════════════════════════════════
|
|
@@ -183,10 +189,18 @@ export const slideStyleSchema = z.object({
|
|
|
183
189
|
bgOpacity: z.number().optional(),
|
|
184
190
|
footer: z.string().optional(),
|
|
185
191
|
});
|
|
192
|
+
/** Optional kicker/category label rendered at the top of a slide (small uppercase pill). */
|
|
193
|
+
export const eyebrowSchema = z.object({
|
|
194
|
+
label: z.string(),
|
|
195
|
+
/** Color token (e.g. "primary", "amber", "success"). Falls back to slide.accentColor / theme primary when absent. */
|
|
196
|
+
color: accentColorKeySchema.optional(),
|
|
197
|
+
});
|
|
186
198
|
/** Common slide properties shared across all layouts */
|
|
187
199
|
const slideBaseFields = {
|
|
188
200
|
accentColor: accentColorKeySchema.optional(),
|
|
189
201
|
style: slideStyleSchema.optional(),
|
|
202
|
+
/** Optional eyebrow (small uppercase pill) shown at the top of the slide above the header/title. */
|
|
203
|
+
eyebrow: eyebrowSchema.optional(),
|
|
190
204
|
};
|
|
191
205
|
// ═══════════════════════════════════════════════════════════
|
|
192
206
|
// Layouts
|
|
@@ -199,6 +213,8 @@ export const titleSlideSchema = z.object({
|
|
|
199
213
|
subtitle: z.string().optional(),
|
|
200
214
|
author: z.string().optional(),
|
|
201
215
|
note: z.string().optional(),
|
|
216
|
+
/** Optional row of small pill badges shown below the subtitle / note (e.g. ["🚀 deploy or die", "⚡ weekly output"]). */
|
|
217
|
+
chips: z.array(z.string()).optional(),
|
|
202
218
|
});
|
|
203
219
|
// ─── columns ───
|
|
204
220
|
export const columnsSlideSchema = z.object({
|
|
@@ -261,6 +277,8 @@ export const statItemSchema = z.object({
|
|
|
261
277
|
label: z.string(),
|
|
262
278
|
color: accentColorKeySchema.optional(),
|
|
263
279
|
change: z.string().optional(),
|
|
280
|
+
/** Optional accent-colored typographic prefix shown above the value (e.g. "01"). */
|
|
281
|
+
numLabel: z.string().optional(),
|
|
264
282
|
});
|
|
265
283
|
export const statsSlideSchema = z.object({
|
|
266
284
|
layout: z.literal("stats"),
|
|
@@ -278,6 +296,8 @@ export const timelineItemSchema = z.object({
|
|
|
278
296
|
description: z.string().optional(),
|
|
279
297
|
color: accentColorKeySchema.optional(),
|
|
280
298
|
done: z.boolean().optional(),
|
|
299
|
+
/** Optional emphasis flag — when true the step is rendered with a stronger accent border and tinted background. */
|
|
300
|
+
hot: z.boolean().optional(),
|
|
281
301
|
});
|
|
282
302
|
export const timelineSlideSchema = z.object({
|
|
283
303
|
layout: z.literal("timeline"),
|
|
@@ -382,6 +402,29 @@ export const funnelSlideSchema = z.object({
|
|
|
382
402
|
stages: z.array(funnelStageSchema),
|
|
383
403
|
callout: calloutBarSchema.optional(),
|
|
384
404
|
});
|
|
405
|
+
// ─── manifesto ───
|
|
406
|
+
/** A single line in a manifesto / creed grid. */
|
|
407
|
+
export const manifestoLineSchema = z.object({
|
|
408
|
+
title: z.string(),
|
|
409
|
+
description: z.string().optional(),
|
|
410
|
+
/** Accent color for the line's left-border highlight. Falls back to slide.accentColor / "primary". */
|
|
411
|
+
accentColor: accentColorKeySchema.optional(),
|
|
412
|
+
});
|
|
413
|
+
/**
|
|
414
|
+
* Grid of short bordered cards, each with a colored left accent — useful for manifestos,
|
|
415
|
+
* principles, "what we believe" lists, etc.
|
|
416
|
+
*/
|
|
417
|
+
export const manifestoSlideSchema = z.object({
|
|
418
|
+
layout: z.literal("manifesto"),
|
|
419
|
+
...slideBaseFields,
|
|
420
|
+
title: z.string(),
|
|
421
|
+
stepLabel: z.string().optional(),
|
|
422
|
+
subtitle: z.string().optional(),
|
|
423
|
+
items: z.array(manifestoLineSchema),
|
|
424
|
+
/** Number of grid columns (default: 2). */
|
|
425
|
+
columns: z.number().int().min(1).max(4).optional(),
|
|
426
|
+
callout: calloutBarSchema.optional(),
|
|
427
|
+
});
|
|
385
428
|
// ═══════════════════════════════════════════════════════════
|
|
386
429
|
// Branding — logo & background image overlay
|
|
387
430
|
// ═══════════════════════════════════════════════════════════
|
|
@@ -429,6 +472,7 @@ export const slideLayoutSchema = z.discriminatedUnion("layout", [
|
|
|
429
472
|
tableSlideSchema,
|
|
430
473
|
funnelSlideSchema,
|
|
431
474
|
waterfallSlideSchema,
|
|
475
|
+
manifestoSlideSchema,
|
|
432
476
|
]);
|
|
433
477
|
/** Media schema registered in mulmoImageAssetSchema */
|
|
434
478
|
export const mulmoSlideMediaSchema = z
|
package/lib/utils.d.ts
CHANGED
|
@@ -47,19 +47,39 @@ export declare const renderCalloutBar: (obj: {
|
|
|
47
47
|
align?: string;
|
|
48
48
|
leftBar?: boolean;
|
|
49
49
|
}) => string;
|
|
50
|
+
/**
|
|
51
|
+
* Render an eyebrow pill (small uppercase letter-spaced category label).
|
|
52
|
+
* Renders nothing when `eyebrow` is undefined, so callers can pass through unconditionally.
|
|
53
|
+
*/
|
|
54
|
+
export declare const renderEyebrow: (eyebrow: {
|
|
55
|
+
label: string;
|
|
56
|
+
color?: string;
|
|
57
|
+
} | undefined, defaultColor?: string) => string;
|
|
58
|
+
/** Render a chip-row (small bordered pill badges, e.g. tags below a title). Empty / undefined input renders nothing. */
|
|
59
|
+
export declare const renderChipRow: (chips: string[] | undefined) => string;
|
|
60
|
+
/** Render an accent-colored typographic prefix (e.g. "01") to be placed before a card / stat title. */
|
|
61
|
+
export declare const renderNumLabel: (label: string | undefined, colorKey?: string) => string;
|
|
50
62
|
/** Render header text elements (stepLabel + title + subtitle) without wrapping div */
|
|
51
63
|
export declare const renderHeaderText: (data: {
|
|
52
64
|
accentColor?: string;
|
|
53
65
|
stepLabel?: string;
|
|
54
66
|
title: string;
|
|
55
67
|
subtitle?: string;
|
|
68
|
+
eyebrow?: {
|
|
69
|
+
label: string;
|
|
70
|
+
color?: string;
|
|
71
|
+
};
|
|
56
72
|
}) => string;
|
|
57
|
-
/** Render the common slide header (accent bar + title + subtitle) */
|
|
73
|
+
/** Render the common slide header (accent bar + title + subtitle, plus optional eyebrow pill) */
|
|
58
74
|
export declare const slideHeader: (data: {
|
|
59
75
|
accentColor?: string;
|
|
60
76
|
stepLabel?: string;
|
|
61
77
|
title: string;
|
|
62
78
|
subtitle?: string;
|
|
79
|
+
eyebrow?: {
|
|
80
|
+
label: string;
|
|
81
|
+
color?: string;
|
|
82
|
+
};
|
|
63
83
|
}) => string;
|
|
64
84
|
/** Render accent bar + vertically-centered wrapper with header text (used by stats, timeline) */
|
|
65
85
|
export declare const centeredSlideHeader: (data: {
|
|
@@ -67,6 +87,10 @@ export declare const centeredSlideHeader: (data: {
|
|
|
67
87
|
stepLabel?: string;
|
|
68
88
|
title: string;
|
|
69
89
|
subtitle?: string;
|
|
90
|
+
eyebrow?: {
|
|
91
|
+
label: string;
|
|
92
|
+
color?: string;
|
|
93
|
+
};
|
|
70
94
|
}) => string;
|
|
71
95
|
/** Generate a unique ID with the given prefix (e.g. "chart-0", "mermaid-1") */
|
|
72
96
|
export declare const generateSlideId: (prefix: string) => string;
|
package/lib/utils.js
CHANGED
|
@@ -155,10 +155,39 @@ export const renderCalloutBar = (obj) => {
|
|
|
155
155
|
<div class="px-4 py-3 text-sm font-body flex-1">${inner}</div>
|
|
156
156
|
</div>`;
|
|
157
157
|
};
|
|
158
|
+
/**
|
|
159
|
+
* Render an eyebrow pill (small uppercase letter-spaced category label).
|
|
160
|
+
* Renders nothing when `eyebrow` is undefined, so callers can pass through unconditionally.
|
|
161
|
+
*/
|
|
162
|
+
export const renderEyebrow = (eyebrow, defaultColor) => {
|
|
163
|
+
if (!eyebrow)
|
|
164
|
+
return "";
|
|
165
|
+
const color = c(eyebrow.color ?? defaultColor ?? "primary");
|
|
166
|
+
return `<span class="inline-flex items-center gap-2 font-accent font-extrabold uppercase tracking-[0.16em] text-[12px] px-3 py-1 rounded-full border border-d-textDim/30 bg-${color}/10 text-${color}">${renderInlineMarkup(eyebrow.label)}</span>`;
|
|
167
|
+
};
|
|
168
|
+
/** Render a chip-row (small bordered pill badges, e.g. tags below a title). Empty / undefined input renders nothing. */
|
|
169
|
+
export const renderChipRow = (chips) => {
|
|
170
|
+
if (!chips || chips.length === 0)
|
|
171
|
+
return "";
|
|
172
|
+
const items = chips
|
|
173
|
+
.map((label) => `<span class="text-sm px-3 py-1.5 rounded-full border border-d-textDim/30 bg-d-card/40 text-d-text">${renderInlineMarkup(label)}</span>`)
|
|
174
|
+
.join("");
|
|
175
|
+
return `<div class="flex gap-2 flex-wrap mt-4">${items}</div>`;
|
|
176
|
+
};
|
|
177
|
+
/** Render an accent-colored typographic prefix (e.g. "01") to be placed before a card / stat title. */
|
|
178
|
+
export const renderNumLabel = (label, colorKey) => {
|
|
179
|
+
if (!label)
|
|
180
|
+
return "";
|
|
181
|
+
const color = c(colorKey ?? "primary");
|
|
182
|
+
return `<span class="font-accent font-extrabold text-${color} mr-2">${renderInlineMarkup(label)}</span>`;
|
|
183
|
+
};
|
|
158
184
|
/** Render header text elements (stepLabel + title + subtitle) without wrapping div */
|
|
159
185
|
export const renderHeaderText = (data) => {
|
|
160
186
|
const accent = resolveAccent(data.accentColor);
|
|
161
187
|
const lines = [];
|
|
188
|
+
const eyebrowHtml = renderEyebrow(data.eyebrow, accent);
|
|
189
|
+
if (eyebrowHtml)
|
|
190
|
+
lines.push(`<div class="mb-2">${eyebrowHtml}</div>`);
|
|
162
191
|
if (data.stepLabel) {
|
|
163
192
|
lines.push(`<p class="text-sm font-bold text-${c(accent)} font-body">${renderInlineMarkup(data.stepLabel)}</p>`);
|
|
164
193
|
}
|
|
@@ -168,7 +197,7 @@ export const renderHeaderText = (data) => {
|
|
|
168
197
|
}
|
|
169
198
|
return lines.join("\n");
|
|
170
199
|
};
|
|
171
|
-
/** Render the common slide header (accent bar + title + subtitle) */
|
|
200
|
+
/** Render the common slide header (accent bar + title + subtitle, plus optional eyebrow pill) */
|
|
172
201
|
export const slideHeader = (data) => {
|
|
173
202
|
const accent = resolveAccent(data.accentColor);
|
|
174
203
|
return [accentBar(accent), `<div class="px-12 pt-5 shrink-0">`, renderHeaderText(data), `</div>`].join("\n");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mulmocast/deck",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "MulmoCast deck DSL: JSON-described semantic slide layouts (stats, comparison, timeline, ...) rendered to Tailwind-based HTML",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"types": "lib/index.d.ts",
|