wand-decks-kit 0.8.1 → 0.8.2
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/SKILL.md +11 -0
- package/build.js +5 -1
- package/lib/schema.js +15 -3
- package/package.json +1 -1
- package/wand_kit.js +17 -12
package/SKILL.md
CHANGED
|
@@ -99,6 +99,17 @@ Decks built here carry their own spec inside the file. Change only what was
|
|
|
99
99
|
asked, then `ship`. If the deck carries no spec it was not built here — treat it
|
|
100
100
|
as a rebrand.
|
|
101
101
|
|
|
102
|
+
## Work in fast loops, ship once
|
|
103
|
+
|
|
104
|
+
`node wand.js validate` and `node wand.js check` run in seconds; `ship` runs the
|
|
105
|
+
whole pipeline. Iterate spec edits against the first two, and run `ship` when
|
|
106
|
+
they are clean — never as your discovery tool.
|
|
107
|
+
|
|
108
|
+
When one error reveals a pattern — a field on the wrong layout, a missing
|
|
109
|
+
optional — **sweep the entire spec for that pattern before re-running anything**.
|
|
110
|
+
Paying a full cycle per instance is the slow way to learn the same fact
|
|
111
|
+
repeatedly.
|
|
112
|
+
|
|
102
113
|
## Rules that decide whether the output is usable
|
|
103
114
|
|
|
104
115
|
1. **Faithful means verbatim.** No consolidating slides, no paraphrasing to
|
package/build.js
CHANGED
|
@@ -129,7 +129,11 @@ function check(spec) {
|
|
|
129
129
|
(bad ? ` — ${bad} overflow${bad > 1 ? "s" : ""} above` : " — clean"));
|
|
130
130
|
console.log(`bounds check: ${oob ? oob + " shape(s) outside the content band — listed above" : "clean"}`);
|
|
131
131
|
console.log(`asset check : ${missing ? missing + " missing — listed above" : `${seen.size} resolved`}`);
|
|
132
|
-
|
|
132
|
+
// A slide whose layout threw has no shapes, so nothing above can fail for
|
|
133
|
+
// it — a crash used to print an ERROR line and exit 0, and the spec author
|
|
134
|
+
// paid a full ship cycle to find out.
|
|
135
|
+
if (t.errors.length) console.log(`layout check: ${t.errors.length} slide(s) threw — listed above`);
|
|
136
|
+
return bad + oob + missing + t.errors.length;
|
|
133
137
|
}
|
|
134
138
|
|
|
135
139
|
/** The same trace, as a PDF. Fonts embedded, no LibreOffice, no conversion. */
|
package/lib/schema.js
CHANGED
|
@@ -32,7 +32,7 @@ const COMMON = {
|
|
|
32
32
|
const L = {
|
|
33
33
|
/* ------------------------------------------------------------ structural */
|
|
34
34
|
cover: {
|
|
35
|
-
use: "Slide 1.
|
|
35
|
+
use: "Slide 1. Always dark, full bleed, no footer. The lede goes in `body` — there is no subtitleText here",
|
|
36
36
|
bleed: true,
|
|
37
37
|
fields: {
|
|
38
38
|
eyebrow: str(), body: str(), kickerPlain: str(), kickerBold: str(),
|
|
@@ -40,12 +40,12 @@ const L = {
|
|
|
40
40
|
},
|
|
41
41
|
},
|
|
42
42
|
divider: {
|
|
43
|
-
use: "Section breaks
|
|
43
|
+
use: "Section breaks. Always dark, keeps the tag, drops the footer. Prose goes in `body`",
|
|
44
44
|
bleed: true,
|
|
45
45
|
fields: { eyebrow: str(), titleText: s("title", req()), body: str() },
|
|
46
46
|
},
|
|
47
47
|
closing: {
|
|
48
|
-
use: "Last slide.
|
|
48
|
+
use: "Last slide. Always dark, centred, full bleed",
|
|
49
49
|
bleed: true,
|
|
50
50
|
fields: { eyebrow: str(), line1: str(req()), line2: str(), body: str(), url: str() },
|
|
51
51
|
},
|
|
@@ -357,6 +357,18 @@ function validate(spec) {
|
|
|
357
357
|
}
|
|
358
358
|
if (sl.layout === "TODO") return;
|
|
359
359
|
checkFields({ ...COMMON, ...def.fields }, sl, path, errs, warns);
|
|
360
|
+
// COMMON exempts fields every layout takes — but the full-bleed three do
|
|
361
|
+
// not take all of them, and a lede typed as subtitleText on a cover was
|
|
362
|
+
// silently dropped: valid name, wrong layout, no message, one ship cycle
|
|
363
|
+
// wasted finding out. Say it at validate time instead.
|
|
364
|
+
if (def.bleed) {
|
|
365
|
+
if (sl.subtitleText != null && !def.fields.subtitleText) {
|
|
366
|
+
errs.push(`${path}: ${sl.layout} does not render subtitleText — its lede goes in \`body\``);
|
|
367
|
+
}
|
|
368
|
+
if (sl.dark === false) {
|
|
369
|
+
warns.push(`${path}: ${sl.layout} is always dark — \`dark: false\` is ignored`);
|
|
370
|
+
}
|
|
371
|
+
}
|
|
360
372
|
seen.push(sl.layout);
|
|
361
373
|
});
|
|
362
374
|
|
package/package.json
CHANGED
package/wand_kit.js
CHANGED
|
@@ -273,7 +273,7 @@ function cover(pres, { eyebrow, titleLines, body, kickerPlain, kickerBold, tag }
|
|
|
273
273
|
s.addImage({ path: `${ASSETS}/bg-cover-dark.png`, x: 0, y: 0, w: 10, h: 5.625 });
|
|
274
274
|
s.addImage({ path: `${ASSETS}/orb-cover.png`, x: 5.72, y: 0.38, w: 4.61, h: 4.61 });
|
|
275
275
|
if (tag) chrome(s, { tag, dark: true });
|
|
276
|
-
s.addText(eyebrow.toUpperCase(), {
|
|
276
|
+
if (eyebrow) s.addText(eyebrow.toUpperCase(), {
|
|
277
277
|
x: 0.38, y: 1.24, w: 6.0, h: 0.4, margin: 0, isTextBox: true,
|
|
278
278
|
fontFace: F.med, fontSize: 12, color: T.accentSoft, valign: "middle",
|
|
279
279
|
});
|
|
@@ -290,10 +290,14 @@ function cover(pres, { eyebrow, titleLines, body, kickerPlain, kickerBold, tag }
|
|
|
290
290
|
fontFace: F.reg, fontSize: 12.5, color: T.inkDark, valign: "top", lineSpacingMultiple: 1.12,
|
|
291
291
|
});
|
|
292
292
|
s.addShape("rect", { x: 0.38, y: 3.95, w: 0.60, h: 0.04, fill: { color: T.accent }, line: { width: 0 } });
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
293
|
+
// Concatenating an absent kicker used to paint the word "undefined" on the
|
|
294
|
+
// cover — visible only in a render, past every machine gate.
|
|
295
|
+
const kickerRuns = [
|
|
296
|
+
kickerPlain && { text: kickerPlain + " ", options: { fontFace: F.reg, fontSize: 10, color: T.inkDark } },
|
|
297
|
+
kickerBold && { text: kickerBold, options: { fontFace: F.reg, fontSize: 10, bold: true, color: T.white } },
|
|
298
|
+
].filter(Boolean);
|
|
299
|
+
if (kickerRuns.length) s.addText(kickerRuns,
|
|
300
|
+
{ x: 0.39, y: 4.03, w: 6.0, h: 0.4, margin: 0, isTextBox: true, valign: "middle" });
|
|
297
301
|
return s;
|
|
298
302
|
}
|
|
299
303
|
|
|
@@ -303,7 +307,7 @@ function divider(pres, { tag, eyebrow, titleText, body }) {
|
|
|
303
307
|
s.background = { color: T.bgDark };
|
|
304
308
|
s.addImage({ path: `${ASSETS}/bg-statement-dark.png`, x: 0, y: 0, w: 10, h: 5.625 });
|
|
305
309
|
chrome(s, { tag, dark: true });
|
|
306
|
-
s.addText(eyebrow.toUpperCase(), {
|
|
310
|
+
if (eyebrow) s.addText(eyebrow.toUpperCase(), {
|
|
307
311
|
x: 0.38, y: 1.77, w: 8.9, h: 0.24, margin: 0, isTextBox: true,
|
|
308
312
|
fontFace: F.med, fontSize: 12, color: T.accentSoft, valign: "middle",
|
|
309
313
|
});
|
|
@@ -329,14 +333,14 @@ function closing(pres, { eyebrow, line1, line2, body, url }) {
|
|
|
329
333
|
s.background = { color: T.bgDark };
|
|
330
334
|
s.addImage({ path: `${ASSETS}/bg-statement-dark.png`, x: 0, y: 0, w: 10, h: 5.625 });
|
|
331
335
|
s.addImage({ path: `${ASSETS}/wordmark-white.png`, x: 4.60, y: 0.98, w: 0.80, h: 0.22 });
|
|
332
|
-
s.addText(eyebrow.toUpperCase(), {
|
|
336
|
+
if (eyebrow) s.addText(eyebrow.toUpperCase(), {
|
|
333
337
|
x: 0.55, y: 1.85, w: 8.9, h: 0.3, margin: 0, isTextBox: true,
|
|
334
338
|
fontFace: F.med, fontSize: 12, color: T.accentSoft, align: "center", valign: "middle",
|
|
335
339
|
});
|
|
336
340
|
s.addText([
|
|
337
|
-
{ text: line1 + "\n", options: { fontFace: F.reg, fontSize: 24, bold: true, color: T.white } },
|
|
338
|
-
{ text: line2,
|
|
339
|
-
], { x: 0.55, y: 2.19, w: 8.9, h: 1.0, margin: 0, isTextBox: true, align: "center", valign: "top", lineSpacingMultiple: 1.15 });
|
|
341
|
+
line1 && { text: line1 + (line2 ? "\n" : ""), options: { fontFace: F.reg, fontSize: 24, bold: true, color: T.white } },
|
|
342
|
+
line2 && { text: line2, options: { fontFace: F.reg, fontSize: 24, bold: true, italic: true, color: T.accentSoft } },
|
|
343
|
+
].filter(Boolean), { x: 0.55, y: 2.19, w: 8.9, h: 1.0, margin: 0, isTextBox: true, align: "center", valign: "top", lineSpacingMultiple: 1.15 });
|
|
340
344
|
if (body) s.addText(body, {
|
|
341
345
|
x: 1.5, y: 3.23, w: 7.0, h: 0.7, margin: 0, isTextBox: true,
|
|
342
346
|
fontFace: F.reg, fontSize: 12.5, color: T.bodyDark, align: "center", valign: "top", lineSpacingMultiple: 1.25,
|
|
@@ -363,7 +367,7 @@ function threeUp(pres, { tag, titleText, subtitleText, items, dark = true, arrow
|
|
|
363
367
|
items.slice(0, 3).forEach((it, i) => {
|
|
364
368
|
const cx = x[i], cy = 2.02;
|
|
365
369
|
card(s, { x: cx, y: cy, w, h: 2.70, dark, variant: it.emph ? "emph" : "glass" });
|
|
366
|
-
s.addText(it.eyebrow.toUpperCase(), {
|
|
370
|
+
if (it.eyebrow) s.addText(it.eyebrow.toUpperCase(), {
|
|
367
371
|
x: cx + 0.22, y: cy + 0.22, w: w - 0.42, h: 0.20, margin: 0, isTextBox: true,
|
|
368
372
|
fontFace: F.med, fontSize: 8, color: th.eyebrow, valign: "middle",
|
|
369
373
|
});
|
|
@@ -917,6 +921,7 @@ function productMap(pres, { tag, titleText, subtitleText, bandRow, cards, split,
|
|
|
917
921
|
if (subtitleText) subtitle(s, subtitleText, { dark, y: 1.16, h: 0.28 });
|
|
918
922
|
const th = theme(dark);
|
|
919
923
|
const rail = (label, y, h) => {
|
|
924
|
+
if (!label) return;
|
|
920
925
|
s.addShape("roundRect", {
|
|
921
926
|
x: 0.37, y, w: 1.0, h, rectRadius: radius(1.0, h),
|
|
922
927
|
fill: { color: dark ? T.cardMuted : T.surface }, line: { width: 0 },
|
|
@@ -994,7 +999,7 @@ function productMap(pres, { tag, titleText, subtitleText, bandRow, cards, split,
|
|
|
994
999
|
|
|
995
1000
|
|
|
996
1001
|
module.exports = {
|
|
997
|
-
VERSION: "0.8.
|
|
1002
|
+
VERSION: "0.8.2",
|
|
998
1003
|
T, F, COLS, ASSETS, radius, theme, autoSize, measure: M,
|
|
999
1004
|
background, chrome, footer, title, subtitle, kicker, contentTop,
|
|
1000
1005
|
card, iconTile, pill,
|