@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/blocks.js
CHANGED
|
@@ -131,14 +131,24 @@ const renderSubBullets = (item) => {
|
|
|
131
131
|
.join("\n");
|
|
132
132
|
return `\n${subs}`;
|
|
133
133
|
};
|
|
134
|
+
/** Map a per-item status icon variant to its glyph and color class segment. */
|
|
135
|
+
const STATUS_ICON_GLYPHS = {
|
|
136
|
+
ok: { glyph: "\u2713", color: "success" }, // \u2713
|
|
137
|
+
no: { glyph: "\u2715", color: "danger" }, // \u2715
|
|
138
|
+
warn: { glyph: "\u26a0", color: "warning" }, // \u26a0
|
|
139
|
+
};
|
|
134
140
|
const renderBullets = (block) => {
|
|
135
141
|
const tag = block.ordered ? "ol" : "ul";
|
|
136
142
|
const items = block.items
|
|
137
143
|
.map((item, i) => {
|
|
138
|
-
|
|
144
|
+
// Per-item status icon overrides the block-level marker / numbered prefix.
|
|
145
|
+
const statusIcon = typeof item === "object" && item.icon ? STATUS_ICON_GLYPHS[item.icon] : undefined;
|
|
146
|
+
const markerHtml = statusIcon
|
|
147
|
+
? `<span class="text-${c(statusIcon.color)} font-extrabold shrink-0">${statusIcon.glyph}</span>`
|
|
148
|
+
: `<span class="text-d-dim shrink-0">${block.ordered ? `${i + 1}.` : escapeHtml(block.icon || "\u2022")}</span>`;
|
|
139
149
|
const text = bulletItemText(item);
|
|
140
150
|
const subHtml = renderSubBullets(item);
|
|
141
|
-
return ` <li class="flex flex-col gap-1"><div class="flex gap-2"
|
|
151
|
+
return ` <li class="flex flex-col gap-1"><div class="flex gap-2">${markerHtml}<span>${renderInlineMarkup(text)}</span></div>${subHtml}</li>`;
|
|
142
152
|
})
|
|
143
153
|
.join("\n");
|
|
144
154
|
return `<${tag} class="space-y-2 text-[15px] text-d-muted font-body">\n${items}\n</${tag}>`;
|
package/lib/layouts/big_quote.js
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
|
-
import { renderInlineMarkup, accentBar, resolveAccent } from "../utils.js";
|
|
1
|
+
import { renderInlineMarkup, accentBar, resolveAccent, renderEyebrow } from "../utils.js";
|
|
2
2
|
export const layoutBigQuote = (data) => {
|
|
3
3
|
const accent = resolveAccent(data.accentColor);
|
|
4
|
+
const eyebrowHtml = renderEyebrow(data.eyebrow, accent);
|
|
4
5
|
const parts = [];
|
|
5
6
|
parts.push(`<div class="flex flex-col items-center justify-center h-full px-20">`);
|
|
7
|
+
if (eyebrowHtml)
|
|
8
|
+
parts.push(` <div class="mb-6">${eyebrowHtml}</div>`);
|
|
6
9
|
parts.push(` ${accentBar(accent, "w-24 mb-8")}`);
|
|
7
10
|
parts.push(` <blockquote class="text-[32px] text-d-text font-title italic text-center leading-relaxed">`);
|
|
8
11
|
parts.push(` “${renderInlineMarkup(data.quote)}”`);
|
package/lib/layouts/columns.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { renderInlineMarkup, c, cardWrap, numBadge, iconSquare, slideHeader, renderOptionalCallout, resolveAccent } from "../utils.js";
|
|
1
|
+
import { renderInlineMarkup, c, cardWrap, numBadge, iconSquare, slideHeader, renderOptionalCallout, resolveAccent, renderNumLabel } from "../utils.js";
|
|
2
2
|
import { renderCardContentBlocks } from "../blocks.js";
|
|
3
3
|
const buildColumnCard = (col) => {
|
|
4
4
|
const accent = resolveAccent(col.accentColor);
|
|
@@ -19,7 +19,8 @@ const buildColumnCard = (col) => {
|
|
|
19
19
|
if (col.label) {
|
|
20
20
|
inner.push(`<p class="text-sm font-bold text-${c(accent)} font-body">${renderInlineMarkup(col.label)}</p>`);
|
|
21
21
|
}
|
|
22
|
-
|
|
22
|
+
const numPrefix = renderNumLabel(col.numLabel, accent);
|
|
23
|
+
inner.push(`<h3 class="text-2xl font-title font-bold text-d-text mt-1">${numPrefix}${renderInlineMarkup(col.title)}</h3>`);
|
|
23
24
|
}
|
|
24
25
|
if (col.content) {
|
|
25
26
|
const centerCls = col.icon ? "text-center" : "";
|
package/lib/layouts/index.js
CHANGED
|
@@ -10,6 +10,7 @@ import { layoutMatrix } from "./matrix.js";
|
|
|
10
10
|
import { layoutTable } from "./table.js";
|
|
11
11
|
import { layoutFunnel } from "./funnel.js";
|
|
12
12
|
import { layoutWaterfall } from "./waterfall.js";
|
|
13
|
+
import { layoutManifesto } from "./manifesto.js";
|
|
13
14
|
import { escapeHtml } from "../utils.js";
|
|
14
15
|
/** Render the inner content of a slide (without the wrapper div) */
|
|
15
16
|
export const renderSlideContent = (slide) => {
|
|
@@ -38,6 +39,8 @@ export const renderSlideContent = (slide) => {
|
|
|
38
39
|
return layoutFunnel(slide);
|
|
39
40
|
case "waterfall":
|
|
40
41
|
return layoutWaterfall(slide);
|
|
42
|
+
case "manifesto":
|
|
43
|
+
return layoutManifesto(slide);
|
|
41
44
|
default: {
|
|
42
45
|
const _exhaustive = slide;
|
|
43
46
|
return `<p class="text-white p-8">Unknown layout: ${escapeHtml(String(_exhaustive.layout))}</p>`;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { renderInlineMarkup, c, slideHeader, renderOptionalCallout, resolveItemColor } from "../utils.js";
|
|
2
|
+
const buildManifestoCard = (line, slideAccent) => {
|
|
3
|
+
const color = resolveItemColor(line.accentColor, slideAccent);
|
|
4
|
+
const parts = [];
|
|
5
|
+
parts.push(`<div class="relative bg-d-card rounded-lg shadow-md overflow-hidden flex flex-col">`);
|
|
6
|
+
parts.push(` <div class="absolute left-0 top-0 bottom-0 w-1 bg-${c(color)}"></div>`);
|
|
7
|
+
parts.push(` <div class="px-5 py-4 pl-6 flex-1">`);
|
|
8
|
+
parts.push(` <h3 class="text-lg font-bold text-d-text font-body leading-snug">${renderInlineMarkup(line.title)}</h3>`);
|
|
9
|
+
if (line.description) {
|
|
10
|
+
parts.push(` <p class="text-sm text-d-muted font-body mt-1 leading-relaxed">${renderInlineMarkup(line.description)}</p>`);
|
|
11
|
+
}
|
|
12
|
+
parts.push(` </div>`);
|
|
13
|
+
parts.push(`</div>`);
|
|
14
|
+
return parts.join("\n");
|
|
15
|
+
};
|
|
16
|
+
const DEFAULT_COLUMNS = 2;
|
|
17
|
+
const MAX_COLUMNS = 4;
|
|
18
|
+
const gridColsClass = (columns) => {
|
|
19
|
+
// Tailwind requires literal class names — map explicitly.
|
|
20
|
+
const cls = {
|
|
21
|
+
1: "grid-cols-1",
|
|
22
|
+
2: "grid-cols-2",
|
|
23
|
+
3: "grid-cols-3",
|
|
24
|
+
4: "grid-cols-4",
|
|
25
|
+
};
|
|
26
|
+
return cls[columns] || cls[DEFAULT_COLUMNS];
|
|
27
|
+
};
|
|
28
|
+
export const layoutManifesto = (data) => {
|
|
29
|
+
const cols = Math.min(Math.max(data.columns ?? DEFAULT_COLUMNS, 1), MAX_COLUMNS);
|
|
30
|
+
const items = data.items || [];
|
|
31
|
+
const parts = [slideHeader(data)];
|
|
32
|
+
parts.push(`<div class="grid ${gridColsClass(cols)} gap-4 px-12 mt-5 flex-1 min-h-0 content-start">`);
|
|
33
|
+
items.forEach((line) => {
|
|
34
|
+
parts.push(buildManifestoCard(line, data.accentColor));
|
|
35
|
+
});
|
|
36
|
+
parts.push(`</div>`);
|
|
37
|
+
parts.push(renderOptionalCallout(data.callout));
|
|
38
|
+
return parts.join("\n");
|
|
39
|
+
};
|
package/lib/layouts/stats.js
CHANGED
|
@@ -9,6 +9,9 @@ export const layoutStats = (data) => {
|
|
|
9
9
|
const color = resolveItemColor(stat.color, data.accentColor);
|
|
10
10
|
parts.push(`<div class="flex-1 bg-d-card rounded-lg shadow-lg p-10 text-center">`);
|
|
11
11
|
parts.push(` <div class="h-[3px] bg-${c(color)} rounded-full w-12 mx-auto mb-6"></div>`);
|
|
12
|
+
if (stat.numLabel) {
|
|
13
|
+
parts.push(` <p class="font-accent font-extrabold text-${c(color)} text-sm tracking-wider mb-2">${renderInlineMarkup(stat.numLabel)}</p>`);
|
|
14
|
+
}
|
|
12
15
|
parts.push(` <p class="text-[52px] font-bold text-${c(color)} font-body leading-none">${renderInlineMarkup(stat.value)}</p>`);
|
|
13
16
|
parts.push(` <p class="text-lg text-d-muted font-body mt-4">${renderInlineMarkup(stat.label)}</p>`);
|
|
14
17
|
if (stat.change) {
|
package/lib/layouts/timeline.js
CHANGED
|
@@ -7,11 +7,14 @@ export const layoutTimeline = (data) => {
|
|
|
7
7
|
parts.push(`<div class="flex items-start mt-10 relative">`);
|
|
8
8
|
parts.push(`<div class="absolute left-4 right-4 top-[52px] h-[2px] bg-d-alt"></div>`);
|
|
9
9
|
items.forEach((item) => {
|
|
10
|
-
const
|
|
10
|
+
const baseColor = resolveItemColor(item.color, data.accentColor);
|
|
11
|
+
// 'hot' items are emphasized — keep the configured color when set, else fall back to warning (amber) for visibility.
|
|
12
|
+
const color = item.hot ? item.color || "warning" : baseColor;
|
|
11
13
|
const dotBorder = item.done ? `bg-${c(color)}` : `bg-d-alt`;
|
|
12
14
|
const dotInner = item.done ? "bg-d-text" : `bg-${c(color)}`;
|
|
15
|
+
const hotRing = item.hot ? ` ring-2 ring-${c(color)} ring-offset-2 ring-offset-d-bg` : "";
|
|
13
16
|
parts.push(`<div class="flex-1 flex flex-col items-center text-center relative z-10">`);
|
|
14
|
-
parts.push(` <div class="w-10 h-10 rounded-full ${dotBorder} flex items-center justify-center shadow-lg">`);
|
|
17
|
+
parts.push(` <div class="w-10 h-10 rounded-full ${dotBorder} flex items-center justify-center shadow-lg${hotRing}">`);
|
|
15
18
|
parts.push(` <div class="w-4 h-4 rounded-full ${dotInner}"></div>`);
|
|
16
19
|
parts.push(` </div>`);
|
|
17
20
|
parts.push(` <p class="text-sm font-bold text-${c(color)} font-body mt-4">${renderInlineMarkup(item.date)}</p>`);
|
package/lib/layouts/title.js
CHANGED
|
@@ -1,16 +1,21 @@
|
|
|
1
|
-
import { renderInlineMarkup, accentBar } from "../utils.js";
|
|
1
|
+
import { renderInlineMarkup, accentBar, renderEyebrow, renderChipRow, resolveAccent } from "../utils.js";
|
|
2
2
|
export const layoutTitle = (data) => {
|
|
3
|
+
const accent = resolveAccent(data.accentColor);
|
|
4
|
+
const eyebrowHtml = renderEyebrow(data.eyebrow, accent);
|
|
5
|
+
const chipsHtml = renderChipRow(data.chips);
|
|
3
6
|
return [
|
|
4
7
|
accentBar("primary"),
|
|
5
8
|
`<div class="absolute -top-20 -right-8 w-[360px] h-[360px] rounded-full bg-d-primary opacity-10"></div>`,
|
|
6
9
|
`<div class="absolute -bottom-12 -left-16 w-[280px] h-[280px] rounded-full bg-d-accent opacity-10"></div>`,
|
|
7
10
|
`<div class="flex flex-col justify-center h-full px-16 relative z-10">`,
|
|
11
|
+
eyebrowHtml ? ` <div class="mb-4">${eyebrowHtml}</div>` : "",
|
|
8
12
|
` <h1 class="text-[60px] leading-tight font-title font-bold text-d-text">${renderInlineMarkup(data.title)}</h1>`,
|
|
9
13
|
data.subtitle ? ` <p class="text-2xl text-d-muted mt-6 font-body">${renderInlineMarkup(data.subtitle)}</p>` : "",
|
|
10
14
|
data.author ? ` <p class="text-lg text-d-dim mt-10 font-body">${renderInlineMarkup(data.author)}</p>` : "",
|
|
11
15
|
data.note
|
|
12
16
|
? ` <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
17
|
: "",
|
|
18
|
+
chipsHtml,
|
|
14
19
|
`</div>`,
|
|
15
20
|
accentBar("accent", "absolute bottom-0 left-0 right-0"),
|
|
16
21
|
]
|