mulmocast 2.2.4 ā 2.2.5
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/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/utils.d.ts +27 -1
- package/lib/slide/utils.js +38 -10
- package/package.json +1 -1
|
@@ -2,7 +2,7 @@ export const builder = (yargs) => yargs
|
|
|
2
2
|
.positional("category", {
|
|
3
3
|
describe: "Category to show info for",
|
|
4
4
|
type: "string",
|
|
5
|
-
choices: ["styles", "bgm", "templates", "voices", "images", "movies", "llm"],
|
|
5
|
+
choices: ["styles", "bgm", "templates", "voices", "images", "movies", "llm", "themes"],
|
|
6
6
|
})
|
|
7
7
|
.option("format", {
|
|
8
8
|
alias: "F",
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { getMarkdownStyleNames, getMarkdownCategories, getMarkdownStylesByCategory } from "../../../../data/markdownStyles.js";
|
|
3
3
|
import { bgmAssets } from "../../../../data/bgmAssets.js";
|
|
4
4
|
import { templateDataSet } from "../../../../data/templateDataSet.js";
|
|
5
|
+
import { slideThemes } from "../../../../data/slideThemes.js";
|
|
5
6
|
import { provider2TTSAgent, provider2ImageAgent, provider2MovieAgent, provider2LLMAgent } from "../../../../types/provider2agent.js";
|
|
6
7
|
import YAML from "yaml";
|
|
7
8
|
const formatOutput = (data, format) => {
|
|
@@ -78,6 +79,9 @@ const getLlmInfo = () => {
|
|
|
78
79
|
}
|
|
79
80
|
return { llmProviders: result };
|
|
80
81
|
};
|
|
82
|
+
const getThemesInfo = () => {
|
|
83
|
+
return { themes: slideThemes, total: Object.keys(slideThemes).length };
|
|
84
|
+
};
|
|
81
85
|
const printStylesText = () => {
|
|
82
86
|
const categories = getMarkdownCategories();
|
|
83
87
|
console.log("\nš Markdown Styles (100 styles in 10 categories)\n");
|
|
@@ -155,6 +159,17 @@ const printLlmText = () => {
|
|
|
155
159
|
console.log(` Models: ${config.models.join(", ")}\n`);
|
|
156
160
|
}
|
|
157
161
|
};
|
|
162
|
+
const printThemesText = () => {
|
|
163
|
+
const themeEntries = Object.entries(slideThemes);
|
|
164
|
+
console.log(`\nšØ Slide Themes (${themeEntries.length} themes)\n`);
|
|
165
|
+
console.log("Usage: Set 'slideParams.theme' in your script\n");
|
|
166
|
+
for (const [name, theme] of themeEntries) {
|
|
167
|
+
const { bg, primary, accent } = theme.colors;
|
|
168
|
+
const { title, body, mono } = theme.fonts;
|
|
169
|
+
console.log(` ${name.padEnd(10)} - bg: ${bg}, primary: ${primary}, accent: ${accent} | fonts: ${title}/${body}/${mono}`);
|
|
170
|
+
}
|
|
171
|
+
console.log("");
|
|
172
|
+
};
|
|
158
173
|
const printAllCategories = () => {
|
|
159
174
|
console.log("\nš Available Info Categories\n");
|
|
160
175
|
console.log(" Usage: mulmo tool info <category> [--format json|yaml]\n");
|
|
@@ -165,9 +180,10 @@ const printAllCategories = () => {
|
|
|
165
180
|
console.log(" voices - TTS providers and voice options");
|
|
166
181
|
console.log(" images - Image generation providers and models");
|
|
167
182
|
console.log(" movies - Movie generation providers and models");
|
|
168
|
-
console.log(" llm - LLM providers and models
|
|
183
|
+
console.log(" llm - LLM providers and models");
|
|
184
|
+
console.log(" themes - Slide themes and color palettes\n");
|
|
169
185
|
};
|
|
170
|
-
const validCategories = ["styles", "bgm", "templates", "voices", "images", "movies", "llm"];
|
|
186
|
+
const validCategories = ["styles", "bgm", "templates", "voices", "images", "movies", "llm", "themes"];
|
|
171
187
|
const isValidCategory = (category) => {
|
|
172
188
|
return validCategories.includes(category);
|
|
173
189
|
};
|
|
@@ -199,6 +215,7 @@ export const handler = (argv) => {
|
|
|
199
215
|
images: getImagesInfo,
|
|
200
216
|
movies: getMoviesInfo,
|
|
201
217
|
llm: getLlmInfo,
|
|
218
|
+
themes: getThemesInfo,
|
|
202
219
|
};
|
|
203
220
|
const textPrinters = {
|
|
204
221
|
styles: printStylesText,
|
|
@@ -208,6 +225,7 @@ export const handler = (argv) => {
|
|
|
208
225
|
images: printImagesText,
|
|
209
226
|
movies: printMoviesText,
|
|
210
227
|
llm: printLlmText,
|
|
228
|
+
themes: printThemesText,
|
|
211
229
|
};
|
|
212
230
|
if (format === "text") {
|
|
213
231
|
textPrinters[category]();
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export declare const command = "info [category]";
|
|
2
|
-
export declare const desc = "Show available options (styles, bgm, templates, voices, images, movies, llm)";
|
|
2
|
+
export declare const desc = "Show available options (styles, bgm, templates, voices, images, movies, llm, themes)";
|
|
3
3
|
export { builder } from "./builder.js";
|
|
4
4
|
export { handler } from "./handler.js";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export const command = "info [category]";
|
|
2
|
-
export const desc = "Show available options (styles, bgm, templates, voices, images, movies, llm)";
|
|
2
|
+
export const desc = "Show available options (styles, bgm, templates, voices, images, movies, llm, themes)";
|
|
3
3
|
export { builder } from "./builder.js";
|
|
4
4
|
export { handler } from "./handler.js";
|
package/lib/slide/blocks.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { escapeHtml, c, generateSlideId, renderInlineMarkup } from "./utils.js";
|
|
1
|
+
import { escapeHtml, c, generateSlideId, renderInlineMarkup, blockTitle, resolveChangeColor, resolveAccent } from "./utils.js";
|
|
2
2
|
// āāā Table cell rendering (shared with layouts/table.ts) āāā
|
|
3
3
|
export const resolveCellColor = (cellObj, isRowHeader) => {
|
|
4
4
|
if (cellObj.color)
|
|
@@ -47,8 +47,7 @@ export const renderTableCore = (headers, rows, rowHeaders, striped) => {
|
|
|
47
47
|
return parts.join("\n");
|
|
48
48
|
};
|
|
49
49
|
const renderTableBlock = (block) => {
|
|
50
|
-
|
|
51
|
-
return `<div class="overflow-auto">${titleHtml}${renderTableCore(block.headers, block.rows, block.rowHeaders, block.striped)}</div>`;
|
|
50
|
+
return `<div class="overflow-auto">${blockTitle(block.title)}${renderTableCore(block.headers, block.rows, block.rowHeaders, block.striped)}</div>`;
|
|
52
51
|
};
|
|
53
52
|
/** Render a single content block to HTML */
|
|
54
53
|
export const renderContentBlock = (block) => {
|
|
@@ -167,11 +166,10 @@ const renderCallout = (block) => {
|
|
|
167
166
|
const renderMetric = (block) => {
|
|
168
167
|
const lines = [];
|
|
169
168
|
lines.push(`<div class="text-center">`);
|
|
170
|
-
lines.push(` <p class="text-4xl font-bold text-${c(block.color
|
|
169
|
+
lines.push(` <p class="text-4xl font-bold text-${c(resolveAccent(block.color))}">${renderInlineMarkup(block.value)}</p>`);
|
|
171
170
|
lines.push(` <p class="text-sm text-d-dim mt-1">${renderInlineMarkup(block.label)}</p>`);
|
|
172
171
|
if (block.change) {
|
|
173
|
-
|
|
174
|
-
lines.push(` <p class="text-sm font-bold text-${c(changeColor)} mt-1">${escapeHtml(block.change)}</p>`);
|
|
172
|
+
lines.push(` <p class="text-sm font-bold text-${c(resolveChangeColor(block.change))} mt-1">${escapeHtml(block.change)}</p>`);
|
|
175
173
|
}
|
|
176
174
|
lines.push(`</div>`);
|
|
177
175
|
return lines.join("\n");
|
|
@@ -191,9 +189,8 @@ const renderImageRefPlaceholder = (block) => {
|
|
|
191
189
|
const renderChart = (block) => {
|
|
192
190
|
const chartId = generateSlideId("chart");
|
|
193
191
|
const chartData = JSON.stringify(block.chartData);
|
|
194
|
-
const titleHtml = block.title ? `<p class="text-sm font-bold text-d-text font-body mb-2">${renderInlineMarkup(block.title)}</p>` : "";
|
|
195
192
|
return `<div class="flex-1 min-h-0 flex flex-col">
|
|
196
|
-
${
|
|
193
|
+
${blockTitle(block.title)}
|
|
197
194
|
<div class="flex-1 min-h-0 relative">
|
|
198
195
|
<canvas id="${chartId}" data-chart-ready="false"></canvas>
|
|
199
196
|
</div>
|
|
@@ -211,46 +208,42 @@ const renderChart = (block) => {
|
|
|
211
208
|
};
|
|
212
209
|
const renderMermaid = (block) => {
|
|
213
210
|
const mermaidId = generateSlideId("mermaid");
|
|
214
|
-
const titleHtml = block.title ? `<p class="text-sm font-bold text-d-text font-body mb-2">${renderInlineMarkup(block.title)}</p>` : "";
|
|
215
211
|
return `<div class="flex-1 min-h-0 flex flex-col">
|
|
216
|
-
${
|
|
212
|
+
${blockTitle(block.title)}
|
|
217
213
|
<div class="flex-1 min-h-0 flex justify-center items-center">
|
|
218
214
|
<div id="${mermaidId}" class="mermaid">${escapeHtml(block.code)}</div>
|
|
219
215
|
</div>
|
|
220
216
|
</div>`;
|
|
221
217
|
};
|
|
218
|
+
/** Render the text + content blocks inside a section (shared by sidebar/default variants) */
|
|
219
|
+
const renderSectionContent = (block) => {
|
|
220
|
+
const parts = [];
|
|
221
|
+
if (block.text) {
|
|
222
|
+
parts.push(`<p class="text-[15px] text-d-muted font-body">${renderInlineMarkup(block.text)}</p>`);
|
|
223
|
+
}
|
|
224
|
+
if (block.content) {
|
|
225
|
+
parts.push(block.content.map(renderContentBlock).join("\n"));
|
|
226
|
+
}
|
|
227
|
+
return parts.join("\n");
|
|
228
|
+
};
|
|
222
229
|
const renderSectionSidebar = (block) => {
|
|
223
|
-
const color = block.color
|
|
230
|
+
const color = resolveAccent(block.color);
|
|
224
231
|
const chars = block.label
|
|
225
232
|
.split("")
|
|
226
233
|
.map((ch) => escapeHtml(ch))
|
|
227
234
|
.join("<br>");
|
|
228
235
|
const sidebar = `<div class="w-[48px] shrink-0 rounded-l bg-${c(color)} flex items-center justify-center"><span class="text-sm font-bold text-white font-body leading-snug text-center">${chars}</span></div>`;
|
|
229
|
-
const contentParts = [];
|
|
230
|
-
if (block.text) {
|
|
231
|
-
contentParts.push(`<p class="text-[15px] text-d-muted font-body">${renderInlineMarkup(block.text)}</p>`);
|
|
232
|
-
}
|
|
233
|
-
if (block.content) {
|
|
234
|
-
contentParts.push(block.content.map(renderContentBlock).join("\n"));
|
|
235
|
-
}
|
|
236
236
|
return `<div class="flex rounded overflow-hidden bg-d-card">
|
|
237
237
|
${sidebar}
|
|
238
|
-
<div class="flex-1 space-y-2 p-3">${
|
|
238
|
+
<div class="flex-1 space-y-2 p-3">${renderSectionContent(block)}</div>
|
|
239
239
|
</div>`;
|
|
240
240
|
};
|
|
241
241
|
const renderSectionDefault = (block) => {
|
|
242
|
-
const color = block.color
|
|
242
|
+
const color = resolveAccent(block.color);
|
|
243
243
|
const badge = `<span class="min-w-[80px] px-3 py-1 rounded text-sm font-bold text-white bg-${c(color)} shrink-0">${renderInlineMarkup(block.label)}</span>`;
|
|
244
|
-
const contentParts = [];
|
|
245
|
-
if (block.text) {
|
|
246
|
-
contentParts.push(`<p class="text-[15px] text-d-muted font-body">${renderInlineMarkup(block.text)}</p>`);
|
|
247
|
-
}
|
|
248
|
-
if (block.content) {
|
|
249
|
-
contentParts.push(block.content.map(renderContentBlock).join("\n"));
|
|
250
|
-
}
|
|
251
244
|
return `<div class="flex gap-4 items-start">
|
|
252
245
|
${badge}
|
|
253
|
-
<div class="flex-1 space-y-2">${
|
|
246
|
+
<div class="flex-1 space-y-2">${renderSectionContent(block)}</div>
|
|
254
247
|
</div>`;
|
|
255
248
|
};
|
|
256
249
|
const renderSection = (block) => {
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import { renderInlineMarkup,
|
|
1
|
+
import { renderInlineMarkup, accentBar, resolveAccent } from "../utils.js";
|
|
2
2
|
export const layoutBigQuote = (data) => {
|
|
3
|
-
const accent = data.accentColor
|
|
3
|
+
const accent = resolveAccent(data.accentColor);
|
|
4
4
|
const parts = [];
|
|
5
5
|
parts.push(`<div class="flex flex-col items-center justify-center h-full px-20">`);
|
|
6
|
-
parts.push(`
|
|
6
|
+
parts.push(` ${accentBar(accent, "w-24 mb-8")}`);
|
|
7
7
|
parts.push(` <blockquote class="text-[32px] text-d-text font-title italic text-center leading-relaxed">`);
|
|
8
8
|
parts.push(` “${renderInlineMarkup(data.quote)}”`);
|
|
9
9
|
parts.push(` </blockquote>`);
|
|
10
|
-
parts.push(`
|
|
10
|
+
parts.push(` ${accentBar(accent, "w-24 mt-8 mb-6")}`);
|
|
11
11
|
if (data.author) {
|
|
12
12
|
parts.push(` <p class="text-lg text-d-muted font-body">${renderInlineMarkup(data.author)}</p>`);
|
|
13
13
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { renderInlineMarkup, c, cardWrap, numBadge, iconSquare, slideHeader,
|
|
1
|
+
import { renderInlineMarkup, c, cardWrap, numBadge, iconSquare, slideHeader, renderOptionalCallout, resolveAccent } from "../utils.js";
|
|
2
2
|
import { renderCardContentBlocks } from "../blocks.js";
|
|
3
3
|
const buildColumnCard = (col) => {
|
|
4
|
-
const accent = col.accentColor
|
|
4
|
+
const accent = resolveAccent(col.accentColor);
|
|
5
5
|
const inner = [];
|
|
6
6
|
if (col.icon) {
|
|
7
7
|
inner.push(`<div class="flex flex-col items-center mb-3">`);
|
|
@@ -45,9 +45,7 @@ export const layoutColumns = (data) => {
|
|
|
45
45
|
parts.push(`<div class="flex gap-4 px-12 mt-5 flex-1 min-h-0 items-start">`);
|
|
46
46
|
parts.push(colElements.join("\n"));
|
|
47
47
|
parts.push(`</div>`);
|
|
48
|
-
|
|
49
|
-
parts.push(`<div class="mt-auto pb-4">${renderCalloutBar(data.callout)}</div>`);
|
|
50
|
-
}
|
|
48
|
+
parts.push(renderOptionalCallout(data.callout));
|
|
51
49
|
if (data.bottomText) {
|
|
52
50
|
parts.push(`<p class="text-center text-sm text-d-dim font-body pb-4">${renderInlineMarkup(data.bottomText)}</p>`);
|
|
53
51
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { renderInlineMarkup, c, cardWrap, slideHeader,
|
|
1
|
+
import { renderInlineMarkup, c, cardWrap, slideHeader, renderOptionalCallout, resolveAccent } from "../utils.js";
|
|
2
2
|
import { renderContentBlocks } from "../blocks.js";
|
|
3
3
|
const buildPanel = (panel) => {
|
|
4
|
-
const accent = panel.accentColor
|
|
4
|
+
const accent = resolveAccent(panel.accentColor);
|
|
5
5
|
const inner = [];
|
|
6
6
|
inner.push(`<h3 class="text-xl font-bold text-${c(accent)} font-body">${renderInlineMarkup(panel.title)}</h3>`);
|
|
7
7
|
if (panel.content) {
|
|
@@ -20,8 +20,6 @@ export const layoutComparison = (data) => {
|
|
|
20
20
|
parts.push(buildPanel(data.left));
|
|
21
21
|
parts.push(buildPanel(data.right));
|
|
22
22
|
parts.push(`</div>`);
|
|
23
|
-
|
|
24
|
-
parts.push(`<div class="mt-auto pb-4">${renderCalloutBar(data.callout)}</div>`);
|
|
25
|
-
}
|
|
23
|
+
parts.push(renderOptionalCallout(data.callout));
|
|
26
24
|
return parts.join("\n");
|
|
27
25
|
};
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { renderInlineMarkup, c, slideHeader,
|
|
1
|
+
import { renderInlineMarkup, c, slideHeader, renderOptionalCallout, resolveItemColor } from "../utils.js";
|
|
2
2
|
export const layoutFunnel = (data) => {
|
|
3
3
|
const parts = [slideHeader(data)];
|
|
4
4
|
const stages = data.stages || [];
|
|
5
5
|
const total = stages.length;
|
|
6
6
|
parts.push(`<div class="flex flex-col items-center gap-2 px-12 mt-6 flex-1">`);
|
|
7
7
|
stages.forEach((stage, i) => {
|
|
8
|
-
const color = stage.color
|
|
8
|
+
const color = resolveItemColor(stage.color, data.accentColor);
|
|
9
9
|
const widthPct = 100 - (i / Math.max(total - 1, 1)) * 55;
|
|
10
10
|
parts.push(`<div class="bg-${c(color)} rounded-lg flex items-center justify-between px-6 py-4" style="width: ${widthPct}%">`);
|
|
11
11
|
parts.push(` <div class="flex items-center gap-3">`);
|
|
@@ -20,8 +20,6 @@ export const layoutFunnel = (data) => {
|
|
|
20
20
|
parts.push(`</div>`);
|
|
21
21
|
});
|
|
22
22
|
parts.push(`</div>`);
|
|
23
|
-
|
|
24
|
-
parts.push(`<div class="mt-auto pb-4">${renderCalloutBar(data.callout)}</div>`);
|
|
25
|
-
}
|
|
23
|
+
parts.push(renderOptionalCallout(data.callout));
|
|
26
24
|
return parts.join("\n");
|
|
27
25
|
};
|
|
@@ -1,16 +1,11 @@
|
|
|
1
|
-
import { renderInlineMarkup,
|
|
1
|
+
import { renderInlineMarkup, cardWrap, numBadge, iconSquare, slideHeader, resolveAccent } from "../utils.js";
|
|
2
2
|
import { renderCardContentBlocks } from "../blocks.js";
|
|
3
3
|
export const layoutGrid = (data) => {
|
|
4
|
-
const accent = data.accentColor || "primary";
|
|
5
4
|
const nCols = data.gridColumns || 3;
|
|
6
|
-
const parts = [];
|
|
7
|
-
parts.push(`<div class="h-[3px] bg-${c(accent)} shrink-0"></div>`);
|
|
8
|
-
parts.push(`<div class="px-12 pt-5 shrink-0">`);
|
|
9
|
-
parts.push(` <h2 class="text-[42px] leading-tight font-title font-bold text-d-text">${renderInlineMarkup(data.title)}</h2>`);
|
|
10
|
-
parts.push(`</div>`);
|
|
5
|
+
const parts = [slideHeader(data)];
|
|
11
6
|
parts.push(`<div class="grid grid-cols-${nCols} gap-4 px-12 mt-5 flex-1 min-h-0 overflow-hidden content-center">`);
|
|
12
7
|
(data.items || []).forEach((item) => {
|
|
13
|
-
const itemAccent = item.accentColor
|
|
8
|
+
const itemAccent = resolveAccent(item.accentColor);
|
|
14
9
|
const inner = [];
|
|
15
10
|
if (item.icon) {
|
|
16
11
|
inner.push(`<div class="flex flex-col items-center mb-2">`);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { renderInlineMarkup, c, cardWrap, slideHeader } from "../utils.js";
|
|
1
|
+
import { renderInlineMarkup, c, cardWrap, slideHeader, resolveAccent } from "../utils.js";
|
|
2
2
|
import { renderContentBlocks } from "../blocks.js";
|
|
3
3
|
export const layoutMatrix = (data) => {
|
|
4
4
|
const parts = [slideHeader(data)];
|
|
@@ -21,7 +21,7 @@ export const layoutMatrix = (data) => {
|
|
|
21
21
|
Array.from({ length: cols }).forEach((_col, ci) => {
|
|
22
22
|
const idx = r * cols + ci;
|
|
23
23
|
const cell = cells[idx] || { label: "" };
|
|
24
|
-
const accent = cell.accentColor
|
|
24
|
+
const accent = resolveAccent(cell.accentColor);
|
|
25
25
|
const inner = [];
|
|
26
26
|
inner.push(`<h3 class="text-lg font-bold text-${c(accent)} font-body">${renderInlineMarkup(cell.label)}</h3>`);
|
|
27
27
|
if (cell.items) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { renderInlineMarkup, c } from "../utils.js";
|
|
1
|
+
import { renderInlineMarkup, c, accentBar, resolveAccent } from "../utils.js";
|
|
2
2
|
import { renderContentBlocks } from "../blocks.js";
|
|
3
3
|
const resolveValign = (valign) => {
|
|
4
4
|
if (valign === "top")
|
|
@@ -34,9 +34,9 @@ const buildSplitPanel = (panel, fallbackAccent, ratio) => {
|
|
|
34
34
|
return lines.join("\n");
|
|
35
35
|
};
|
|
36
36
|
export const layoutSplit = (data) => {
|
|
37
|
-
const accent = data.accentColor
|
|
37
|
+
const accent = resolveAccent(data.accentColor);
|
|
38
38
|
const parts = [];
|
|
39
|
-
parts.push(
|
|
39
|
+
parts.push(accentBar(accent));
|
|
40
40
|
const leftRatio = data.left?.ratio || 50;
|
|
41
41
|
const rightRatio = data.right?.ratio || 50;
|
|
42
42
|
parts.push(`<div class="flex h-full">`);
|
|
@@ -1,36 +1,23 @@
|
|
|
1
|
-
import { renderInlineMarkup, c,
|
|
1
|
+
import { renderInlineMarkup, c, resolveItemColor, resolveChangeColor, centeredSlideHeader, renderOptionalCallout } from "../utils.js";
|
|
2
2
|
export const layoutStats = (data) => {
|
|
3
|
-
const accent = data.accentColor || "primary";
|
|
4
3
|
const stats = data.stats || [];
|
|
5
4
|
const parts = [];
|
|
6
|
-
parts.push(
|
|
7
|
-
parts.push(`<div class="flex-1 flex flex-col justify-center px-12 min-h-0">`);
|
|
8
|
-
// Header inside centering wrapper
|
|
9
|
-
if (data.stepLabel) {
|
|
10
|
-
parts.push(`<p class="text-sm font-bold text-${c(accent)} font-body">${renderInlineMarkup(data.stepLabel)}</p>`);
|
|
11
|
-
}
|
|
12
|
-
parts.push(`<h2 class="text-[42px] leading-tight font-title font-bold text-d-text">${renderInlineMarkup(data.title)}</h2>`);
|
|
13
|
-
if (data.subtitle) {
|
|
14
|
-
parts.push(`<p class="text-[15px] text-d-dim mt-2 font-body">${renderInlineMarkup(data.subtitle)}</p>`);
|
|
15
|
-
}
|
|
5
|
+
parts.push(centeredSlideHeader(data));
|
|
16
6
|
// Stats cards
|
|
17
7
|
parts.push(`<div class="flex gap-6 mt-10">`);
|
|
18
8
|
stats.forEach((stat) => {
|
|
19
|
-
const color = stat.color
|
|
9
|
+
const color = resolveItemColor(stat.color, data.accentColor);
|
|
20
10
|
parts.push(`<div class="flex-1 bg-d-card rounded-lg shadow-lg p-10 text-center">`);
|
|
21
11
|
parts.push(` <div class="h-[3px] bg-${c(color)} rounded-full w-12 mx-auto mb-6"></div>`);
|
|
22
12
|
parts.push(` <p class="text-[52px] font-bold text-${c(color)} font-body leading-none">${renderInlineMarkup(stat.value)}</p>`);
|
|
23
13
|
parts.push(` <p class="text-lg text-d-muted font-body mt-4">${renderInlineMarkup(stat.label)}</p>`);
|
|
24
14
|
if (stat.change) {
|
|
25
|
-
|
|
26
|
-
parts.push(` <p class="text-base font-bold text-${c(changeColor)} font-body mt-3">${renderInlineMarkup(stat.change)}</p>`);
|
|
15
|
+
parts.push(` <p class="text-base font-bold text-${c(resolveChangeColor(stat.change))} font-body mt-3">${renderInlineMarkup(stat.change)}</p>`);
|
|
27
16
|
}
|
|
28
17
|
parts.push(`</div>`);
|
|
29
18
|
});
|
|
30
19
|
parts.push(`</div>`);
|
|
31
20
|
parts.push(`</div>`);
|
|
32
|
-
|
|
33
|
-
parts.push(`<div class="mt-auto pb-4">${renderCalloutBar(data.callout)}</div>`);
|
|
34
|
-
}
|
|
21
|
+
parts.push(renderOptionalCallout(data.callout));
|
|
35
22
|
return parts.join("\n");
|
|
36
23
|
};
|
|
@@ -1,12 +1,10 @@
|
|
|
1
|
-
import { slideHeader,
|
|
1
|
+
import { slideHeader, renderOptionalCallout } from "../utils.js";
|
|
2
2
|
import { renderTableCore } from "../blocks.js";
|
|
3
3
|
export const layoutTable = (data) => {
|
|
4
4
|
const parts = [slideHeader(data)];
|
|
5
5
|
parts.push(`<div class="px-12 mt-5 flex-1 overflow-auto">`);
|
|
6
6
|
parts.push(renderTableCore(data.headers, data.rows, data.rowHeaders, data.striped));
|
|
7
7
|
parts.push(`</div>`);
|
|
8
|
-
|
|
9
|
-
parts.push(`<div class="mt-auto pb-4">${renderCalloutBar(data.callout)}</div>`);
|
|
10
|
-
}
|
|
8
|
+
parts.push(renderOptionalCallout(data.callout));
|
|
11
9
|
return parts.join("\n");
|
|
12
10
|
};
|
|
@@ -1,23 +1,13 @@
|
|
|
1
|
-
import { renderInlineMarkup, c } from "../utils.js";
|
|
1
|
+
import { renderInlineMarkup, c, resolveItemColor, centeredSlideHeader } from "../utils.js";
|
|
2
2
|
export const layoutTimeline = (data) => {
|
|
3
|
-
const accent = data.accentColor || "primary";
|
|
4
3
|
const parts = [];
|
|
5
4
|
const items = data.items || [];
|
|
6
|
-
parts.push(
|
|
7
|
-
parts.push(`<div class="flex-1 flex flex-col justify-center px-12 min-h-0">`);
|
|
8
|
-
// Header inside centering wrapper
|
|
9
|
-
if (data.stepLabel) {
|
|
10
|
-
parts.push(`<p class="text-sm font-bold text-${c(accent)} font-body">${renderInlineMarkup(data.stepLabel)}</p>`);
|
|
11
|
-
}
|
|
12
|
-
parts.push(`<h2 class="text-[42px] leading-tight font-title font-bold text-d-text">${renderInlineMarkup(data.title)}</h2>`);
|
|
13
|
-
if (data.subtitle) {
|
|
14
|
-
parts.push(`<p class="text-[15px] text-d-dim mt-2 font-body">${renderInlineMarkup(data.subtitle)}</p>`);
|
|
15
|
-
}
|
|
5
|
+
parts.push(centeredSlideHeader(data));
|
|
16
6
|
// Timeline items
|
|
17
7
|
parts.push(`<div class="flex items-start mt-10 relative">`);
|
|
18
8
|
parts.push(`<div class="absolute left-4 right-4 top-[52px] h-[2px] bg-d-alt"></div>`);
|
|
19
9
|
items.forEach((item) => {
|
|
20
|
-
const color = item.color
|
|
10
|
+
const color = resolveItemColor(item.color, data.accentColor);
|
|
21
11
|
const dotBorder = item.done ? `bg-${c(color)}` : `bg-d-alt`;
|
|
22
12
|
const dotInner = item.done ? "bg-d-text" : `bg-${c(color)}`;
|
|
23
13
|
parts.push(`<div class="flex-1 flex flex-col items-center text-center relative z-10">`);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { renderInlineMarkup } from "../utils.js";
|
|
1
|
+
import { renderInlineMarkup, accentBar } from "../utils.js";
|
|
2
2
|
export const layoutTitle = (data) => {
|
|
3
3
|
return [
|
|
4
|
-
|
|
4
|
+
accentBar("primary"),
|
|
5
5
|
`<div class="absolute -top-20 -right-8 w-[360px] h-[360px] rounded-full bg-d-primary opacity-10"></div>`,
|
|
6
6
|
`<div class="absolute -bottom-12 -left-16 w-[280px] h-[280px] rounded-full bg-d-accent opacity-10"></div>`,
|
|
7
7
|
`<div class="flex flex-col justify-center h-full px-16 relative z-10">`,
|
|
@@ -12,7 +12,7 @@ export const layoutTitle = (data) => {
|
|
|
12
12
|
? ` <div class="bg-d-card px-4 py-2 mt-6 inline-block rounded"><p class="text-sm text-d-dim font-body">${renderInlineMarkup(data.note)}</p></div>`
|
|
13
13
|
: "",
|
|
14
14
|
`</div>`,
|
|
15
|
-
|
|
15
|
+
accentBar("accent", "absolute bottom-0 left-0 right-0"),
|
|
16
16
|
]
|
|
17
17
|
.filter(Boolean)
|
|
18
18
|
.join("\n");
|
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
|
// āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|