@taloswork/ose-shared 0.1.11 → 0.1.13
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/package.json +3 -3
- package/skills/document/SKILL.md +135 -219
- package/skills/document/art-directions/base.css +27 -2
- package/skills/document/art-directions/cinematic-depth.css +3 -1
- package/skills/document/art-directions/collage-paper.css +5 -1
- package/skills/document/art-directions/editorial-cut.css +5 -1
- package/skills/document/art-directions/geometric-pop.css +6 -0
- package/skills/document/art-directions/gradient-mesh.css +5 -1
- package/skills/document/art-directions/illustrated-doodle.css +5 -0
- package/skills/document/art-directions/luxury-ornament.css +4 -1
- package/skills/document/art-directions/maximalist-blocks.css +6 -0
- package/skills/document/art-directions/technical-grid.css +4 -0
- package/skills/document/assets/fonts/fonts.json +2 -2
- package/skills/document/assets/pdf-fonts/NotoSansSC-PDF-Bold.ttf +0 -0
- package/skills/document/assets/pdf-fonts/NotoSansSC-PDF-Regular.ttf +0 -0
- package/skills/document/assets/pdf-fonts/OFL.txt +93 -0
- package/skills/document/assets/pdf-fonts/README.md +35 -0
- package/skills/document/assets/tessdata/LICENSE-Apache-2.0.txt +202 -0
- package/skills/document/assets/tessdata/README.md +21 -0
- package/skills/document/assets/tessdata/chi_sim.traineddata.gz +0 -0
- package/skills/document/assets/tessdata/eng.traineddata.gz +0 -0
- package/skills/document/bin/deck-build.cjs +12563 -83
- package/skills/document/bin/deck-chart.cjs +771 -0
- package/skills/document/bin/deck-plan.cjs +12414 -0
- package/skills/document/bin/deck-qa.cjs +1075 -266
- package/skills/document/bin/deck-scenario.cjs +1363 -0
- package/skills/document/bin/deck-styles.cjs +1604 -0
- package/skills/document/bin/doc-convert.cjs +308 -292
- package/skills/document/bin/doc-fill.cjs +73264 -192
- package/skills/document/bin/doc-gen.cjs +2491 -312
- package/skills/document/bin/doc-run.cjs +162 -24
- package/skills/document/bin/doc-scan.cjs +13 -0
- package/skills/document/bin/doc-text.cjs +13 -0
- package/skills/document/bin/doc-validate.cjs +838 -68
- package/skills/document/bin/doc-xml.cjs +9 -5
- package/skills/document/bin/docx-annotate.cjs +95 -19
- package/skills/document/bin/docx-defaults.cjs +9907 -0
- package/skills/document/bin/fontkit.cjs +1 -0
- package/skills/document/bin/html2pptx.cjs +36622 -31394
- package/skills/document/bin/ocr-worker.cjs +1 -0
- package/skills/document/bin/ocr.cjs +131 -40
- package/skills/document/bin/pdf-form.cjs +682 -177
- package/skills/document/bin/pdf-images.cjs +13 -0
- package/skills/document/bin/pdf-ops.cjs +642 -199
- package/skills/document/bin/pdf-render.cjs +19 -3
- package/skills/document/bin/pdf-tables.cjs +13 -0
- package/skills/document/bin/pptx-clone.cjs +3 -3
- package/skills/document/bin/pptx-notes.cjs +10038 -0
- package/skills/document/bin/pptx-slide.cjs +3 -3
- package/skills/document/bin/pptx-tokenize.cjs +19 -4
- package/skills/document/bin/template-skin.cjs +10910 -0
- package/skills/document/bin/xlsx-query.cjs +192 -61
- package/skills/document/bin-reference.md +61 -33
- package/skills/document/catalog.json +33 -32
- package/skills/document/content-pipeline.md +179 -257
- package/skills/document/deck-design.md +378 -638
- package/skills/document/generated-visuals.md +99 -0
- package/skills/document/layouts/MAINTAINERS.md +33 -0
- package/skills/document/layouts/README.md +49 -36
- package/skills/document/layouts/big-stat.tpl +1 -1
- package/skills/document/layouts/capacity.json +1 -0
- package/skills/document/layouts/cards-3.tpl +1 -1
- package/skills/document/layouts/chart-full.tpl +1 -1
- package/skills/document/layouts/chart-kpi.tpl +1 -1
- package/skills/document/layouts/comparison.tpl +1 -1
- package/skills/document/layouts/data-table.tpl +1 -1
- package/skills/document/layouts/funnel.tpl +1 -1
- package/skills/document/layouts/gantt.tpl +1 -1
- package/skills/document/layouts/hub-flow.tpl +4 -0
- package/skills/document/layouts/icon-row.tpl +1 -1
- package/skills/document/layouts/image-grid.tpl +1 -1
- package/skills/document/layouts/matrix-2x2.tpl +1 -1
- package/skills/document/layouts/mock-qa.tpl +4 -0
- package/skills/document/layouts/profiles.json +2 -2
- package/skills/document/layouts/pyramid.tpl +1 -1
- package/skills/document/layouts/radar.tpl +1 -1
- package/skills/document/layouts/ring-progress.tpl +1 -1
- package/skills/document/layouts/slots.json +1 -1
- package/skills/document/layouts/steps-vertical.tpl +1 -1
- package/skills/document/layouts/steps.tpl +1 -1
- package/skills/document/layouts/three-kpi.tpl +1 -1
- package/skills/document/layouts/timeline.tpl +1 -1
- package/skills/document/layouts/toc-panel.tpl +2 -2
- package/skills/document/layouts/two-kpi.tpl +2 -2
- package/skills/document/layouts/versus.tpl +1 -1
- package/skills/document/motion/README.md +3 -2
- package/skills/document/office.md +3 -3
- package/skills/document/packs/README.md +1 -1
- package/skills/document/packs/board-brief/backdrop.html +2 -0
- package/skills/document/packs/board-brief/pack.css +2 -1
- package/skills/document/packs/mckinsey-photo/backdrop.html +2 -0
- package/skills/document/packs/mckinsey-photo/pack.css +2 -1
- package/skills/document/packs/press-room/backdrop.html +2 -0
- package/skills/document/packs/press-room/pack.css +2 -1
- package/skills/document/packs/prismatic-3d/backdrop.html +2 -0
- package/skills/document/packs/prismatic-3d/pack.css +4 -1
- package/skills/document/pdf.md +60 -43
- package/skills/document/rewrite.md +257 -309
- package/skills/document/scenarios.json +1307 -0
- package/skills/document/scenarios.schema.json +104 -0
- package/skills/document/styles/README.md +15 -25
- package/skills/document/styles/_backdrops/rimlight.html +3 -2
- package/skills/document/styles/_contract-defaults.css +31 -2
- package/skills/document/styles/_samples/_groups/biz-premium.webp +0 -0
- package/skills/document/styles/_samples/mckinsey.webp +0 -0
- package/skills/document/styles/anthropic/skin.css +6 -3
- package/skills/document/styles/apple/skin.css +4 -3
- package/skills/document/styles/bistro-warm/backdrop.html +2 -0
- package/skills/document/styles/bistro-warm/skin.css +4 -1
- package/skills/document/styles/black-gold/skin.css +6 -5
- package/skills/document/styles/bold-clash/backdrop.html +2 -0
- package/skills/document/styles/bold-clash/skin.css +2 -1
- package/skills/document/styles/branding/skin.css +4 -4
- package/skills/document/styles/campus-teal/backdrop.html +2 -0
- package/skills/document/styles/campus-teal/skin.css +2 -1
- package/skills/document/styles/cinereel/skin.css +7 -3
- package/skills/document/styles/clinic-care/backdrop.html +2 -0
- package/skills/document/styles/clinic-care/skin.css +2 -1
- package/skills/document/styles/cool/skin.css +6 -5
- package/skills/document/styles/dark-glass/backdrop.html +2 -0
- package/skills/document/styles/dark-glass/skin.css +2 -1
- package/skills/document/styles/gov-brief/backdrop.html +2 -0
- package/skills/document/styles/gov-brief/skin.css +2 -1
- package/skills/document/styles/grid-tech/backdrop.html +2 -0
- package/skills/document/styles/grid-tech/skin.css +2 -1
- package/skills/document/styles/hand-drawn/skin.css +3 -3
- package/skills/document/styles/ivory-gold/skin.css +7 -6
- package/skills/document/styles/legal-slate/backdrop.html +2 -0
- package/skills/document/styles/legal-slate/skin.css +2 -1
- package/skills/document/styles/magazine-ink/skin.css +4 -3
- package/skills/document/styles/mckinsey/backdrop.html +7 -5
- package/skills/document/styles/mckinsey/skin.css +2 -1
- package/skills/document/styles/memphis/backdrop.html +2 -0
- package/skills/document/styles/memphis/skin.css +2 -1
- package/skills/document/styles/mono/skin.css +6 -2
- package/skills/document/styles/neon-cyber/backdrop.html +2 -0
- package/skills/document/styles/neon-cyber/skin.css +2 -1
- package/skills/document/styles/olive-gold/skin.css +7 -6
- package/skills/document/styles/organic-earth/backdrop.html +2 -0
- package/skills/document/styles/organic-earth/skin.css +2 -1
- package/skills/document/styles/pastel-saas/backdrop.html +2 -0
- package/skills/document/styles/pastel-saas/skin.css +2 -1
- package/skills/document/styles/portfolio-noir/backdrop.html +2 -0
- package/skills/document/styles/portfolio-noir/skin.css +2 -1
- package/skills/document/styles/retro-press/backdrop.html +2 -0
- package/skills/document/styles/retro-press/skin.css +2 -1
- package/skills/document/styles/sport-arena/backdrop.html +2 -0
- package/skills/document/styles/sport-arena/skin.css +4 -1
- package/skills/document/styles/swiss-grid/skin.css +10 -5
- package/skills/document/styles/wedding-blush/backdrop.html +2 -0
- package/skills/document/styles/wedding-blush/skin.css +4 -1
- package/skills/document/theme-system.md +85 -77
- package/skills/document/themes/README.md +7 -5
- package/skills/document/vision.md +8 -5
- package/skills/document/xlsx.md +15 -25
- package/skills/grill-me/SKILL.md +6 -0
- package/skills/grill-me/agents/openai.yaml +5 -0
- package/skills/grilling/SKILL.md +28 -0
- package/skills/grilling/agents/openai.yaml +3 -0
|
@@ -12683,7 +12683,7 @@ var require_xlsx = __commonJS({
|
|
|
12683
12683
|
var s = o.dense ? [] : {}, n = "Sheet1", next_n = "", sidx = 0;
|
|
12684
12684
|
var sheets = {}, snames = [], realnames = [];
|
|
12685
12685
|
var refguess = { s: { r: 0, c: 0 }, e: { r: 0, c: 0 } };
|
|
12686
|
-
var
|
|
12686
|
+
var sheetRows = o.sheetRows || 0;
|
|
12687
12687
|
if (d[2] == 0) {
|
|
12688
12688
|
if (d[3] == 8 || d[3] == 9) {
|
|
12689
12689
|
if (d.length >= 16 && d[14] == 5 && d[15] === 108) throw new Error("Unsupported Works 3 for Mac file");
|
|
@@ -12789,7 +12789,7 @@ var require_xlsx = __commonJS({
|
|
|
12789
12789
|
sidx = val[3];
|
|
12790
12790
|
n = "Sheet" + (sidx + 1);
|
|
12791
12791
|
}
|
|
12792
|
-
if (
|
|
12792
|
+
if (sheetRows > 0 && val[0].r >= sheetRows) break;
|
|
12793
12793
|
if (o.dense) {
|
|
12794
12794
|
if (!s[val[0].r]) s[val[0].r] = [];
|
|
12795
12795
|
s[val[0].r][val[0].c] = val[1];
|
|
@@ -29309,7 +29309,7 @@ var require_xlsx = __commonJS({
|
|
|
29309
29309
|
}
|
|
29310
29310
|
}
|
|
29311
29311
|
var rows = table.getElementsByTagName("tr");
|
|
29312
|
-
var
|
|
29312
|
+
var sheetRows = Math.min(opts.sheetRows || 1e7, rows.length);
|
|
29313
29313
|
var range = { s: { r: 0, c: 0 }, e: { r: or_R, c: or_C } };
|
|
29314
29314
|
if (ws["!ref"]) {
|
|
29315
29315
|
var _range = decode_range(ws["!ref"]);
|
|
@@ -29323,7 +29323,7 @@ var require_xlsx = __commonJS({
|
|
|
29323
29323
|
var rowinfo = ws["!rows"] || (ws["!rows"] = []);
|
|
29324
29324
|
var _R = 0, R = 0, _C = 0, C = 0, RS = 0, CS = 0;
|
|
29325
29325
|
if (!ws["!cols"]) ws["!cols"] = [];
|
|
29326
|
-
for (; _R < rows.length && R <
|
|
29326
|
+
for (; _R < rows.length && R < sheetRows; ++_R) {
|
|
29327
29327
|
var row = rows[_R];
|
|
29328
29328
|
if (is_dom_element_hidden(row)) {
|
|
29329
29329
|
if (opts.display) continue;
|
|
@@ -29379,7 +29379,7 @@ var require_xlsx = __commonJS({
|
|
|
29379
29379
|
if (merges.length) ws["!merges"] = (ws["!merges"] || []).concat(merges);
|
|
29380
29380
|
range.e.r = Math.max(range.e.r, R - 1 + or_R);
|
|
29381
29381
|
ws["!ref"] = encode_range(range);
|
|
29382
|
-
if (R >=
|
|
29382
|
+
if (R >= sheetRows) ws["!fullref"] = encode_range((range.e.r = rows.length - _R + R - 1 + or_R, range));
|
|
29383
29383
|
return ws;
|
|
29384
29384
|
}
|
|
29385
29385
|
function parse_dom_table(table, _opts) {
|
|
@@ -33253,30 +33253,104 @@ var import_promises = require("node:fs/promises");
|
|
|
33253
33253
|
|
|
33254
33254
|
// packages/core/dist/document/xlsx-query.js
|
|
33255
33255
|
var XLSX = __toESM(require_xlsx(), 1);
|
|
33256
|
+
var AGGREGATE_OPS = ["sum", "avg", "min", "max", "count", "count-nonempty"];
|
|
33256
33257
|
function parseWorkbook(buffer) {
|
|
33257
33258
|
const data = buffer instanceof Uint8Array ? buffer : new Uint8Array(buffer);
|
|
33258
33259
|
return XLSX.read(data, { type: "array" });
|
|
33259
33260
|
}
|
|
33260
|
-
|
|
33261
|
+
var hasValue = (c) => c != null && c.t !== "z" && c.v != null;
|
|
33262
|
+
function sheetGrid(wb, sheetName) {
|
|
33261
33263
|
const ws = wb.Sheets[sheetName];
|
|
33262
|
-
if (!ws)
|
|
33263
|
-
return [];
|
|
33264
|
-
|
|
33264
|
+
if (!ws || !ws["!ref"])
|
|
33265
|
+
return { rows: [], width: 0, firstCol: 0 };
|
|
33266
|
+
const range = XLSX.utils.decode_range(ws["!ref"]);
|
|
33267
|
+
const rows = [];
|
|
33268
|
+
for (let r = range.s.r; r <= range.e.r; r++) {
|
|
33269
|
+
const cells = [];
|
|
33270
|
+
for (let c = range.s.c; c <= range.e.c; c++)
|
|
33271
|
+
cells.push(ws[XLSX.utils.encode_cell({ r, c })]);
|
|
33272
|
+
if (cells.some(hasValue))
|
|
33273
|
+
rows.push({ row: r + 1, cells });
|
|
33274
|
+
}
|
|
33275
|
+
return { rows, width: rows.length ? range.e.c - range.s.c + 1 : 0, firstCol: range.s.c };
|
|
33276
|
+
}
|
|
33277
|
+
function parseNumberText(text) {
|
|
33278
|
+
let t = text.replace(/[¥¥$€£,\s]/g, "").replace(/元$/, "");
|
|
33279
|
+
let sign = 1;
|
|
33280
|
+
const paren = /^\((.*)\)$/.exec(t);
|
|
33281
|
+
if (paren) {
|
|
33282
|
+
sign = -1;
|
|
33283
|
+
t = paren[1];
|
|
33284
|
+
}
|
|
33285
|
+
let scale = 1;
|
|
33286
|
+
if (t.endsWith("%")) {
|
|
33287
|
+
scale = 0.01;
|
|
33288
|
+
t = t.slice(0, -1);
|
|
33289
|
+
}
|
|
33290
|
+
if (t === "" || !/^[+-]?(\d+\.?\d*|\.\d+)(e[+-]?\d+)?$/i.test(t))
|
|
33291
|
+
return null;
|
|
33292
|
+
const n = Number(t);
|
|
33293
|
+
return Number.isFinite(n) ? cleanFloat(sign * n * scale) : null;
|
|
33294
|
+
}
|
|
33295
|
+
function cellNumber(c) {
|
|
33296
|
+
if (!hasValue(c))
|
|
33297
|
+
return null;
|
|
33298
|
+
if (c.t === "n")
|
|
33299
|
+
return typeof c.v === "number" && Number.isFinite(c.v) ? c.v : null;
|
|
33300
|
+
if (c.t === "s")
|
|
33301
|
+
return parseNumberText(String(c.v));
|
|
33302
|
+
return null;
|
|
33303
|
+
}
|
|
33304
|
+
var isEmptyCell = (c) => !hasValue(c) || String(c.v).trim() === "";
|
|
33305
|
+
var isTextCell = (c) => !isEmptyCell(c) && c.t === "s" && cellNumber(c) == null;
|
|
33306
|
+
function cleanFloat(v) {
|
|
33307
|
+
if (!Number.isFinite(v) || v === 0)
|
|
33308
|
+
return v;
|
|
33309
|
+
return Number(v.toPrecision(15));
|
|
33310
|
+
}
|
|
33311
|
+
var HEADER_SCAN_ROWS = 10;
|
|
33312
|
+
function detectHeaderIndex(grid) {
|
|
33313
|
+
const { rows, width } = grid;
|
|
33314
|
+
for (let k = 0; k < Math.min(HEADER_SCAN_ROWS, rows.length - 1); k++) {
|
|
33315
|
+
const cells = rows[k].cells;
|
|
33316
|
+
const filled = cells.filter((c) => !isEmptyCell(c));
|
|
33317
|
+
if (!filled.length || filled.length * 2 <= width)
|
|
33318
|
+
continue;
|
|
33319
|
+
if (cells.filter(isTextCell).length * 2 < filled.length)
|
|
33320
|
+
continue;
|
|
33321
|
+
if (rows[k + 1].cells.some((c) => cellNumber(c) != null))
|
|
33322
|
+
return k;
|
|
33323
|
+
}
|
|
33324
|
+
return 0;
|
|
33265
33325
|
}
|
|
33266
|
-
function
|
|
33267
|
-
|
|
33268
|
-
|
|
33269
|
-
|
|
33326
|
+
function layout(grid, opts) {
|
|
33327
|
+
if (!grid.rows.length)
|
|
33328
|
+
return { headerRow: opts.headerRow ?? 1, headers: [], data: [] };
|
|
33329
|
+
let headerCells;
|
|
33330
|
+
let headerRow;
|
|
33331
|
+
let data;
|
|
33332
|
+
if (opts.headerRow != null) {
|
|
33333
|
+
headerRow = opts.headerRow;
|
|
33334
|
+
headerCells = grid.rows.find((r) => r.row === headerRow)?.cells ?? [];
|
|
33335
|
+
data = grid.rows.filter((r) => r.row > headerRow);
|
|
33336
|
+
} else {
|
|
33337
|
+
const k = detectHeaderIndex(grid);
|
|
33338
|
+
headerRow = grid.rows[k].row;
|
|
33339
|
+
headerCells = grid.rows[k].cells;
|
|
33340
|
+
data = grid.rows.slice(k + 1);
|
|
33341
|
+
}
|
|
33342
|
+
const headers = Array.from({ length: grid.width }, (_, i) => {
|
|
33343
|
+
const c = headerCells[i];
|
|
33344
|
+
const s = hasValue(c) ? String(c.v).trim() : "";
|
|
33270
33345
|
return s.length > 0 ? s : `col${i + 1}`;
|
|
33271
33346
|
});
|
|
33347
|
+
return { headerRow, headers, data };
|
|
33272
33348
|
}
|
|
33273
|
-
function summarizeWorkbook(wb) {
|
|
33349
|
+
function summarizeWorkbook(wb, opts = {}) {
|
|
33274
33350
|
const sheets = wb.SheetNames.map((name) => {
|
|
33275
|
-
const
|
|
33276
|
-
const
|
|
33277
|
-
|
|
33278
|
-
const cols = rows.reduce((m, r) => Math.max(m, r.length), 0);
|
|
33279
|
-
return { name, rows: dataRows, cols, headers };
|
|
33351
|
+
const grid = sheetGrid(wb, name);
|
|
33352
|
+
const { headerRow, headers, data } = layout(grid, opts);
|
|
33353
|
+
return { name, rows: data.length, cols: grid.width, headerRow, headers };
|
|
33280
33354
|
});
|
|
33281
33355
|
return { sheets };
|
|
33282
33356
|
}
|
|
@@ -33289,54 +33363,95 @@ function resolveColumnIndex(headers, column) {
|
|
|
33289
33363
|
return asNum - 1;
|
|
33290
33364
|
return -1;
|
|
33291
33365
|
}
|
|
33292
|
-
|
|
33293
|
-
|
|
33294
|
-
|
|
33295
|
-
|
|
33296
|
-
|
|
33297
|
-
|
|
33298
|
-
|
|
33299
|
-
|
|
33300
|
-
|
|
33366
|
+
var TOTAL_LABEL = /^(合计|总计|小计|total|subtotal|sum)$/i;
|
|
33367
|
+
function totalReason(row, colIdx, colLetter) {
|
|
33368
|
+
for (const c of row.cells) {
|
|
33369
|
+
if (!hasValue(c) || c.t !== "s")
|
|
33370
|
+
continue;
|
|
33371
|
+
const label = String(c.v).replace(/\s+/g, "").replace(/[::]$/, "");
|
|
33372
|
+
if (TOTAL_LABEL.test(label))
|
|
33373
|
+
return `total label "${String(c.v).trim()}"`;
|
|
33374
|
+
}
|
|
33375
|
+
const f = row.cells[colIdx]?.f;
|
|
33376
|
+
if (f) {
|
|
33377
|
+
const m = /^\s*=?\s*SUM\(\s*\$?([A-Z]+)\$?\d+\s*:\s*\$?([A-Z]+)\$?\d+\s*\)\s*$/i.exec(f);
|
|
33378
|
+
if (m && m[1].toUpperCase() === colLetter && m[2].toUpperCase() === colLetter)
|
|
33379
|
+
return `${f.trim().replace(/^=/, "")} over this column`;
|
|
33301
33380
|
}
|
|
33302
33381
|
return null;
|
|
33303
33382
|
}
|
|
33304
|
-
|
|
33305
|
-
|
|
33306
|
-
const
|
|
33307
|
-
const
|
|
33308
|
-
|
|
33309
|
-
|
|
33310
|
-
|
|
33311
|
-
|
|
33312
|
-
|
|
33313
|
-
|
|
33383
|
+
var SKIPPED_CELLS_CAP = 20;
|
|
33384
|
+
function aggregateColumn(wb, sheetName, column, op, opts = {}) {
|
|
33385
|
+
const grid = sheetGrid(wb, sheetName);
|
|
33386
|
+
const { headerRow, headers, data } = layout(grid, opts);
|
|
33387
|
+
const colIdx = headers.length > 0 ? resolveColumnIndex(headers, column) : -1;
|
|
33388
|
+
const result = {
|
|
33389
|
+
sheet: sheetName,
|
|
33390
|
+
column,
|
|
33391
|
+
op,
|
|
33392
|
+
headerRow,
|
|
33393
|
+
colIdx,
|
|
33394
|
+
rowsScanned: 0,
|
|
33395
|
+
numericCells: 0,
|
|
33396
|
+
value: null,
|
|
33397
|
+
excludedRows: [],
|
|
33398
|
+
skippedCells: [],
|
|
33399
|
+
skippedCellCount: 0
|
|
33400
|
+
};
|
|
33401
|
+
if (colIdx === -1) {
|
|
33402
|
+
result.rowsScanned = data.length;
|
|
33403
|
+
return result;
|
|
33404
|
+
}
|
|
33405
|
+
const colLetter = XLSX.utils.encode_col(grid.firstCol + colIdx);
|
|
33406
|
+
const kept = [];
|
|
33407
|
+
for (const r of data) {
|
|
33408
|
+
const reason = opts.includeTotals ? null : totalReason(r, colIdx, colLetter);
|
|
33409
|
+
if (reason)
|
|
33410
|
+
result.excludedRows.push({ row: r.row, reason });
|
|
33411
|
+
else
|
|
33412
|
+
kept.push(r);
|
|
33413
|
+
}
|
|
33414
|
+
result.rowsScanned = kept.length;
|
|
33314
33415
|
if (op === "count") {
|
|
33315
|
-
|
|
33416
|
+
result.value = kept.length;
|
|
33417
|
+
return result;
|
|
33316
33418
|
}
|
|
33419
|
+
const cells = kept.map((r) => ({ row: r.row, cell: r.cells[colIdx] }));
|
|
33317
33420
|
if (op === "count-nonempty") {
|
|
33318
|
-
|
|
33319
|
-
return
|
|
33421
|
+
result.value = cells.filter(({ cell }) => !isEmptyCell(cell)).length;
|
|
33422
|
+
return result;
|
|
33320
33423
|
}
|
|
33321
|
-
const nums =
|
|
33424
|
+
const nums = [];
|
|
33425
|
+
for (const { row, cell } of cells) {
|
|
33426
|
+
if (isEmptyCell(cell))
|
|
33427
|
+
continue;
|
|
33428
|
+
const n = cellNumber(cell);
|
|
33429
|
+
if (n != null) {
|
|
33430
|
+
nums.push(n);
|
|
33431
|
+
continue;
|
|
33432
|
+
}
|
|
33433
|
+
result.skippedCellCount++;
|
|
33434
|
+
if (result.skippedCells.length < SKIPPED_CELLS_CAP)
|
|
33435
|
+
result.skippedCells.push({ row, value: cell.v });
|
|
33436
|
+
}
|
|
33437
|
+
result.numericCells = nums.length;
|
|
33322
33438
|
if (nums.length === 0)
|
|
33323
|
-
return
|
|
33324
|
-
let value;
|
|
33439
|
+
return result;
|
|
33325
33440
|
switch (op) {
|
|
33326
33441
|
case "sum":
|
|
33327
|
-
value = nums.reduce((a, b) => a + b, 0);
|
|
33442
|
+
result.value = cleanFloat(nums.reduce((a, b) => a + b, 0));
|
|
33328
33443
|
break;
|
|
33329
33444
|
case "avg":
|
|
33330
|
-
value = nums.reduce((a, b) => a + b, 0) / nums.length;
|
|
33445
|
+
result.value = cleanFloat(nums.reduce((a, b) => a + b, 0) / nums.length);
|
|
33331
33446
|
break;
|
|
33332
33447
|
case "min":
|
|
33333
|
-
value = Math.min(...nums);
|
|
33448
|
+
result.value = Math.min(...nums);
|
|
33334
33449
|
break;
|
|
33335
33450
|
case "max":
|
|
33336
|
-
value = Math.max(...nums);
|
|
33451
|
+
result.value = Math.max(...nums);
|
|
33337
33452
|
break;
|
|
33338
33453
|
}
|
|
33339
|
-
return
|
|
33454
|
+
return result;
|
|
33340
33455
|
}
|
|
33341
33456
|
var COMPUTE_KEYWORDS_EN = [
|
|
33342
33457
|
"sum",
|
|
@@ -33356,15 +33471,13 @@ var COMPUTE_KEYWORDS_EN = [
|
|
|
33356
33471
|
"percentage"
|
|
33357
33472
|
];
|
|
33358
33473
|
var COMPUTE_EN_RE = new RegExp(`\\b(${COMPUTE_KEYWORDS_EN.map((k) => k.replace(/ /g, "\\s+")).join("|")})\\b`, "i");
|
|
33359
|
-
function sampleRows(wb, sheetName, limit = 20) {
|
|
33360
|
-
const
|
|
33361
|
-
|
|
33362
|
-
return [];
|
|
33363
|
-
const headers = headerRow(rows);
|
|
33364
|
-
return rows.slice(1, 1 + Math.max(0, limit)).map((r) => {
|
|
33474
|
+
function sampleRows(wb, sheetName, limit = 20, opts = {}) {
|
|
33475
|
+
const { headers, data } = layout(sheetGrid(wb, sheetName), opts);
|
|
33476
|
+
return data.slice(0, Math.max(0, limit)).map((r) => {
|
|
33365
33477
|
const obj = {};
|
|
33366
33478
|
headers.forEach((h, i) => {
|
|
33367
|
-
|
|
33479
|
+
const c = r.cells[i];
|
|
33480
|
+
obj[h] = hasValue(c) ? c.v : "";
|
|
33368
33481
|
});
|
|
33369
33482
|
return obj;
|
|
33370
33483
|
});
|
|
@@ -33379,9 +33492,24 @@ function die(msg, code = 1) {
|
|
|
33379
33492
|
function out(obj) {
|
|
33380
33493
|
process.stdout.write(JSON.stringify(obj, null, 2) + "\n");
|
|
33381
33494
|
}
|
|
33495
|
+
var USAGE = "usage: xlsx-query <file> [--summary | --agg <sheet> <col> <op> | --sample <sheet> [n]] [--header-row N] [--include-totals]";
|
|
33496
|
+
function parseFlags(argv) {
|
|
33497
|
+
const opts = {};
|
|
33498
|
+
const positional = [];
|
|
33499
|
+
for (let i = 0; i < argv.length; i++) {
|
|
33500
|
+
if (argv[i] === "--include-totals") opts.includeTotals = true;
|
|
33501
|
+
else if (argv[i] === "--header-row") {
|
|
33502
|
+
const n = Number(argv[++i]);
|
|
33503
|
+
if (!Number.isInteger(n) || n < 1) die("--header-row needs a 1-based row number", 64);
|
|
33504
|
+
opts.headerRow = n;
|
|
33505
|
+
} else positional.push(argv[i]);
|
|
33506
|
+
}
|
|
33507
|
+
return { opts, positional };
|
|
33508
|
+
}
|
|
33382
33509
|
async function main() {
|
|
33383
|
-
const
|
|
33384
|
-
|
|
33510
|
+
const { opts, positional } = parseFlags(process.argv.slice(2));
|
|
33511
|
+
const [file, mode, ...rest] = positional;
|
|
33512
|
+
if (!file) die(USAGE, 64);
|
|
33385
33513
|
let raw;
|
|
33386
33514
|
try {
|
|
33387
33515
|
raw = await (0, import_promises.readFile)(file);
|
|
@@ -33395,19 +33523,22 @@ async function main() {
|
|
|
33395
33523
|
die(`parse failed: ${e.message}`);
|
|
33396
33524
|
}
|
|
33397
33525
|
if (!mode || mode === "--summary") {
|
|
33398
|
-
out(summarizeWorkbook(wb));
|
|
33526
|
+
out(summarizeWorkbook(wb, opts));
|
|
33399
33527
|
return;
|
|
33400
33528
|
}
|
|
33401
33529
|
if (mode === "--agg") {
|
|
33402
33530
|
const [sheet, col, op] = rest;
|
|
33403
33531
|
if (!sheet || !col || !op) die("usage: --agg <sheet> <col> <op>", 64);
|
|
33404
|
-
|
|
33532
|
+
if (!AGGREGATE_OPS.includes(op)) die(`unknown op "${op}" \u2014 use one of: ${AGGREGATE_OPS.join(", ")}`, 64);
|
|
33533
|
+
if (!wb.SheetNames.includes(sheet)) die(`no sheet "${sheet}" \u2014 the workbook has: ${wb.SheetNames.join(", ")}`);
|
|
33534
|
+
out(aggregateColumn(wb, sheet, col, op, opts));
|
|
33405
33535
|
return;
|
|
33406
33536
|
}
|
|
33407
33537
|
if (mode === "--sample") {
|
|
33408
33538
|
const [sheet, n] = rest;
|
|
33409
33539
|
if (!sheet) die("usage: --sample <sheet> [n]", 64);
|
|
33410
|
-
|
|
33540
|
+
if (!wb.SheetNames.includes(sheet)) die(`no sheet "${sheet}" \u2014 the workbook has: ${wb.SheetNames.join(", ")}`);
|
|
33541
|
+
out({ sheet, sample: sampleRows(wb, sheet, Math.max(0, Number(n) || 20), opts) });
|
|
33411
33542
|
return;
|
|
33412
33543
|
}
|
|
33413
33544
|
die(`unknown mode: ${mode}`, 64);
|
|
@@ -1,35 +1,63 @@
|
|
|
1
1
|
# Script reference (L3)
|
|
2
2
|
|
|
3
|
-
Every bin in `bin/`,
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
- `bin/
|
|
17
|
-
- `bin/doc-
|
|
18
|
-
- `bin/
|
|
19
|
-
- `bin/
|
|
20
|
-
- `bin/
|
|
21
|
-
- `bin/
|
|
22
|
-
- `bin/
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
- `bin/
|
|
27
|
-
- `bin/
|
|
28
|
-
- `bin/xlsx-recalc.cjs <in.xlsx> <out.xlsx>` — re-evaluate
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
3
|
+
Every bin in `bin/`, grouped by task. **Read only your task's section** — Read this
|
|
4
|
+
file with the offset/limit of the line range below; another section only when you call
|
|
5
|
+
a bin from it. Exit codes are shared: `64` bad usage · `65` invalid input · `66`
|
|
6
|
+
unreadable file · `69` a required engine is unavailable · `75` transient, retry · `1` failure.
|
|
7
|
+
|
|
8
|
+
- **Read** (lines 14–22) — doc-scan · doc-text · pdf-render · pdf-images · pdf-tables · ocr · xlsx-query
|
|
9
|
+
- **Generate** (lines 24–28) — doc-gen · doc-run · xlsx-recalc
|
|
10
|
+
- **Fill and edit** (lines 30–44) — doc-fill · doc-xml · docx-annotate · docx-defaults · pptx-clone · pptx-slide · pptx-tokenize · pptx-set-image · pptx-notes · pptx-theme · pptx-embed-fonts · pdf-form · pdf-ops
|
|
11
|
+
- **Decks** (lines 46–58) — deck-scenario · deck-styles · deck-plan · deck-build · deck-chart · html2pptx · deck-qa · template-skin
|
|
12
|
+
- **Validate and convert** (lines 60–63) — doc-validate · doc-convert
|
|
13
|
+
|
|
14
|
+
## Read
|
|
15
|
+
|
|
16
|
+
- `bin/doc-scan.cjs <file.pdf>` — per-page signals: `{file, pages:[{page, textChars, imageCoverage, mathHint, tableHint, scanned, …}]}`. PDF only. Every PDF reader here (doc-scan, doc-text, pdf-render, pdf-images, pdf-tables) exits 1 on a password-protected PDF — see pdf.md.
|
|
17
|
+
- `bin/doc-text.cjs <file.pdf>` — the PDF's text layer, `[page N]`-delimited. A .docx/.pptx/.xlsx exits 64 (read the sidecar / use xlsx-query).
|
|
18
|
+
- `bin/pdf-render.cjs <file.pdf> <pages|all> [scale]` — render pages to PNG for vision, written beside the PDF as `<stem>_p<N>.png` (prints `{rendered:[{page, path}]}`); `pages` = `3` | `2-5` | `3,7-8` | `all`. `scale` 0.5–4 (default `2` ≈ 144dpi) trades resolution for vision tokens: `1` (≈72dpi, ¼ the tokens) for layout QA; keep `2` when fine print must stay legible (formulas/tables).
|
|
19
|
+
- `bin/pdf-images.cjs <file.pdf> [pages]` — EMBEDDED images at native resolution, written beside the PDF as `<stem>_p<page>_i<index>.png`, plus a manifest `{images:[{page, index, bbox, width, height, path}], skipped}` (`pages` = `N` | `N-M` | `all`, default all). Use this — not a full-page `pdf-render` — when you need the original figures as their own files (e.g. rebuilding a deck). Vector/text-only pages yield nothing → `pdf-render`.
|
|
20
|
+
- `bin/pdf-tables.cjs <file.pdf> [page] [minCols]` — text-layer tables as `{tables:[{page, rows}]}` (`minCols` default 2; rows may carry empty spacer cells). Nothing found, or a ruled/scanned table → render + vision.
|
|
21
|
+
- `bin/ocr.cjs <image> [lang]` — text off a png/jpg/webp/bmp image → `{text, confidence}`; for a scanned PDF `pdf-render` the page first. `lang` is a tesseract code, default `eng`; `chi_sim` = simplified Chinese, `chi_tra` traditional, `+` combines (`chi_sim+eng` for mixed text). `eng` and `chi_sim` ship with the skill and work offline; others download once into `$TALOS_TESSDATA` (no network → exit 1 `OCR failed: …`); `$TALOS_TESSDATA_LANGPATH` names a dir holding every `<lang>.traineddata.gz`. **Prefer vision** when a vision model is available; OCR is the text-only fallback.
|
|
22
|
+
- `bin/xlsx-query.cjs <file> [--summary | --agg <sheet> <col> <op> | --sample <sheet> [n]] [--header-row N] [--include-totals]` — exact spreadsheet compute; no mode = `--summary` (sheet names, row/column counts, `headerRow`, headers). `<op>` = `sum` | `avg` | `min` | `max` | `count` | `count-nonempty` (anything else exits 64 listing these); `<col>` is a header label or a 1-based index. The header row is detected (a title row is skipped; `--header-row N` overrides). `--agg` excludes total rows (合计/总计/小计/Total/Subtotal/Sum, or a SUM over the column) and reports them in `excludedRows` (`--include-totals` keeps them), and lists non-numeric cells it skipped in `skippedCells`. `15%` text is 0.15; `¥`/`元` amounts parse. An unknown sheet exits 1 naming the sheets; a column it cannot find returns `value: null, colIdx: -1` — run `--summary` for the exact names. `--sample` prints up to `n` (default 20) rows keyed by header. Caveats: xlsx.md.
|
|
23
|
+
|
|
24
|
+
## Generate
|
|
25
|
+
|
|
26
|
+
- `bin/doc-gen.cjs <docx|xlsx|pdf> <out> <spec.json> [--theme <theme.json|name>] [--topic <subject> | --palette <id>] [--accent <#rrggbb> | --style <id>] [--no-preview]` — generate documents from scratch; spec shapes in rewrite.md "Generate". DOCX/PDF body fields are exactly `paragraphs` and/or `blocks` (never `sections`); in a PDF, any http(s) URL written in body text, list items or table cells becomes a clickable link and wraps inside the page on its own — never split a URL by hand (there is no other link syntax). Unknown fields and empty bodies exit 65 with `DOC_SPEC_INVALID` before any output is written (`allowCoverOnly:true` is the explicit title-only opt-in): correct the reported JSON path and rerun. Prints `wrote <out>` (a PDF adds its engine) then one JSON line — the self-check in SKILL.md "After writing a file"; PDF previews land beside it as `<stem>_p<N>.png` (`--no-preview` skips them). A PDF prints in Chromium — the app's service, else a headless Chromium; with neither it exits 69. `doc-gen pptx` exits 64: decks are built with html2pptx. **Look flags** (docx/pdf): `--theme` = a theme file (built-ins `<skill>/themes/<id>.json`) or the name of one the user saved in `$TALOS_DOC_STYLES_DIR/themes`; `--topic <subject>` picks a topic-adaptive palette, `--palette <id>` forces one (`doc-gen --list-palettes`); `--accent <#rrggbb>` / `--style <id>` keep white paper and near-black text with ONE brand colour on H1/H2 + a faint table-header tint — `--style` takes it from a catalog skin or pack (the deck picker's ids; the deck's full palette is NOT carried onto paper); an accent that is not 6-digit hex falls back to black-on-white with a warning. Precedence: `--theme` > spec `"theme"` > `--accent`/`--style` > `--palette`/`--topic` > default.
|
|
27
|
+
- `bin/doc-run.cjs <script.js> [args…]` — run a custom Node script with the engine's libraries bundled in (`require("pptxgenjs"|"docx"|"xlsx"|"exceljs"|"pdf-lib"|"jszip"|"pizzip"|"hyperformula")` guaranteed on any machine; `--list` prints the registry; nothing else is installed, and an unbundled `require` fails naming the bundled alternative). For a **formatted workbook** (fills, font colors, borders, merged cells) use `exceljs` — `xlsx` (SheetJS community) writes no cell styles at all and drops them without an error; `doc-gen xlsx` covers only a bold header, column widths, number formats and formulas. The long-tail escape hatch when no verb fits — see rewrite.md "Custom scripts"; ALWAYS `doc-validate --strict` the output.
|
|
28
|
+
- `bin/xlsx-recalc.cjs <in.xlsx> <out.xlsx>` — re-evaluate every formula (HyperFormula) and cache the results, keeping the formulas (`out` may equal `in`). Prints `{ok, out, recomputed, errors:[{sheet, cell, error}], sample}`; it exits 0 even with formula errors, so `errors` must be read — rewrite.md "Formulas".
|
|
29
|
+
|
|
30
|
+
## Fill and edit
|
|
31
|
+
|
|
32
|
+
- `bin/doc-fill.cjs <template.docx|pptx|xlsx> <values.json> <out>` — fill `{{placeholders}}` in an existing template, preserving its styling; prints `{ok, out, unfilled}`. A malformed or unclosed tag exits 1 and writes nothing. Syntax: rewrite.md "Fill a template".
|
|
33
|
+
- `bin/doc-xml.cjs replace <in.docx> <out.docx> <find> <replacement>` — cross-run text replace (finds text even when Word split it over runs, keeps formatting; matched within one paragraph; body incl. tables + headers/footers + footnotes/endnotes, not comments); prints `{replaced}` — `0` means the text is not there as written. Try this FIRST for plain text edits. `unpack <file> <dir>` / `pack <dir> <out>` — the raw-XML escape hatch (pretty-printed unpack → edit → repack); always `doc-validate --strict` afterwards. `merge-runs <in.docx> <out.docx>` coalesces fragmented runs before a manual XML edit.
|
|
34
|
+
- `bin/docx-annotate.cjs comment <in.docx> <out.docx> <comments.json>` / `suggest <in.docx> <out.docx> <author> <text>` / `accept <in.docx> <out.docx>` / `redline-check <original.docx> <edited.docx>` — Word review: comments, a tracked insertion, accept ALL tracked changes, and the proof that an edited file changed nothing outside tracked changes (exit 1 + `firstDivergence`). JSON shapes and caveats: rewrite.md "Review".
|
|
35
|
+
- `bin/docx-defaults.cjs <in.docx> <out.docx>` — add the default paragraph style a script-written (docx.js) .docx lacks; without it Pages lays body text out like the heading before it.
|
|
36
|
+
- `bin/pptx-clone.cjs <template.pptx> <slideNo> <out.pptx> [replacements.json]` — clone a styled template slide once per data item (appended at the end), replacing `{{tokens}}` in the clone only (tokens split across runs are still found); prints `{newSlideNumber, replaced, missing, warnings}`. `pptx-clone remove <in> <slideNo> <out>` drops a slide. CAPACITY warnings on stderr mean the text likely overflows its slot — shorten the wording, never shrink the font. See rewrite.md "Template decks".
|
|
37
|
+
- `bin/pptx-slide.cjs add <in.pptx> <out.pptx> [--layout N]` / `dup <in> <slideNo> <out>` / `remove <in> <slideNo> <out>` / `reorder <in> <order> <out>` — slide-lifecycle surgery on an EXISTING deck without a `{{token}}` template: append a blank slide bound to a layout (default: the last slide's), duplicate (appended) or drop a slide, or permute deck order (`reorder in.pptx 3,1,2 out.pptx` — current 1-based positions in the new sequence). Lossless; in-slide TEXT still goes through doc-fill / doc-xml.
|
|
38
|
+
- `bin/pptx-tokenize.cjs <user.pptx> <template.pptx> [schema.json]` — UPLOADED deck → `{{token}}` template keeping its layouts/master/brand, and write `schema.json` of every token. Then `doc-fill` it — fill EVERY token (`original` for slots you keep, else they blank). `leadStyle:true` = lead + body: fill `lead\nbody`. See rewrite.md "Reuse an uploaded deck".
|
|
39
|
+
- `bin/pptx-set-image.cjs <in.pptx> <slideNo> <rId> <image> <out.pptx>` — swap the picture behind a slide's `rId` (pptx-tokenize's image tokens carry it as `rel`), keeping the frame (position/size); prints `{newMedia, oldMedia}`. A different aspect ratio STRETCHES — pre-crop to the slot ratio.
|
|
40
|
+
- `bin/pptx-notes.cjs <in.pptx> <notes.json|{}> <out.pptx>` — speaker notes on an existing deck (`{"1": "…"}` by slide); repairs notes Keynote refuses (`{}`). **Never hand-write notesSlide XML.**
|
|
41
|
+
- `bin/pptx-theme.cjs <ref.pptx> [out.json]` — extract a theme.json (palette/fonts) from an existing deck (printed when no `out.json`). For documents, `doc-gen --theme` it. For a deck, copy its colours into a user skin's tokens (deck-design.md "Custom skins") — `html2pptx --theme` does not repaint a `--style` deck. Palette only, never layouts.
|
|
42
|
+
- `bin/pptx-embed-fonts.cjs <in.pptx> <out.pptx> --font "<Typeface>=<path.ttf>" [--bold …] [--italic …] [--bold-italic …]` — EMBED fonts (OOXML font embedding) so a deck looks the same where the font is missing. `<Typeface>` must match the runs' font name. Full-font embed: SMALL custom/brand fonts only, never big CJK fonts.
|
|
43
|
+
- `bin/pdf-form.cjs list <in.pdf>` / `fill <in.pdf> <values.json> <out.pdf> [--flatten]` / `overlay <filled.pdf> <out.pdf>` — PDF AcroForms: `list` → `{fields:[{name, type}]}`; `fill` takes `{"<text or dropdown field>": "value", "<checkbox>": true}` → `{filled, skipped}` (unknown names are skipped, not errors); `overlay` boxes every field in red for the render-and-look check (pdf.md). Chinese values are drawn in the bundled Noto Sans SC (`--flatten` too); a character it lacks (emoji, rare extension hanzi) exits 1 naming its code point.
|
|
44
|
+
- `bin/pdf-ops.cjs merge <out> <in…>` / `split <in> <from-to> <out>` / `rotate <in> <deg> <out> [pages]` / `watermark <in> <text> <out>` / `stamp <in.pdf> <stamps.json> <out.pdf>` / `encrypt <in> <out> <user-pw> [owner-pw]` / `decrypt <in> <pw> <out>` — page ops that keep every page verbatim (`rotate` pages like `1,3-5`), text stamping onto a flat (non-form) PDF — `stamps.json` = `[{page, x, y, text, size?, grey?}]` in points from the bottom-left (size 11, grey 0 = black, by default) — and AES-256 password protection (a wrong password exits 1). `watermark`/`stamp` draw Helvetica, or the bundled Noto Sans SC when the text has Chinese (same missing-character rule). An encrypted input exits 1 naming `decrypt`; `decrypt <in> "" <out>` handles an owner-password-only PDF.
|
|
45
|
+
|
|
46
|
+
## Decks
|
|
47
|
+
|
|
48
|
+
Flags and outputs only; the workflow is deck-design.md.
|
|
49
|
+
|
|
50
|
+
- `bin/deck-scenario.cjs "<scenario>"` — the ONE matrix row the clarify gate picked: `{ok, scenario:{id, tone, density, pages, narrative, flows, sections:[{label, key, content}], researchFocus}}`. `content` = what that section must contain (elements, evidence; `;宜` a recommended shape) — it is in no md table. Matches the id, else the longest alias in the text; no match exits 1 with the ids (map to the nearest and rerun).
|
|
51
|
+
- `bin/deck-styles.cjs --scenario "<scenario>"` — the style picker's first round: `{scenario, source, options}`; pass `options` to the picker as printed (SKILL.md "STYLE PICKER"). `source` `mixed`/`fallback` = the scenario claimed under six styles and the default group topped it up.
|
|
52
|
+
- `bin/deck-plan.cjs --layouts --style <id>` — before plan.json: the layouts this style renders and the plan fields each takes (`stats×N` = exactly N, `unit?` optional, `slots.<name>` = pass it, `source ✓` = footer).
|
|
53
|
+
- `bin/deck-plan.cjs <plan.json> [spec.json]` — **content-first layout selection, run BEFORE deck-build.** The plan is the deck spec's top-level fields (`style`, `themeVersion`, `layoutPack`, `scenario`, `density` (read · talk; default from scenario), `footer`, `chartRender`, …) plus per slide its CONTENT: `role` (cover · toc · section · content · ending; default content), optional `intent` (layouts/profiles.json) / `shape` / `layout` (pin), and `title` · `subtitle` (口径 line) · `takeaway` (启示 line) · `eyebrow` · `meta` · `index` · `points` (`[{title, body, date?, label?}]` or strings) · `stats` (`[{value, unit, label, note}]`) · `chart` · `quote` (`{text, source}` or a string) · `table` (`{columns, rows}`) · `image: true` · `source` (one short source line, "IDC,2026" → the footer; dropped with a warning on a layout without one) · `notes` (speaker notes) · `narrative` (outline only) · `file` (a bespoke slide: one `<section class="slide">` in that file, taken as is, `pick:"bespoke"`) · `slots` / `rows` (explicit values, override the draft). A `\n` in any text is a line break. `--outline <slide-outline.md>` also writes the readable outline (one block per slide + its pick) — the approval gate's document, rendered from the plan, never hand-written. Prints `{ok, slides:[{slide, pick, warnings, blanks, alternatives, rejected:[{layout, why}]}], next}`. Fit = the layout's item count (a 3-card layout takes exactly 3 points; rows layouts take 1..their measured ceiling, at least the count the README density column names) + every drafted string within the layout's capacity measured under your `style` (`layouts/capacity.json`; an unknown style uses the tightest skin). Picks under P1/P2/P3 (layouts/README.md). With `spec.json` it writes the drafted deck-build spec (chart slides get `chartRender:"svg"`) — only when EVERY slide got a pick; otherwise exit 1, nothing written: fix the content the `why` names and re-plan. `blanks` = slots the content had nothing for (left empty, not demo text) — fill them or accept. A layout rejected for leaving slots empty names where each one is filled from (`kpi1_desc ← each stat's note`).
|
|
54
|
+
- `bin/deck-build.cjs <spec.json> <deck.html>` — fills catalog layouts from a COMPACT spec (normally the one `deck-plan` drafted): top-level `style`, `layoutPack`, `scenario`, `chartRender`, `capacityCheck`, `motion`, Theme V2 fields (theme-system.md), and per slide `intent` + `layout` + `slots` / `rows` / `chart` / `source` / `notes` (layouts/README.md "How to fill"), or `file` + `notes` for a bespoke slide (relative to the spec, exactly one `<section class="slide">`); a `\n` in a slot is a line break, a typed `<br>` stays text. An unknown layout/slot/field, an unknown intent, a missing figure input, or a chart layout without `chart` / with a chart that has nothing to plot (it would ship the demo chart or a blank box) exits 1 and writes nothing; an intent/layout mismatch warns. **Capacity check** (`layouts/capacity.json`, measured per layout × style): a slot wider than the layout holds, a number that would wrap, more rows than fit, or text longer than its font size may carry (deck-qa's `tiny-font` floor: ≥10pt past 24 characters, ≥8pt up to 24 — a ~9pt footer takes a ≤24-character source line) exits 1 naming the slot and the cut; an un-filled NUMBERED slot (`card3_title` — it ships demo copy), a one-line slot that wraps, or an un-filled unnumbered slot (renders its template text; pass `""` to blank it) warns — treat those warnings as defects. `"capacityCheck":"strict"` makes the un-filled numbered slot fatal too; `"warn"|"off"` downgrades/disables it. **Charts:** `"chartRender":"svg"` (or a chart's own `"render":"svg"`) draws each layout chart from its data as an inline SVG sized to the layout's chart box (see deck-chart above); default `native` keeps the editable `c:chart`. `scenario` decides whether the family's motion preset applies (`motion: true|false` overrides; deck-design.md "Motion"). Prints `{ok, out, slides, warnings, capacity:{checked, mode}, motion:{on, reason}, next}` — `next` is the matching html2pptx command.
|
|
55
|
+
- `bin/deck-chart.cjs <chart.json> [out.svg] --width <px> --height <px>` — a chart drawn from data as an inline `<svg>` for a BESPOKE slide (catalog chart layouts use `chartRender:"svg"` in deck-build instead). `chart.json` = `{type: bar|barH|line|area|pie|doughnut|radar|scatter, labels, series:[{name, values}], stacked?, unit?, highlight?, showValues?, centerLabel?, zeroBaseline?}` (line: axis from 0 instead of zoomed to the data); radar = 3–8 labels, ≤3 series, `max?` (out-of-range clamps + warns); scatter = `series:[{name, points:[[x,y,label?]|{x,y,label}]}]`, `xLabel?`, `yLabel?`, `xUnit?`. Painted only with the skin's CSS variables (re-skins with `--style`); ≤4 series (more exits 1), a 5th+ pie slice folds into 其他; data mismatches warn like the native chart. Without `out.svg` prints the SVG; paste it into a box of exactly W×H px.
|
|
56
|
+
- `bin/html2pptx.cjs <deck.html|snapshot.layout.json> [out.pptx] [--style <id>] [--theme-v2] [--media-style <id>] [--backdrop <variant>] [--line <name|none>] [--editable | --mode native] [--rasterize] [--notes-doc <out.md>] [--preview-dir <dir>] [--preview-pages <all|N,N-M>] [--preview-density <1..4>]` — **THE deck path** (modes, slide contract: deck-design.md). Prints `{ok, out, engine, slides, backdrop, warnings, valid, qa}`; `valid:false` exits 1. Hybrid text a skin bakes into the backdrop (`--talos-bake`) that came out blank there is written native in its place (`healedText`, a warning each — the face may be a fallback); only text it cannot write fails: `ok:false` + `missingText`, exits 1 — never deliver that. `.pdf` output is retired (exit 64): convert the final PPTX with `doc-convert`. `--preview-dir` (no output file) writes slide PNGs straight from the styled HTML, skipping PPTX/LibreOffice — all pages once, then `--preview-pages` for changed ones — read each image once; 1280×720 by default (`--preview-density 2` only for fine print), plus `sheet.png` (all rendered slides as 640×360 tiles — read it first). `--style` resolves a catalog id, then a user style in `$TALOS_DOC_STYLES_DIR/styles/<name>/`, or an explicit directory. `--theme-v2` / `--media-style`: theme-system.md (rejected for `classic-v1` entries). `--backdrop <variant>` picks a signature backdrop (the skin's `backdrop-<variant>.html`, else `styles/_backdrops/<variant>.html`; `default` = the skin's own); without it the deck's variant is picked once at random and remembered beside the HTML (`.html2pptx-backdrop.json`, per style), so every later preview and build of that deck — edited or renamed — keeps it; the result reports it as `backdrop`. `--line` swaps the skin's line motif (`styles/_lines/`). `--backdrop-cache <dir>` (services re-rendering many decks) reuses unchanged hybrid backdrops; `--backdrop-cache-verify` captures anyway and fails on any difference. Advisory warnings: hardcoded brand colours, repeated layouts (P3), and a `data-chart` with nothing to plot — drawn as a dashed "图表占位" box (qa flags it as placeholder text). `[--theme <theme.json>]` is the legacy document-recolor path; a `--style` skin overrides it (a warning says so).
|
|
57
|
+
- `bin/deck-qa.cjs <file.pptx> [--json] [--rendered <office.pdf> | --final [--cache-dir <dir>] [--require-office]]` — MECHANICAL design QA (deterministic, token-free; the authoritative list of lint codes): `off-canvas` (content past the edge), `tiny-font` (<10pt), `shape-overlap` (colliding text), `text-only-slide` (a wall of text with no visual anchor), `low-contrast-text`, `placeholder-text` (a layout never filled — 替换为…/占位/在这里写/Lorem), `font-mismatch` (CJK in a Latin-only font → 豆腐; expected language defaults to the system locale, override with `TALOS_DECK_LANG`). Advisory: exit 0 even with findings. `--json` → `{ok,findings:[{slide,code,message}]}`. `html2pptx` already prints this lint as `qa` after every build. **`--final` is the deck's delivery sign-off in one call**: strict XSD validation (failure exits 1), the lint, an Office render through `doc-convert` (cached in `.office-cache/` beside the deck, or `--cache-dir` / `$TALOS_OFFICE_CACHE_DIR`), `render-overflow` / `render-collision` where Office re-laid the native text past its box, `render-type3-font` + `office.type3Fonts` when the render draws with a Type 3 font (LibreOffice's empty CJK fallback — that text is blank), and `review` + `previews` — PPTX review slides at ≈72dpi (PDF delivery checks every final PDF page; deck-design.md "Verify before delivering"). `--rendered <pdf>` runs the same render check against a PDF you converted yourself; a page count that differs from the deck exits 1.
|
|
58
|
+
- `bin/template-skin.cjs <template.pptx> <name> [--label <显示名>] [--content-page N] [--exclude <key>,…] [--include <key>,…]` — a pptx template → the user's style `$TALOS_DOC_STYLES_DIR/styles/<name>/`. Prints `{style, samples, uncertain, kept, warnings, replaced}`; each `kept` element carries a `hint` (its text, or a picture's corner — "a logo?") for `--exclude <key>`; `--label` = the name the picker shows ("我的风格 · <label>", default the file name); not a catalog id.
|
|
59
|
+
|
|
60
|
+
## Validate and convert
|
|
61
|
+
|
|
62
|
+
- `bin/doc-validate.cjs <file.docx|pptx|xlsx|pdf> [--strict] [--design-lint]` — structural validation gate: OOXML zip/parts/rels/Content_Types/XML well-formedness, plus pptx `notes-keynote` (→ `pptx-notes <in> {} <out>`) and docx `docx-default-style` (→ `docx-defaults`); a PDF must parse, open without a password (`pdf-encrypted` error: validate before `pdf-ops encrypt`) and hold at least one page (reports `pages` and a `pdf-parse-only` warning — how it looks is render-and-look's job). Exit 1 = broken, fix before delivering. `--strict` (OOXML only; on a PDF it just adds a `strict-not-applicable` warning) adds full ECMA-376 schema (XSD) validation of every recognised part — REQUIRED after custom-code (doc-run) or raw-XML output; a strict failure means the file is non-standard even if Office opens it. `--design-lint` (pptx only) adds deck-qa's lint as advisory warnings (never changes the exit code; codes under deck-qa in Decks) — real design bugs, fix them.
|
|
63
|
+
- `bin/doc-convert.cjs <in.docx|pptx|xlsx> <out.pdf> [--cache-dir <dir>] [--require-office]` — Office → PDF. **PPTX requires Office**: local `soffice`, then `TALOS_OFFICE_URL` sidecar. Missing Office exits 69; failed Office exits 75. Never rebuild a deck as browser HTML. Spreadsheets also require Office. DOCX can fall back to reconstructed HTML via the app's Chromium or headless Chromium (content retained, layout lost); `--require-office` blocks this fallback. Report the `[pdf (engine)]` result and any caveat. `--cache-dir` (or `TALOS_OFFICE_CACHE_DIR`) reuses the local Office result for identical source bytes; after any PPTX edit, convert again. Fonts come from the Office environment, which must have the required faces for faithful typography.
|