@taloswork/ose-shared 0.1.11 → 0.1.12

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.
Files changed (119) hide show
  1. package/package.json +3 -3
  2. package/skills/document/SKILL.md +143 -215
  3. package/skills/document/art-directions/base.css +27 -2
  4. package/skills/document/art-directions/cinematic-depth.css +3 -1
  5. package/skills/document/art-directions/collage-paper.css +5 -1
  6. package/skills/document/art-directions/editorial-cut.css +5 -1
  7. package/skills/document/art-directions/geometric-pop.css +6 -0
  8. package/skills/document/art-directions/gradient-mesh.css +5 -1
  9. package/skills/document/art-directions/illustrated-doodle.css +5 -0
  10. package/skills/document/art-directions/luxury-ornament.css +4 -1
  11. package/skills/document/art-directions/maximalist-blocks.css +6 -0
  12. package/skills/document/art-directions/technical-grid.css +4 -0
  13. package/skills/document/assets/fonts/fonts.json +2 -2
  14. package/skills/document/assets/pdf-fonts/NotoSansSC-PDF-Bold.ttf +0 -0
  15. package/skills/document/assets/pdf-fonts/NotoSansSC-PDF-Regular.ttf +0 -0
  16. package/skills/document/assets/pdf-fonts/OFL.txt +93 -0
  17. package/skills/document/assets/pdf-fonts/README.md +35 -0
  18. package/skills/document/assets/tessdata/LICENSE-Apache-2.0.txt +202 -0
  19. package/skills/document/assets/tessdata/README.md +21 -0
  20. package/skills/document/assets/tessdata/chi_sim.traineddata.gz +0 -0
  21. package/skills/document/assets/tessdata/eng.traineddata.gz +0 -0
  22. package/skills/document/bin/deck-build.cjs +2311 -41
  23. package/skills/document/bin/deck-chart.cjs +762 -0
  24. package/skills/document/bin/deck-plan.cjs +802 -0
  25. package/skills/document/bin/deck-qa.cjs +984 -249
  26. package/skills/document/bin/deck-scenario.cjs +1346 -0
  27. package/skills/document/bin/doc-convert.cjs +130 -39
  28. package/skills/document/bin/doc-fill.cjs +73121 -170
  29. package/skills/document/bin/doc-gen.cjs +625 -68
  30. package/skills/document/bin/doc-run.cjs +162 -24
  31. package/skills/document/bin/doc-scan.cjs +13 -0
  32. package/skills/document/bin/doc-text.cjs +13 -0
  33. package/skills/document/bin/doc-validate.cjs +718 -23
  34. package/skills/document/bin/doc-xml.cjs +9 -5
  35. package/skills/document/bin/docx-annotate.cjs +95 -19
  36. package/skills/document/bin/fontkit.cjs +1 -0
  37. package/skills/document/bin/html2pptx.cjs +1372 -121
  38. package/skills/document/bin/ocr-worker.cjs +1 -0
  39. package/skills/document/bin/ocr.cjs +131 -40
  40. package/skills/document/bin/pdf-form.cjs +674 -177
  41. package/skills/document/bin/pdf-images.cjs +13 -0
  42. package/skills/document/bin/pdf-ops.cjs +634 -199
  43. package/skills/document/bin/pdf-render.cjs +19 -3
  44. package/skills/document/bin/pdf-tables.cjs +13 -0
  45. package/skills/document/bin/pptx-clone.cjs +3 -3
  46. package/skills/document/bin/pptx-slide.cjs +3 -3
  47. package/skills/document/bin/xlsx-query.cjs +192 -61
  48. package/skills/document/bin-reference.md +56 -33
  49. package/skills/document/content-pipeline.md +129 -242
  50. package/skills/document/deck-design.md +355 -638
  51. package/skills/document/layouts/README.md +46 -33
  52. package/skills/document/layouts/capacity.json +1 -0
  53. package/skills/document/layouts/slots.json +1 -1
  54. package/skills/document/layouts/toc-panel.tpl +1 -1
  55. package/skills/document/motion/README.md +3 -2
  56. package/skills/document/office.md +3 -3
  57. package/skills/document/packs/board-brief/backdrop.html +2 -0
  58. package/skills/document/packs/board-brief/pack.css +2 -1
  59. package/skills/document/packs/mckinsey-photo/backdrop.html +2 -0
  60. package/skills/document/packs/mckinsey-photo/pack.css +2 -1
  61. package/skills/document/packs/press-room/backdrop.html +2 -0
  62. package/skills/document/packs/press-room/pack.css +2 -1
  63. package/skills/document/packs/prismatic-3d/backdrop.html +2 -0
  64. package/skills/document/packs/prismatic-3d/pack.css +4 -1
  65. package/skills/document/pdf.md +53 -49
  66. package/skills/document/rewrite.md +241 -308
  67. package/skills/document/scenarios.json +1290 -0
  68. package/skills/document/scenarios.schema.json +103 -0
  69. package/skills/document/styles/README.md +15 -25
  70. package/skills/document/styles/_contract-defaults.css +25 -1
  71. package/skills/document/styles/anthropic/skin.css +6 -3
  72. package/skills/document/styles/apple/skin.css +4 -3
  73. package/skills/document/styles/bistro-warm/backdrop.html +2 -0
  74. package/skills/document/styles/bistro-warm/skin.css +4 -1
  75. package/skills/document/styles/black-gold/skin.css +6 -5
  76. package/skills/document/styles/bold-clash/backdrop.html +2 -0
  77. package/skills/document/styles/bold-clash/skin.css +2 -1
  78. package/skills/document/styles/branding/skin.css +4 -4
  79. package/skills/document/styles/campus-teal/backdrop.html +2 -0
  80. package/skills/document/styles/campus-teal/skin.css +2 -1
  81. package/skills/document/styles/cinereel/skin.css +7 -3
  82. package/skills/document/styles/clinic-care/backdrop.html +2 -0
  83. package/skills/document/styles/clinic-care/skin.css +2 -1
  84. package/skills/document/styles/cool/skin.css +6 -5
  85. package/skills/document/styles/dark-glass/backdrop.html +2 -0
  86. package/skills/document/styles/dark-glass/skin.css +2 -1
  87. package/skills/document/styles/gov-brief/backdrop.html +2 -0
  88. package/skills/document/styles/gov-brief/skin.css +2 -1
  89. package/skills/document/styles/grid-tech/backdrop.html +2 -0
  90. package/skills/document/styles/grid-tech/skin.css +2 -1
  91. package/skills/document/styles/hand-drawn/skin.css +3 -3
  92. package/skills/document/styles/ivory-gold/skin.css +7 -6
  93. package/skills/document/styles/legal-slate/backdrop.html +2 -0
  94. package/skills/document/styles/legal-slate/skin.css +2 -1
  95. package/skills/document/styles/magazine-ink/skin.css +4 -3
  96. package/skills/document/styles/mckinsey/skin.css +2 -1
  97. package/skills/document/styles/memphis/backdrop.html +2 -0
  98. package/skills/document/styles/memphis/skin.css +2 -1
  99. package/skills/document/styles/mono/skin.css +6 -2
  100. package/skills/document/styles/neon-cyber/backdrop.html +2 -0
  101. package/skills/document/styles/neon-cyber/skin.css +2 -1
  102. package/skills/document/styles/olive-gold/skin.css +7 -6
  103. package/skills/document/styles/organic-earth/backdrop.html +2 -0
  104. package/skills/document/styles/organic-earth/skin.css +2 -1
  105. package/skills/document/styles/pastel-saas/backdrop.html +2 -0
  106. package/skills/document/styles/pastel-saas/skin.css +2 -1
  107. package/skills/document/styles/portfolio-noir/backdrop.html +2 -0
  108. package/skills/document/styles/portfolio-noir/skin.css +2 -1
  109. package/skills/document/styles/retro-press/backdrop.html +2 -0
  110. package/skills/document/styles/retro-press/skin.css +2 -1
  111. package/skills/document/styles/sport-arena/backdrop.html +2 -0
  112. package/skills/document/styles/sport-arena/skin.css +4 -1
  113. package/skills/document/styles/swiss-grid/skin.css +10 -5
  114. package/skills/document/styles/wedding-blush/backdrop.html +2 -0
  115. package/skills/document/styles/wedding-blush/skin.css +4 -1
  116. package/skills/document/theme-system.md +85 -77
  117. package/skills/document/themes/README.md +7 -5
  118. package/skills/document/vision.md +8 -5
  119. package/skills/document/xlsx.md +15 -25
@@ -0,0 +1,802 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+
4
+ // apps/admin/data/skills.lib/document/bin-src/deck-plan.mjs
5
+ var import_promises = require("node:fs/promises");
6
+ var import_node_fs = require("node:fs");
7
+ var import_node_path = require("node:path");
8
+
9
+ // packages/core/dist/document/deck-figures.js
10
+ var FIGURE_SPECS = {
11
+ radar: {
12
+ metrics: { required: ["name", "score"], derived: [] }
13
+ },
14
+ gantt: {
15
+ cols: { required: ["label"], derived: [] },
16
+ tasks: { required: ["name", "from", "to"], derived: ["_left", "_width"] }
17
+ },
18
+ "ring-progress": {
19
+ rings: { required: ["label", "pct"], derived: ["_pct", "_dashoffset"] }
20
+ },
21
+ "map-distribution": {
22
+ regions: { required: ["name", "pct"], derived: [] },
23
+ pins: { required: ["x", "y"], derived: ["_left", "_top"] }
24
+ }
25
+ };
26
+ var DERIVED_CHART_LAYOUTS = /* @__PURE__ */ new Set(["radar"]);
27
+ var DERIVED_CHART_OPTIONS = /* @__PURE__ */ new Set(["max", "unit", "showValues", "render"]);
28
+ function rowsFromChart(layout, chart) {
29
+ if (!DERIVED_CHART_LAYOUTS.has(layout))
30
+ return { reject: `${layout} does not derive its chart from rows` };
31
+ const c = chart && typeof chart === "object" && !Array.isArray(chart) ? chart : null;
32
+ if (!c || c.type !== "radar")
33
+ return { reject: `draws only a radar chart (content chart type: ${String(c?.type ?? "none")})` };
34
+ const labels = Array.isArray(c.labels) ? c.labels : [];
35
+ const series = Array.isArray(c.series) ? c.series : [];
36
+ if (series.length !== 1)
37
+ return { reject: `its score list holds one series \u2014 the radar has ${series.length}` };
38
+ const values = series[0]?.values;
39
+ if (!labels.length || !Array.isArray(values) || values.length !== labels.length) {
40
+ return { reject: `needs one value per radar dimension (${labels.length} label(s), ${Array.isArray(values) ? values.length : 0} value(s))` };
41
+ }
42
+ const options = {};
43
+ for (const [k, v] of Object.entries(c))
44
+ if (DERIVED_CHART_OPTIONS.has(k))
45
+ options[k] = v;
46
+ return { rows: { metrics: labels.map((name, j) => ({ name: String(name), score: values[j] })) }, options };
47
+ }
48
+
49
+ // packages/core/dist/document/deck-chart-svg.js
50
+ var RAMP = [
51
+ "var(--series-1, var(--c1, var(--accent)))",
52
+ "var(--series-2, var(--c2, var(--muted)))",
53
+ "var(--series-3, var(--c3, var(--title)))",
54
+ "var(--series-4, var(--c4, var(--body)))"
55
+ ];
56
+ var MAX_SERIES = RAMP.length;
57
+
58
+ // packages/core/dist/document/deck-build.js
59
+ var SLOT_RE = /\{\{(?![#/])([^{}]*)\}\}/g;
60
+ var BLOCK_RE = /\{\{#([A-Za-z_][\w-]*)\}\}([\s\S]*?)\{\{\/\1\}\}/g;
61
+ var slotName = (inner) => {
62
+ const bar = inner.indexOf("|");
63
+ return (bar >= 0 ? inner.slice(0, bar) : inner).trim();
64
+ };
65
+ var withoutComments = (tpl) => tpl.replace(/<!--[\s\S]*?-->/g, "");
66
+ var withoutBlocks = (tpl) => tpl.replace(BLOCK_RE, "");
67
+ function templateDefaults(tpl) {
68
+ const out = {};
69
+ for (const m of withoutBlocks(withoutComments(tpl)).matchAll(SLOT_RE)) {
70
+ const name = slotName(m[1]);
71
+ if (name.startsWith("_") || Object.hasOwn(out, name))
72
+ continue;
73
+ const bar = m[1].indexOf("|");
74
+ out[name] = bar >= 0 ? m[1].slice(bar + 1) : "";
75
+ }
76
+ return out;
77
+ }
78
+
79
+ // packages/core/dist/document/ooxml-util.js
80
+ function visualWidth(text) {
81
+ let w = 0;
82
+ for (const ch of text) {
83
+ const cp = ch.codePointAt(0);
84
+ if (ch === " " || ch === " ")
85
+ w += 0.35;
86
+ else if (cp >= 4352 && cp <= 4447 || // Hangul Jamo
87
+ cp >= 11904 && cp <= 40959 || // CJK radicals … unified ideographs
88
+ cp >= 40960 && cp <= 42191 || // Yi
89
+ cp >= 44032 && cp <= 55203 || // Hangul syllables
90
+ cp >= 63744 && cp <= 64255 || // CJK compat ideographs
91
+ cp >= 65072 && cp <= 65103 || // CJK compat forms
92
+ cp >= 65280 && cp <= 65376 || // fullwidth forms
93
+ cp >= 65504 && cp <= 65510 || cp >= 131072 && cp <= 262141)
94
+ w += 1;
95
+ else
96
+ w += 0.5;
97
+ }
98
+ return w;
99
+ }
100
+
101
+ // packages/core/dist/document/deck-capacity.js
102
+ function parseCapacityDoc(raw) {
103
+ const d = raw;
104
+ if (!d || typeof d !== "object" || !Array.isArray(d.styles) || !d.layouts || typeof d.layouts !== "object")
105
+ return null;
106
+ return d;
107
+ }
108
+ function pick(v, idx, ceiling) {
109
+ if (v === void 0)
110
+ return void 0;
111
+ let n;
112
+ if (typeof v === "number")
113
+ n = v;
114
+ else if (idx >= 0 && typeof v[idx] === "number")
115
+ n = v[idx];
116
+ else {
117
+ const present = v.filter((x) => typeof x === "number");
118
+ n = present.length ? Math.min(...present) : void 0;
119
+ }
120
+ if (n === void 0)
121
+ return void 0;
122
+ return Math.min(n, ceiling);
123
+ }
124
+ function capOf(v, idx, ceiling) {
125
+ if (!v)
126
+ return void 0;
127
+ const max = pick(v.max, idx, ceiling);
128
+ if (max === void 0)
129
+ return void 0;
130
+ const one = pick(v.one, idx, ceiling);
131
+ return one !== void 0 && one < max ? { max, one } : { max };
132
+ }
133
+ function resolveLayoutCapacity(doc, layout, ctx = {}) {
134
+ const packKey = ctx.pack ? `${ctx.pack}/${layout}` : null;
135
+ const key = packKey && doc.layouts[packKey] ? packKey : layout;
136
+ const raw = doc.layouts[key];
137
+ if (!raw)
138
+ return null;
139
+ const styleId = key === packKey ? ctx.pack : ctx.style;
140
+ const idx = styleId ? doc.styles.indexOf(styleId) : -1;
141
+ const ceiling = doc.maxUnits ?? 160;
142
+ const slots = {};
143
+ for (const [s, v] of Object.entries(raw.slots ?? {})) {
144
+ const c = capOf(v, idx, ceiling);
145
+ if (c)
146
+ slots[s] = c;
147
+ }
148
+ const rows = {};
149
+ for (const [k, r] of Object.entries(raw.rows ?? {})) {
150
+ const fields = {};
151
+ for (const [f, fv] of Object.entries(r.fields ?? {})) {
152
+ const lo = capOf(fv.lo, idx, ceiling);
153
+ const hi = capOf(fv.hi, idx, ceiling);
154
+ if (lo && hi)
155
+ fields[f] = { lo, hi };
156
+ }
157
+ rows[k] = { max: pick(r.max, idx, Number.POSITIVE_INFINITY) ?? 1, fields };
158
+ }
159
+ return { key, slots, numeric: new Set(raw.numeric ?? []), rows, charts: raw.charts ?? [] };
160
+ }
161
+ function rowFieldCap(field, n, max) {
162
+ const loN = Math.min(3, max);
163
+ if (n <= loN || max <= loN)
164
+ return field.lo;
165
+ if (n >= max)
166
+ return field.hi;
167
+ const t = (n - loN) / (max - loN);
168
+ const mix = (a, b) => Math.floor(a + (b - a) * t);
169
+ const one = field.lo.one !== void 0 || field.hi.one !== void 0 ? mix(field.lo.one ?? field.lo.max, field.hi.one ?? field.hi.max) : void 0;
170
+ const out = { max: mix(field.lo.max, field.hi.max) };
171
+ if (one !== void 0 && one < out.max)
172
+ out.one = one;
173
+ return out;
174
+ }
175
+ var ONE_LINE_SLOT = /_(?:title|name|label|head|date|dur|unit|tag|initials|role|key)$|^(?:eyebrow|badge|tag|round|kicker|featured|brand\d*|col\d+|name|label)$/;
176
+ var NUMBERED_SLOT = /\d/;
177
+ var CHROME_SLOT = /^(?:eyebrow|footer|pageno|meta|date|presenter|brand\d+|refs|image_label|media_label|source_label|index)$/;
178
+ var fmtW = (n) => String(Math.round(n * 10) / 10);
179
+ function checkSlideCapacity(slide, cap, defaults) {
180
+ const errors = [];
181
+ const warnings = [];
182
+ const slots = slide.slots ?? {};
183
+ const checkText = (where, value, c, numeric, oneLine) => {
184
+ const w = visualWidth(value);
185
+ if (w > c.max) {
186
+ errors.push({
187
+ slot: where,
188
+ code: "overflow",
189
+ message: numeric ? `${where} "${value}" will not stay on one line \u2014 it holds \u2264${fmtW(c.max)} (\u2248${Math.floor(c.max * 2)} digits); shorten the number (\u4E07/\u4EBF/K/M) or move the unit into its unit slot` : `${where} is ~${fmtW(w)} wide but holds \u2264${fmtW(c.max)} \u2014 cut ~${fmtW(w - c.max)} (CJK=1, Latin\u22480.5), or pick a roomier layout (deck-plan)`
190
+ });
191
+ } else if (oneLine && c.one !== void 0 && w > c.one) {
192
+ warnings.push({ slot: where, code: "wrap", message: `${where} wraps (~${fmtW(w)} wide; one line holds ${fmtW(c.one)})` });
193
+ }
194
+ };
195
+ for (const [name, raw] of Object.entries(slots)) {
196
+ const c = cap.slots[name];
197
+ if (!c || raw === void 0 || raw === null)
198
+ continue;
199
+ checkText(name, String(raw), c, cap.numeric.has(name), ONE_LINE_SLOT.test(name));
200
+ }
201
+ for (const [key, list] of Object.entries(slide.rows ?? {})) {
202
+ const r = cap.rows[key];
203
+ if (!r || !Array.isArray(list))
204
+ continue;
205
+ if (list.length > r.max) {
206
+ errors.push({
207
+ slot: `rows.${key}`,
208
+ code: "rows",
209
+ message: `rows.${key} has ${list.length} rows but the layout holds \u2264${r.max} \u2014 split the slide or merge rows`
210
+ });
211
+ }
212
+ list.forEach((row, i) => {
213
+ if (!row || typeof row !== "object")
214
+ return;
215
+ for (const [f, v] of Object.entries(row)) {
216
+ const fc = r.fields[f];
217
+ if (!fc || v === void 0 || v === null)
218
+ continue;
219
+ checkText(`rows.${key}[${i}].${f}`, String(v), rowFieldCap(fc, list.length, r.max), false, ONE_LINE_SLOT.test(f));
220
+ }
221
+ });
222
+ }
223
+ if (defaults) {
224
+ const missingNumbered = [];
225
+ for (const [name, def] of Object.entries(defaults)) {
226
+ if (name.startsWith("_") || Object.hasOwn(slots, name))
227
+ continue;
228
+ if (!def.trim())
229
+ continue;
230
+ if (NUMBERED_SLOT.test(name) && !CHROME_SLOT.test(name))
231
+ missingNumbered.push(name);
232
+ else {
233
+ warnings.push({
234
+ slot: name,
235
+ code: "default",
236
+ message: `${name} is not filled \u2014 it renders the template text "${def.replace(/<[^>]+>/g, " ").trim().slice(0, 24)}"; pass "" to leave it empty`
237
+ });
238
+ }
239
+ }
240
+ if (missingNumbered.length) {
241
+ errors.push({
242
+ slot: missingNumbered[0],
243
+ code: "underfill",
244
+ message: `${missingNumbered.join(", ")} not filled \u2014 the slide would show the template's demo copy there. Give every item this layout has, or pick a layout with fewer slots (deck-plan)`
245
+ });
246
+ }
247
+ }
248
+ return { errors, warnings };
249
+ }
250
+
251
+ // packages/core/dist/document/deck-plan.js
252
+ function parseLayoutTable(md) {
253
+ const out = [];
254
+ for (const line of md.split("\n")) {
255
+ const m = /^\|\s*`([a-z0-9-]+)`\s*\|(.*)\|\s*$/.exec(line.trim());
256
+ if (!m)
257
+ continue;
258
+ const cells = m[2].split("|").map((c) => c.trim());
259
+ if (cells.length < 5)
260
+ continue;
261
+ out.push({ id: m[1], role: cells[0], structure: cells[1], density: cells[2], hasImage: cells[3], pickWhen: cells.slice(4).join("|"), order: out.length });
262
+ }
263
+ return out;
264
+ }
265
+ var SUFFIX_ROLE = {
266
+ title: "head",
267
+ name: "head",
268
+ head: "head",
269
+ key: "head",
270
+ body: "body",
271
+ desc: "body",
272
+ bio: "body",
273
+ text: "body",
274
+ num: "num",
275
+ val: "num",
276
+ value: "num",
277
+ stat: "num",
278
+ unit: "unit",
279
+ label: "label",
280
+ note: "note",
281
+ date: "date",
282
+ dur: "date",
283
+ initials: "initials",
284
+ role: "label"
285
+ };
286
+ var BARE_PREFIX_ROLE = { unit: "unit", label: "label", pct: "num", price: "num", num: "num", val: "num", value: "num", stat: "num", note: "note", date: "date" };
287
+ var NUMERIC_TEXT = /^[\s\d.,%+\-−~$¥€£x×/:]*\d[\s\d.,%+\-−~$¥€£x×/:]*$/;
288
+ function layoutStructure(slots, defaults = {}) {
289
+ const raw = /* @__PURE__ */ new Map();
290
+ const singles = [];
291
+ const isTable = slots.some((s) => /^r\d+c\d+$/.test(s));
292
+ const table = isTable ? { cols: [], keys: [], cells: [] } : null;
293
+ for (const s of slots) {
294
+ if (table) {
295
+ let m2 = /^r(\d+)c(\d+)$/.exec(s);
296
+ if (m2) {
297
+ (table.cells[+m2[1] - 1] ??= [])[+m2[2] - 1] = s;
298
+ continue;
299
+ }
300
+ m2 = /^r(\d+)_key$/.exec(s);
301
+ if (m2) {
302
+ table.keys[+m2[1] - 1] = s;
303
+ continue;
304
+ }
305
+ m2 = /^col(\d+)$/.exec(s);
306
+ if (m2) {
307
+ table.cols[+m2[1]] = s;
308
+ continue;
309
+ }
310
+ }
311
+ const m = /^([a-z]+?)(\d+)_([a-z]+)$/.exec(s) ?? /^([a-z]+)_(\d+)()$/.exec(s) ?? /^([a-z]+?)(\d+)()$/.exec(s);
312
+ if (!m) {
313
+ singles.push(s);
314
+ continue;
315
+ }
316
+ const list = raw.get(m[1]) ?? [];
317
+ list.push({ n: Number(m[2]), suffix: m[3], slot: s });
318
+ raw.set(m[1], list);
319
+ }
320
+ const byPrefix = /* @__PURE__ */ new Map();
321
+ for (const s of singles) {
322
+ const m = /^([a-z]+)_([a-z]+)$/.exec(s);
323
+ if (m && !raw.has(m[1]))
324
+ byPrefix.set(m[1], [...byPrefix.get(m[1]) ?? [], m[2]]);
325
+ }
326
+ const sig = (p) => [...byPrefix.get(p) ?? []].sort().join(",");
327
+ const groups = /* @__PURE__ */ new Map();
328
+ for (const p of byPrefix.keys())
329
+ if ((byPrefix.get(p)?.length ?? 0) >= 2)
330
+ groups.set(sig(p), [...groups.get(sig(p)) ?? [], p]);
331
+ for (const prefixes of groups.values()) {
332
+ if (prefixes.length < 2)
333
+ continue;
334
+ const name = prefixes.join("+");
335
+ raw.set(name, prefixes.flatMap((p, i) => (byPrefix.get(p) ?? []).map((suffix) => ({ n: i + 1, suffix, slot: `${p}_${suffix}` }))));
336
+ for (const p of prefixes)
337
+ for (const suffix of byPrefix.get(p) ?? [])
338
+ singles.splice(singles.indexOf(`${p}_${suffix}`), 1);
339
+ }
340
+ const families = [];
341
+ for (const [prefix, list] of raw) {
342
+ const hasHead = list.some((x) => SUFFIX_ROLE[x.suffix] === "head");
343
+ const members = /* @__PURE__ */ new Map();
344
+ for (const x of list) {
345
+ let role;
346
+ if (x.suffix)
347
+ role = SUFFIX_ROLE[x.suffix] ?? "other";
348
+ else if (NUMERIC_TEXT.test((defaults[x.slot] ?? "").trim()))
349
+ role = "num";
350
+ else
351
+ role = BARE_PREFIX_ROLE[prefix] ?? (hasHead ? "label" : "head");
352
+ members.set(x.n, [...members.get(x.n) ?? [], { slot: x.slot, role }]);
353
+ }
354
+ const all = [...members.values()].flat();
355
+ families.push({
356
+ prefix,
357
+ indexes: [...members.keys()].sort((a, b) => a - b),
358
+ members,
359
+ stat: all.some((m) => m.role === "num"),
360
+ chrome: all.every((m) => CHROME_SLOT.test(m.slot)),
361
+ hasHead: all.some((m) => m.role === "head"),
362
+ hasBody: all.some((m) => m.role === "body")
363
+ });
364
+ }
365
+ return { families, singles, table };
366
+ }
367
+ var item = (p) => typeof p === "string" ? { title: p } : p ?? {};
368
+ var str = (v) => v === void 0 || v === null || v === "" ? void 0 : String(v);
369
+ var initials = (s) => s.trim().slice(0, s.trim().match(/^[\x00-\x7f]/) ? 2 : 1).toUpperCase();
370
+ var MAX_BLANKS = 2;
371
+ var TITLE_SLOTS = ["title", "statement", "banner"];
372
+ var SUB_SLOTS = ["subtitle", "sub", "lead", "intro", "desc", "body", "tagline", "caption", "note"];
373
+ var SOURCE_SLOTS = ["source", "quote_attr", "person"];
374
+ var STAT_LABEL_SLOTS = ["stat_label", "lead", "stat_note"];
375
+ function fillMember(role, slot, it, fam, reject, blanks) {
376
+ switch (role) {
377
+ case "head": {
378
+ const v = str(it.title) ?? str(it.label);
379
+ if (v === void 0) {
380
+ reject.push(`${slot} needs a title for every item`);
381
+ return "";
382
+ }
383
+ return !fam.hasBody && str(it.body) && fam.members.get(1)?.length === 1 ? `${v}\uFF1A${it.body}` : v;
384
+ }
385
+ case "body": {
386
+ const v = str(it.body) ?? str(it.note);
387
+ if (v === void 0)
388
+ blanks.push(slot);
389
+ return v ?? "";
390
+ }
391
+ case "num": {
392
+ const v = str(it.value);
393
+ if (v === void 0) {
394
+ reject.push(`${slot} needs a number (value) for every item`);
395
+ return "";
396
+ }
397
+ return v;
398
+ }
399
+ case "unit":
400
+ return str(it.unit) ?? "";
401
+ case "label":
402
+ return (fam.hasHead ? str(it.label) : str(it.label) ?? str(it.title)) ?? "";
403
+ case "note":
404
+ return str(it.note) ?? (fam.hasBody ? "" : str(it.body) ?? "");
405
+ case "date": {
406
+ const v = str(it.date);
407
+ if (v === void 0) {
408
+ reject.push(`${slot} needs a date for every item`);
409
+ return "";
410
+ }
411
+ return v;
412
+ }
413
+ case "initials":
414
+ return initials(str(it.title) ?? str(it.label) ?? "");
415
+ default:
416
+ blanks.push(slot);
417
+ return "";
418
+ }
419
+ }
420
+ function rowField(field, it, required, reject, at) {
421
+ if (Object.hasOwn(it, field))
422
+ return it[field];
423
+ const role = SUFFIX_ROLE[field] ?? (field === "score" || field === "pct" || field === "hex" ? "num" : void 0);
424
+ let v;
425
+ if (role === "head")
426
+ v = str(it.title) ?? str(it.label);
427
+ else if (role === "label")
428
+ v = str(it.label) ?? str(it.title);
429
+ else if (role === "body" || role === "note")
430
+ v = str(it.body) ?? str(it.note);
431
+ else if (role === "num")
432
+ v = str(it.value);
433
+ else if (role === "date")
434
+ v = str(it.date);
435
+ if (v === void 0 && required)
436
+ reject.push(`${at}.${field} is required`);
437
+ if (v !== void 0 && (field === "pct" || field === "score")) {
438
+ const n = Number(String(v).replace(/%$/, ""));
439
+ if (!Number.isFinite(n) || field === "pct" && (n < 0 || n > 100))
440
+ reject.push(`${at}.${field} must be ${field === "pct" ? "a percentage 0\u2013100" : "a number"} (got "${v}")`);
441
+ }
442
+ return v;
443
+ }
444
+ function draftSlide(info, content, ctx) {
445
+ const structure = layoutStructure(info.slots, info.defaults);
446
+ const points = (content.points ?? []).map(item);
447
+ const stats = (content.stats ?? []).map(item);
448
+ const slots = {};
449
+ const blanks = [];
450
+ const reject = [];
451
+ const claimed = /* @__PURE__ */ new Set();
452
+ const set = (slot, v) => {
453
+ slots[slot] = v;
454
+ claimed.add(slot);
455
+ };
456
+ let usedPoints = false;
457
+ let usedStats = false;
458
+ for (const fam of structure.families) {
459
+ const k = fam.indexes.length;
460
+ if (fam.chrome) {
461
+ for (const ms of fam.members.values())
462
+ for (const m of ms)
463
+ set(m.slot, "");
464
+ continue;
465
+ }
466
+ let src = null;
467
+ if (fam.stat) {
468
+ if (stats.length === k) {
469
+ src = stats;
470
+ usedStats = true;
471
+ } else if (points.length === k && points.every((p) => str(p.value) !== void 0)) {
472
+ src = points;
473
+ usedPoints = true;
474
+ }
475
+ } else if (points.length === k) {
476
+ src = points;
477
+ usedPoints = true;
478
+ } else if (stats.length === k && !points.length) {
479
+ src = stats;
480
+ usedStats = true;
481
+ }
482
+ if (!src) {
483
+ const have = fam.stat ? `${stats.length} stat(s)${points.length ? ` / ${points.length} point(s)` : ""}` : `${points.length} point(s)`;
484
+ reject.push(`holds exactly ${k} ${fam.stat ? "numbers" : "items"} (${fam.prefix}1..${k}), content has ${have}`);
485
+ continue;
486
+ }
487
+ fam.indexes.forEach((n, i) => {
488
+ for (const m of fam.members.get(n) ?? [])
489
+ set(m.slot, fillMember(m.role, m.slot, src[i], fam, reject, blanks));
490
+ });
491
+ }
492
+ if (structure.table) {
493
+ const t = structure.table;
494
+ const nCols = t.cells[0]?.length ?? 0;
495
+ if (!content.table)
496
+ reject.push(`a ${t.keys.length}\xD7${nCols} table \u2014 give table {columns, rows}`);
497
+ else if (content.table.rows.length !== t.keys.length || content.table.columns.length !== t.cols.length) {
498
+ reject.push(`holds exactly ${t.keys.length} rows \xD7 ${t.cols.length} columns (incl. the key column), content has ${content.table.rows.length} \xD7 ${content.table.columns.length}`);
499
+ } else {
500
+ t.cols.forEach((s, j) => set(s, String(content.table.columns[j] ?? "")));
501
+ t.keys.forEach((s, i) => set(s, String(content.table.rows[i]?.[0] ?? "")));
502
+ t.cells.forEach((row, i) => row.forEach((s, j) => set(s, String(content.table.rows[i]?.[j + 1] ?? ""))));
503
+ usedPoints = true;
504
+ }
505
+ }
506
+ const fromChart = info.chart === "derived" && content.chart !== void 0 ? rowsFromChart(info.id, content.chart) : null;
507
+ if (fromChart && "reject" in fromChart)
508
+ reject.push(fromChart.reject);
509
+ let rows;
510
+ for (const [key, fields] of Object.entries(info.rows ?? {})) {
511
+ rows ??= {};
512
+ if (content.rows?.[key]) {
513
+ rows[key] = content.rows[key];
514
+ usedPoints = true;
515
+ continue;
516
+ }
517
+ const required = FIGURE_SPECS[info.id]?.[key]?.required ?? [];
518
+ const numeric = fields.some((f) => f === "score" || f === "pct");
519
+ const charted = fromChart && "rows" in fromChart ? fromChart.rows[key] : void 0;
520
+ const src = charted ?? (numeric ? stats.length ? stats : points.filter((p) => str(p.value) !== void 0) : points.length ? points : stats);
521
+ if (!src.length) {
522
+ reject.push(`rows.${key} {${fields.join(",")}} needs ${numeric ? "stats with values" : "points"}`);
523
+ continue;
524
+ }
525
+ if (charted) {
526
+ } else if (numeric)
527
+ usedStats = true;
528
+ else
529
+ usedPoints = true;
530
+ const max = ctx.cap?.rows[key]?.max;
531
+ if (max !== void 0 && src.length > max)
532
+ reject.push(`rows.${key} holds \u2264${max} rows, content has ${src.length}`);
533
+ const range = /\((\d+)(?:\s*[–-]\s*(\d+))?/.exec(info.density);
534
+ if (range && src.length < Number(range[1]))
535
+ reject.push(`rows.${key} reads well with ${range[1]}${range[2] ? `\u2013${range[2]}` : "+"} rows (layouts/README.md), content has ${src.length}`);
536
+ rows[key] = src.map((it, i) => {
537
+ const row = {};
538
+ for (const f of fields) {
539
+ const v = rowField(f, it, required.includes(f), reject, `rows.${key}[${i}]`);
540
+ row[f] = v ?? "";
541
+ }
542
+ return row;
543
+ });
544
+ for (const f of fields)
545
+ if (rows[key].every((r) => r[f] === ""))
546
+ blanks.push(`rows.${key}.${f}`);
547
+ }
548
+ const singles = structure.singles.filter((s) => !claimed.has(s));
549
+ const has = (s) => singles.includes(s) && !claimed.has(s);
550
+ const take = (names, v) => {
551
+ const s = names.find(has);
552
+ if (s !== void 0 && v !== void 0)
553
+ set(s, v);
554
+ return s;
555
+ };
556
+ const quote = typeof content.quote === "string" ? { text: content.quote } : content.quote;
557
+ if (has("stat")) {
558
+ const st = stats[0];
559
+ if (stats.length !== 1 || str(st.value) === void 0)
560
+ reject.push(`holds exactly 1 number (stat), content has ${stats.length}`);
561
+ else {
562
+ set("stat", String(st.value));
563
+ if (has("unit"))
564
+ set("unit", str(st.unit) ?? "");
565
+ take(STAT_LABEL_SLOTS, str(st.label) ?? str(st.title) ?? "");
566
+ if (has("note") && str(st.note))
567
+ set("note", String(st.note));
568
+ usedStats = true;
569
+ }
570
+ }
571
+ if (str(content.title) !== void 0 && !take(TITLE_SLOTS, str(content.title))) {
572
+ if (has("quote") && !quote)
573
+ set("quote", String(content.title));
574
+ else
575
+ reject.push("has no title slot");
576
+ }
577
+ if (quote) {
578
+ if (has("quote"))
579
+ set("quote", quote.text);
580
+ else
581
+ reject.push("has no quote slot");
582
+ take(SOURCE_SLOTS, str(quote.source) ?? "");
583
+ }
584
+ take(SUB_SLOTS, str(content.subtitle));
585
+ if (has("eyebrow"))
586
+ set("eyebrow", str(content.eyebrow) ?? "");
587
+ if (has("footer"))
588
+ set("footer", ctx.footer ?? "");
589
+ if (has("pageno"))
590
+ set("pageno", String(ctx.pageNo).padStart(2, "0"));
591
+ if (has("index"))
592
+ set("index", str(content.index) ?? "");
593
+ take(["meta", "presenter"], str(content.meta) ?? "");
594
+ for (const s of singles) {
595
+ if (claimed.has(s))
596
+ continue;
597
+ set(s, "");
598
+ if (!CHROME_SLOT.test(s))
599
+ blanks.push(s);
600
+ }
601
+ if (points.length && !usedPoints)
602
+ reject.push(`has nowhere to put ${points.length} point(s)`);
603
+ if (stats.length && !usedStats)
604
+ reject.push(`has nowhere to put ${stats.length} number(s)`);
605
+ if (content.slots)
606
+ for (const [k, v] of Object.entries(content.slots)) {
607
+ if (!info.slots.includes(k))
608
+ reject.push(`unknown slot "${k}" for this layout`);
609
+ else {
610
+ slots[k] = v;
611
+ const b = blanks.indexOf(k);
612
+ if (b >= 0)
613
+ blanks.splice(b, 1);
614
+ }
615
+ }
616
+ if (blanks.length > MAX_BLANKS)
617
+ reject.push(`would leave ${blanks.length} slots empty (${blanks.join(", ")}) \u2014 content for a different layout`);
618
+ return { slots, ...rows ? { rows } : {}, ...fromChart && "options" in fromChart ? { chartOptions: fromChart.options } : {}, blanks, reject };
619
+ }
620
+ var SHAPES = {
621
+ parallel: /grid|cards|并列|要点|icon|bento|blocks|pillar/i,
622
+ process: /process|step|流程|步骤|serpentine/i,
623
+ sequence: /process|step|流程|步骤|serpentine/i,
624
+ timeline: /timeline|时间|历程|milestone|gantt|排期/i,
625
+ compare: /compar|versus|vs|对比|before|problem|问题|do & don/i,
626
+ hierarchy: /hierarch|pyramid|tree|层级|组织/i,
627
+ matrix: /quadrant|2x2|象限|matrix/i,
628
+ list: /list|清单|numbered/i,
629
+ metrics: /kpi|stat|number|指标|数字/i,
630
+ quote: /quote|金句|引言/i,
631
+ table: /table|数据表/i,
632
+ agenda: /agenda|议程|目录/i
633
+ };
634
+ var STRUCTURAL = /* @__PURE__ */ new Set(["cover", "toc", "section", "ending", "title", "transition"]);
635
+ function roleOf(info) {
636
+ return info.role.split(/[\s·]+/)[0];
637
+ }
638
+ function roleMatches(info, role) {
639
+ const r = roleOf(info);
640
+ if (role === "content")
641
+ return r === "content" || r === "pitch";
642
+ if (role === "section")
643
+ return r === "section" || r === "title" || r === "transition";
644
+ return r === role;
645
+ }
646
+ var needsImage = (info) => /\*\*/.test(info.hasImage) && /photo|image|media|map slot/.test(info.hasImage);
647
+ var takesImage = (info) => needsImage(info) || /optional/.test(info.hasImage);
648
+ function densityWanted(n) {
649
+ return n <= 2 ? "light" : n <= 4 ? "medium" : "dense";
650
+ }
651
+ function evaluate(info, content, ctx, deck, pageNo, intentRank) {
652
+ const cap = ctx.capacity ? resolveLayoutCapacity(ctx.capacity, info.id, { style: deck.style, pack: typeof deck.layoutPack === "string" && deck.layoutPack !== "none" ? deck.layoutPack : null }) : null;
653
+ const draft = draftSlide(info, content, { footer: typeof deck.footer === "string" ? deck.footer : void 0, pageNo, cap });
654
+ const errors = [...draft.reject];
655
+ const warnings = [];
656
+ if (cap && !errors.length) {
657
+ const r = checkSlideCapacity({ layout: info.id, slots: draft.slots, rows: draft.rows }, cap, info.defaults);
658
+ errors.push(...r.errors.map((f) => f.message));
659
+ warnings.push(...r.warnings.map((f) => f.message));
660
+ }
661
+ let score = 0;
662
+ if (intentRank >= 0)
663
+ score += 40 - 3 * intentRank;
664
+ const shape = content.shape ? SHAPES[content.shape] : void 0;
665
+ if (shape && shape.test(`${info.id} ${info.structure} ${info.pickWhen}`))
666
+ score += 12;
667
+ const n = Math.max(content.points?.length ?? 0, content.stats?.length ?? 0);
668
+ if (n && info.density.startsWith(densityWanted(n)))
669
+ score += 4;
670
+ if (content.image && takesImage(info))
671
+ score += 10;
672
+ if (draft.chartOptions)
673
+ score += 12;
674
+ score -= 4 * draft.blanks.length + 2 * warnings.length + info.order * 0.01;
675
+ return { layout: info.id, score: Math.round(score * 100) / 100, draft, warnings, errors };
676
+ }
677
+ function derivedChartMismatch(layout, chart) {
678
+ const r = rowsFromChart(layout, chart);
679
+ return "reject" in r ? r.reject : null;
680
+ }
681
+ function planDeck(deck, ctx) {
682
+ const v2 = deck.themeVersion === 2;
683
+ const used = /* @__PURE__ */ new Map();
684
+ const structuralPick = /* @__PURE__ */ new Map();
685
+ let prev = null;
686
+ const slides = [];
687
+ const specSlides = [];
688
+ (Array.isArray(deck.slides) ? deck.slides : []).forEach((content, i) => {
689
+ const role = content.role ?? "content";
690
+ const intent = content.intent ? ctx.intents?.[content.intent] : void 0;
691
+ const intentList = intent?.candidates ?? [];
692
+ let pool = ctx.layouts.filter((l) => content.layout ? l.id === content.layout : roleMatches(l, role));
693
+ const rejected = [];
694
+ if (content.layout && !pool.length)
695
+ rejected.push({ layout: content.layout, why: "unknown layout" });
696
+ pool = pool.filter((l) => {
697
+ const why = l.v2Only && !v2 ? "needs themeVersion: 2" : content.chart !== void 0 && !l.chart ? "has no chart slot" : content.chart !== void 0 && l.chart === "derived" ? derivedChartMismatch(l.id, content.chart) : content.chart === void 0 && l.chart === true ? "has a chart \u2014 give chart data or pick another" : content.image && !takesImage(l) ? "has no image slot" : !content.image && needsImage(l) ? "needs an image" : null;
698
+ if (why && content.layout)
699
+ rejected.push({ layout: l.id, why });
700
+ return !why;
701
+ });
702
+ const cands = pool.map((l) => evaluate(l, content, ctx, deck, i + 1, intentList.indexOf(l.id)));
703
+ const fits = cands.filter((c) => !c.errors.length).sort((a, b) => b.score - a.score || a.layout.localeCompare(b.layout));
704
+ for (const c of cands.filter((x) => x.errors.length).sort((a, b) => b.score - a.score))
705
+ rejected.push({ layout: c.layout, why: c.errors[0] });
706
+ const structural = STRUCTURAL.has(role);
707
+ let pick2;
708
+ const warnings = [];
709
+ if (structural && structuralPick.has(role))
710
+ pick2 = fits.find((c) => c.layout === structuralPick.get(role));
711
+ if (!pick2)
712
+ pick2 = structural ? fits[0] : fits.find((c) => c.layout !== prev && (used.get(c.layout) ?? 0) < 2);
713
+ if (!pick2 && fits.length) {
714
+ pick2 = fits[0];
715
+ warnings.push(`every fitting layout is already used twice / on the previous slide \u2014 repeating ${pick2.layout}`);
716
+ }
717
+ if (pick2) {
718
+ if (structural && !structuralPick.has(role))
719
+ structuralPick.set(role, pick2.layout);
720
+ used.set(pick2.layout, (used.get(pick2.layout) ?? 0) + 1);
721
+ prev = structural ? null : pick2.layout;
722
+ warnings.push(...pick2.warnings);
723
+ }
724
+ const spec = pick2 ? {
725
+ layout: pick2.layout,
726
+ ...content.intent && ctx.intents?.[content.intent] ? { intent: content.intent } : {},
727
+ slots: pick2.draft.slots,
728
+ ...pick2.draft.rows ? { rows: pick2.draft.rows } : {},
729
+ ...pick2.draft.chartOptions ? Object.keys(pick2.draft.chartOptions).length ? { chart: pick2.draft.chartOptions } : {} : content.chart !== void 0 ? { chart: content.chart } : {}
730
+ } : null;
731
+ if (spec)
732
+ specSlides.push(spec);
733
+ slides.push({
734
+ slide: i + 1,
735
+ pick: pick2?.layout ?? null,
736
+ spec,
737
+ warnings,
738
+ blanks: pick2?.draft.blanks ?? [],
739
+ alternatives: fits.filter((c) => c !== pick2).slice(0, 3).map((c) => ({ layout: c.layout, score: c.score })),
740
+ rejected: rejected.slice(0, 8)
741
+ });
742
+ });
743
+ const ok = slides.length > 0 && slides.every((s) => s.pick);
744
+ const { slides: _drop, footer: _footer, ...rest } = deck;
745
+ return {
746
+ ok,
747
+ slides,
748
+ spec: ok ? { ...rest, chartRender: deck.chartRender ?? "svg", slides: specSlides } : null
749
+ };
750
+ }
751
+
752
+ // apps/admin/data/skills.lib/document/bin-src/deck-plan.mjs
753
+ function die(msg, code = 1) {
754
+ process.stderr.write(`deck-plan: ${msg}
755
+ `);
756
+ process.exit(code);
757
+ }
758
+ async function main() {
759
+ const [planPath, outPath] = process.argv.slice(2).filter((a) => !a.startsWith("--"));
760
+ if (!planPath) die("usage: deck-plan <plan.json> [spec.json]", 64);
761
+ let plan;
762
+ try {
763
+ plan = JSON.parse(await (0, import_promises.readFile)(planPath, "utf8"));
764
+ } catch (e) {
765
+ die(`cannot read plan ${planPath}: ${e.message}`, 66);
766
+ }
767
+ if (!Array.isArray(plan?.slides) || !plan.slides.length) die("plan needs a non-empty slides array", 64);
768
+ const skillDir = (0, import_node_path.dirname)((0, import_node_path.dirname)(process.argv[1] || ""));
769
+ const layoutsDir = (0, import_node_path.join)(skillDir, "layouts");
770
+ const read = (f) => JSON.parse((0, import_node_fs.readFileSync)((0, import_node_path.join)(layoutsDir, f), "utf8"));
771
+ const slots = read("slots.json");
772
+ const profiles = (0, import_node_fs.existsSync)((0, import_node_path.join)(layoutsDir, "profiles.json")) ? read("profiles.json") : {};
773
+ const capacityPath = (0, import_node_path.join)(layoutsDir, "capacity.json");
774
+ const capacity = (0, import_node_fs.existsSync)(capacityPath) ? parseCapacityDoc(read("capacity.json")) : null;
775
+ if (!capacity) process.stderr.write("deck-plan: layouts/capacity.json missing \u2014 fit is checked by item count only\n");
776
+ const pack = typeof plan.layoutPack === "string" && plan.layoutPack !== "none" ? plan.layoutPack : null;
777
+ const tplOf = (id) => {
778
+ const own = pack ? (0, import_node_path.join)(skillDir, "packs", pack, "layouts", `${id}.tpl`) : null;
779
+ if (own && (0, import_node_fs.existsSync)(own)) return (0, import_node_fs.readFileSync)(own, "utf8");
780
+ const p = (0, import_node_path.join)(layoutsDir, `${id}.tpl`);
781
+ return (0, import_node_fs.existsSync)(p) ? (0, import_node_fs.readFileSync)(p, "utf8") : null;
782
+ };
783
+ const v2 = new Set(profiles.themeV2Layouts ?? []);
784
+ const layouts = [];
785
+ for (const row of parseLayoutTable((0, import_node_fs.readFileSync)((0, import_node_path.join)(layoutsDir, "README.md"), "utf8"))) {
786
+ const entry = slots[row.id];
787
+ const tpl = entry ? tplOf(row.id) : null;
788
+ if (!entry || !tpl) continue;
789
+ const defaults = templateDefaults(tpl);
790
+ layouts.push({ ...row, slots: Object.keys(defaults), rows: entry.rows, chart: entry.chart, defaults, v2Only: v2.has(row.id) });
791
+ }
792
+ const result = planDeck(plan, { layouts, capacity, intents: profiles.intents ?? {} });
793
+ if (outPath && result.ok) await (0, import_promises.writeFile)(outPath, JSON.stringify(result.spec, null, 1) + "\n", "utf8");
794
+ process.stdout.write(JSON.stringify({
795
+ ok: result.ok,
796
+ ...outPath ? { out: result.ok ? outPath : null } : {},
797
+ slides: result.slides.map(({ spec, ...s }) => outPath ? s : { ...s, spec }),
798
+ next: result.ok ? `deck-build ${outPath ?? "spec.json"} deck.html` : "fix the slides with pick:null (see rejected[].why): change the content's item count / length, or pin a layout and pass its slots"
799
+ }, null, 1) + "\n");
800
+ if (!result.ok) process.exit(1);
801
+ }
802
+ main().catch((e) => die(e.message, 1));