@taloswork/ose-shared 0.1.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +3 -3
- package/skills/document/SKILL.md +1 -1
- package/skills/document/bin/deck-build.cjs +2 -2
- package/skills/document/bin/deck-qa.cjs +1 -1
- package/skills/document/bin/doc-convert.cjs +288 -14
- package/skills/document/bin/doc-fill.cjs +1 -1
- package/skills/document/bin/doc-gen.cjs +318 -21
- package/skills/document/bin/doc-scan.cjs +8 -10
- package/skills/document/bin/doc-text.cjs +1 -1
- package/skills/document/bin/doc-validate.cjs +3 -3
- package/skills/document/bin/doc-xml.cjs +3 -3
- package/skills/document/bin/docx-annotate.cjs +3 -3
- package/skills/document/bin/html2pptx.cjs +10 -10
- package/skills/document/bin/ocr.cjs +1 -1
- package/skills/document/bin/pdf-form.cjs +1 -1
- package/skills/document/bin/pdf-images.cjs +1 -1
- package/skills/document/bin/pdf-ops.cjs +2 -2
- package/skills/document/bin/pdf-render.cjs +1 -1
- package/skills/document/bin/pdf-tables.cjs +1 -1
- package/skills/document/bin/pptx-clone.cjs +3 -3
- package/skills/document/bin/pptx-embed-fonts.cjs +1 -1
- package/skills/document/bin/pptx-set-image.cjs +3 -3
- package/skills/document/bin/pptx-slide.cjs +4 -4
- package/skills/document/bin/pptx-theme.cjs +1 -1
- package/skills/document/bin/pptx-tokenize.cjs +3 -3
- package/skills/document/bin/xlsx-query.cjs +1 -1
- package/skills/document/bin/xlsx-recalc.cjs +2 -2
- package/skills/document/rewrite.md +6 -0
- package/mcp/.gitkeep +0 -8
- package/skills/.gitkeep +0 -7
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@taloswork/ose-shared",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Talos cross-OSE shared library — skills + MCP servers auto-loaded into every OSE (P93 2026-07-27).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
@@ -10,6 +10,6 @@
|
|
|
10
10
|
"publishConfig": {
|
|
11
11
|
"access": "public"
|
|
12
12
|
},
|
|
13
|
-
"_talosPublishHash": "sha256-
|
|
14
|
-
"_talosPublishedAt": "2026-
|
|
13
|
+
"_talosPublishHash": "sha256-T36gm46DynkzOQOg4I0N8sK_-SP1h0kOm1929zOwv9U",
|
|
14
|
+
"_talosPublishedAt": "2026-09-02T17:54:52.828Z"
|
|
15
15
|
}
|
package/skills/document/SKILL.md
CHANGED
|
@@ -200,7 +200,7 @@ Fidelity-decision self-correct, but pointed at your OWN output:
|
|
|
200
200
|
- `bin/pdf-tables.cjs <file.pdf> [page] [minCols]` — recover text-layer tables as rows (ruled/scanned tables need render + vision instead).
|
|
201
201
|
- `bin/ocr.cjs <image.png|jpg> [lang]` — read text off a scanned image (tesseract.js). For a scanned PDF, `pdf-render` a page first, then OCR the PNG. Language data downloads on first use / set `$TALOS_TESSDATA` for offline. **Prefer vision** when a vision model is available; OCR is the text-only fallback.
|
|
202
202
|
- `bin/xlsx-query.cjs <file> [--summary | --agg <sheet> <col> <op> | --sample <sheet> [n]]` — spreadsheet compute.
|
|
203
|
-
- `bin/doc-gen.cjs <docx|xlsx|pptx|pdf> <out> <spec.json> [--theme <theme.json>] [--topic <subject> | --palette <id>] [--design native|html]` — generate documents from scratch; `--theme` applies a presentation theme (colors/fonts/cover/header/footer/pptx master — see rewrite.md "Themes"; built-ins in `themes/`). **`--topic <subject>` (pptx, when no `--theme`) picks a topic-adaptive palette** so the deck is designed-for-subject instead of template-flat, and turns on richness (motif + gradient wash + depth). `--palette <id>` forces a specific palette (`doc-gen --list-palettes` prints them). **`--accent <#hex>` / `--style <skin>` (docx/pdf)**: white-paper-black-text base with ONE brand color on H1/H2 + a faint table-header tint — the practical doc shape (a document is read/printed/edited, not projected, so body text stays near-black). `--accent` takes a literal hex; `--style <skin>` pulls just the `--accent` from `styles/<skin>/skin.css` (the deck skin's full palette is NOT carried onto paper). Precedence: `--theme` > spec `"theme"` > `--accent`/`--style` > `--palette`/`--topic` > default. `--design html` (pptx only, in-app) renders eligible slides through designed HTML templates for a magazine look — richer but a flat collage (no master structure); auto-falls back to native when headless or a slide has no template. Default `native` (structured, editable, charts, headless-safe).
|
|
203
|
+
- `bin/doc-gen.cjs <docx|xlsx|pptx|pdf> <out> <spec.json> [--theme <theme.json>] [--topic <subject> | --palette <id>] [--design native|html]` — generate documents from scratch. DOCX/PDF body fields are exactly `paragraphs` and/or `blocks` (never `sections`); unknown fields and empty bodies fail 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. `--theme` applies a presentation theme (colors/fonts/cover/header/footer/pptx master — see rewrite.md "Themes"; built-ins in `themes/`). **`--topic <subject>` (pptx, when no `--theme`) picks a topic-adaptive palette** so the deck is designed-for-subject instead of template-flat, and turns on richness (motif + gradient wash + depth). `--palette <id>` forces a specific palette (`doc-gen --list-palettes` prints them). **`--accent <#hex>` / `--style <skin>` (docx/pdf)**: white-paper-black-text base with ONE brand color on H1/H2 + a faint table-header tint — the practical doc shape (a document is read/printed/edited, not projected, so body text stays near-black). `--accent` takes a literal hex; `--style <skin>` pulls just the `--accent` from `styles/<skin>/skin.css` (the deck skin's full palette is NOT carried onto paper). Precedence: `--theme` > spec `"theme"` > `--accent`/`--style` > `--palette`/`--topic` > default. `--design html` (pptx only, in-app) renders eligible slides through designed HTML templates for a magazine look — richer but a flat collage (no master structure); auto-falls back to native when headless or a slide has no template. Default `native` (structured, editable, charts, headless-safe).
|
|
204
204
|
- `bin/doc-convert.cjs <in.docx|pptx|xlsx> <out.pdf>` — faithful docx/pptx/xlsx → PDF conversion. Engine precedence (best fidelity first, each downgrade tagged on stdout `[pdf (…)]` + warned on stderr): (1) **LibreOffice** if a system `soffice` is found — true layout render (page geometry, headers/footers, text-box positions, native charts), offline; (2) **LibreOffice sidecar** if `TALOS_OFFICE_URL` is set — same true-layout fidelity via a remote converter (e.g. Gotenberg), needs network; (3) **Chromium** (in-app) — reconstructed HTML, content/structure/images preserved but in-document layout is not (pptx = one page per slide); (4) **pdf-lib text-only** headless last resort — images/tables flatten. Surface the resulting `[pdf (engine)]` caveat to the user. NOTE: spreadsheets (xlsx/xls/ods) render ONLY via tiers 1–2 (real LibreOffice); tiers 3–4 do not rebuild spreadsheets, so with no LibreOffice available the command errors (never a degraded text-only sheet).
|
|
205
205
|
- `bin/doc-validate.cjs <file.docx|pptx|xlsx> [--strict] [--design-lint]` — structural OOXML validation gate (zip/parts/rels/Content_Types/XML well-formedness); run after every generation or edit; exit 1 = broken, fix before delivering. `--strict` adds full ECMA-376 schema (XSD) validation of every recognised part — REQUIRED after custom-code (doc-run/html2pptx) or raw-XML output; a strict failure means the file is non-standard even if Office opens it. `--design-lint` (pptx only) adds an ADVISORY design pass — flags `text-only-slide` (a wall of text with no visual anchor), `low-contrast-text`, `placeholder-text` (a layout pasted but never filled — 替换为…/占位/Lorem still in the slide), `font-mismatch` (CJK text set in a Latin-only font → 豆腐; expected language defaults to the system locale, override with `TALOS_DECK_LANG`), `off-canvas`, `tiny-font`, `shape-overlap`; findings are warnings (never change the exit code) but are real design bugs — fix them. See deck-design.md "Design principles".
|
|
206
206
|
- `bin/deck-qa.cjs <file.pptx> [--json]` — MECHANICAL design QA (deterministic, token-free): reads slide XML and flags `off-canvas` (content clipped past the edge), `tiny-font` (<10pt), `shape-overlap` (colliding text), plus `text-only-slide` / `low-contrast-text` / `placeholder-text` / `font-mismatch` (CJK text in a Latin-only font → 豆腐). Advisory (exit 0 even with findings). `--json` → `{ok,findings:[{slide,code,message}]}` for the visual-QA loop. It is the CHEAP GATE before spending vision tokens — run it every QA round and fix all findings first; then do the vision critic + per-slide regeneration (deck-design.md "Verify before delivering").
|
|
@@ -6,7 +6,7 @@ var import_promises = require("node:fs/promises");
|
|
|
6
6
|
var import_node_fs = require("node:fs");
|
|
7
7
|
var import_node_path = require("node:path");
|
|
8
8
|
|
|
9
|
-
// packages/core/dist/
|
|
9
|
+
// packages/core/dist/document/deck-figures.js
|
|
10
10
|
var FIGURE_SPECS = {
|
|
11
11
|
radar: {
|
|
12
12
|
metrics: { required: ["name", "score"], derived: [] }
|
|
@@ -159,7 +159,7 @@ function derivePins(rows, warnings) {
|
|
|
159
159
|
});
|
|
160
160
|
}
|
|
161
161
|
|
|
162
|
-
// packages/core/dist/
|
|
162
|
+
// packages/core/dist/document/deck-build.js
|
|
163
163
|
var SLOT_RE = /\{\{(?![#/])([^{}]*)\}\}/g;
|
|
164
164
|
var BLOCK_RE = /\{\{#([A-Za-z_][\w-]*)\}\}([\s\S]*?)\{\{\/\1\}\}/g;
|
|
165
165
|
function escapeHtml(s) {
|
|
@@ -9818,7 +9818,7 @@ var require_lib3 = __commonJS({
|
|
|
9818
9818
|
// apps/admin/data/skills.lib/document/bin-src/deck-qa.mjs
|
|
9819
9819
|
var import_promises = require("node:fs/promises");
|
|
9820
9820
|
|
|
9821
|
-
// packages/core/dist/
|
|
9821
|
+
// packages/core/dist/document/doc-design-lint.js
|
|
9822
9822
|
var import_jszip = __toESM(require_lib3(), 1);
|
|
9823
9823
|
var PLACEHOLDER_RE = /替换为|在这里写|占位|示例文本|Lorem ipsum/i;
|
|
9824
9824
|
var CJK_TEXT_RE = /[-ヿ㐀-䶿一-鿿가-豈-]/;
|
|
@@ -148976,11 +148976,11 @@ var require_fontkit_umd = __commonJS({
|
|
|
148976
148976
|
var import_node_path2 = require("node:path");
|
|
148977
148977
|
var import_promises2 = require("node:fs/promises");
|
|
148978
148978
|
|
|
148979
|
-
// packages/core/dist/
|
|
148979
|
+
// packages/core/dist/document/doc-convert.js
|
|
148980
148980
|
var import_jszip3 = __toESM(require_lib3(), 1);
|
|
148981
148981
|
var import_mammoth2 = __toESM(require_lib6(), 1);
|
|
148982
148982
|
|
|
148983
|
-
// packages/core/dist/
|
|
148983
|
+
// packages/core/dist/document/office-converter.js
|
|
148984
148984
|
var import_jszip = __toESM(require_lib3(), 1);
|
|
148985
148985
|
var import_mammoth = __toESM(require_lib6(), 1);
|
|
148986
148986
|
function isZipMagic(buffer) {
|
|
@@ -168101,7 +168101,7 @@ var findPatchKeys = (text) => {
|
|
|
168101
168101
|
return (_text$match = text.match(pattern)) !== null && _text$match !== void 0 ? _text$match : [];
|
|
168102
168102
|
};
|
|
168103
168103
|
|
|
168104
|
-
// packages/core/dist/
|
|
168104
|
+
// packages/core/dist/document/doc-gen.js
|
|
168105
168105
|
var import_exceljs = __toESM(require_excel(), 1);
|
|
168106
168106
|
var import_jszip2 = __toESM(require_lib3(), 1);
|
|
168107
168107
|
var import_pptxgenjs = __toESM(require_pptxgen_cjs(), 1);
|
|
@@ -168109,7 +168109,7 @@ var import_pdf_lib = __toESM(require_cjs(), 1);
|
|
|
168109
168109
|
var import_fontkit = __toESM(require_fontkit_umd(), 1);
|
|
168110
168110
|
var import_node_fs = require("node:fs");
|
|
168111
168111
|
|
|
168112
|
-
// packages/core/dist/
|
|
168112
|
+
// packages/core/dist/document/doc-theme.js
|
|
168113
168113
|
var DEFAULT_THEME = {
|
|
168114
168114
|
bestFor: "",
|
|
168115
168115
|
fonts: { heading: "", body: "" },
|
|
@@ -168301,7 +168301,7 @@ function asTheme(themeInput) {
|
|
|
168301
168301
|
return themeInput && typeof themeInput === "object" && "warnings" in themeInput ? themeInput : resolveTheme(themeInput);
|
|
168302
168302
|
}
|
|
168303
168303
|
|
|
168304
|
-
// packages/core/dist/
|
|
168304
|
+
// packages/core/dist/document/doc-palette.js
|
|
168305
168305
|
var PALETTE_LIBRARY = [
|
|
168306
168306
|
{
|
|
168307
168307
|
name: "corporate-navy",
|
|
@@ -168536,7 +168536,7 @@ function themeSpecFromTopic(topic) {
|
|
|
168536
168536
|
return paletteToThemeSpec(resolvePalette(topic));
|
|
168537
168537
|
}
|
|
168538
168538
|
|
|
168539
|
-
// packages/core/dist/
|
|
168539
|
+
// packages/core/dist/document/lucide-icons.js
|
|
168540
168540
|
var LUCIDE_ICONS = {
|
|
168541
168541
|
check: '<path d="M20 6 9 17l-5-5"/>',
|
|
168542
168542
|
"check-circle": '<circle cx="12" cy="12" r="10"/><path d="m9 12 2 2 4-4"/>',
|
|
@@ -168593,16 +168593,289 @@ function iconSvgDataUri(name, colorHex) {
|
|
|
168593
168593
|
return "data:image/svg+xml;base64," + Buffer.from(svg).toString("base64");
|
|
168594
168594
|
}
|
|
168595
168595
|
|
|
168596
|
-
// packages/core/dist/
|
|
168596
|
+
// packages/core/dist/document/doc-gen.js
|
|
168597
168597
|
function bareHex(c) {
|
|
168598
168598
|
return c.replace("#", "").toUpperCase();
|
|
168599
168599
|
}
|
|
168600
|
+
var DocumentSpecValidationError = class extends Error {
|
|
168601
|
+
code = "DOC_SPEC_INVALID";
|
|
168602
|
+
issues;
|
|
168603
|
+
constructor(issues) {
|
|
168604
|
+
const detail = issues.map((issue) => `${issue.path}: ${issue.message}`).join("; ");
|
|
168605
|
+
super(`DOC_SPEC_INVALID: ${detail}`);
|
|
168606
|
+
this.name = "DocumentSpecValidationError";
|
|
168607
|
+
this.issues = [...issues];
|
|
168608
|
+
}
|
|
168609
|
+
};
|
|
168610
|
+
var TEXT_DOCUMENT_KEYS = /* @__PURE__ */ new Set(["title", "paragraphs", "blocks", "allowCoverOnly", "fontPath", "theme", "topic", "palette"]);
|
|
168611
|
+
var XLSX_DOCUMENT_KEYS = /* @__PURE__ */ new Set(["sheets", "theme", "topic", "palette"]);
|
|
168612
|
+
var PPTX_DOCUMENT_KEYS = /* @__PURE__ */ new Set(["slides", "theme", "topic", "palette"]);
|
|
168613
|
+
var PPTX_SLIDE_KEYS = /* @__PURE__ */ new Set([
|
|
168614
|
+
"title",
|
|
168615
|
+
"cover",
|
|
168616
|
+
"toc",
|
|
168617
|
+
"ending",
|
|
168618
|
+
"section",
|
|
168619
|
+
"kpiCards",
|
|
168620
|
+
"cards",
|
|
168621
|
+
"timeline",
|
|
168622
|
+
"steps",
|
|
168623
|
+
"iconList",
|
|
168624
|
+
"splitImage",
|
|
168625
|
+
"comparison",
|
|
168626
|
+
"quadrant",
|
|
168627
|
+
"bigStat",
|
|
168628
|
+
"quote",
|
|
168629
|
+
"bullets",
|
|
168630
|
+
"table",
|
|
168631
|
+
"image",
|
|
168632
|
+
"chart"
|
|
168633
|
+
]);
|
|
168634
|
+
function isJsonRecord(value) {
|
|
168635
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
168636
|
+
}
|
|
168637
|
+
function addUnknownPropertyIssues(record, allowed, path, issues) {
|
|
168638
|
+
for (const key of Object.keys(record)) {
|
|
168639
|
+
if (allowed.has(key))
|
|
168640
|
+
continue;
|
|
168641
|
+
const message = allowed === TEXT_DOCUMENT_KEYS && (key === "section" || key === "sections") ? `${key} is unsupported for DOCX/PDF content; use $.blocks with heading/paragraph/list/table/image blocks (section is PPTX-only)` : `unsupported property ${JSON.stringify(key)}; allowed properties: ${[...allowed].join(", ")}`;
|
|
168642
|
+
issues.push({ path: `${path}.${key}`, code: "UNKNOWN_PROPERTY", message });
|
|
168643
|
+
}
|
|
168644
|
+
}
|
|
168645
|
+
function nonBlankString(value) {
|
|
168646
|
+
return typeof value === "string" && value.trim().length > 0;
|
|
168647
|
+
}
|
|
168648
|
+
function validateTextBlock(value, path, issues) {
|
|
168649
|
+
if (!isJsonRecord(value)) {
|
|
168650
|
+
issues.push({ path, code: "INVALID_TYPE", message: "block must be an object" });
|
|
168651
|
+
return false;
|
|
168652
|
+
}
|
|
168653
|
+
if (typeof value.type !== "string") {
|
|
168654
|
+
issues.push({ path: `${path}.type`, code: "INVALID_TYPE", message: "block type must be a string" });
|
|
168655
|
+
return false;
|
|
168656
|
+
}
|
|
168657
|
+
const requireText = () => {
|
|
168658
|
+
if (nonBlankString(value.text))
|
|
168659
|
+
return true;
|
|
168660
|
+
issues.push({ path: `${path}.text`, code: "EMPTY_CONTENT", message: "text must be a non-empty string" });
|
|
168661
|
+
return false;
|
|
168662
|
+
};
|
|
168663
|
+
switch (value.type) {
|
|
168664
|
+
case "heading": {
|
|
168665
|
+
addUnknownPropertyIssues(value, /* @__PURE__ */ new Set(["type", "text", "level"]), path, issues);
|
|
168666
|
+
const valid = requireText();
|
|
168667
|
+
if (value.level !== void 0 && ![1, 2, 3, 4].includes(value.level)) {
|
|
168668
|
+
issues.push({ path: `${path}.level`, code: "INVALID_VALUE", message: "level must be one of 1, 2, 3, 4" });
|
|
168669
|
+
}
|
|
168670
|
+
return valid;
|
|
168671
|
+
}
|
|
168672
|
+
case "paragraph": {
|
|
168673
|
+
addUnknownPropertyIssues(value, /* @__PURE__ */ new Set(["type", "text", "bold", "italic"]), path, issues);
|
|
168674
|
+
const valid = requireText();
|
|
168675
|
+
for (const key of ["bold", "italic"]) {
|
|
168676
|
+
if (value[key] !== void 0 && typeof value[key] !== "boolean") {
|
|
168677
|
+
issues.push({ path: `${path}.${key}`, code: "INVALID_TYPE", message: `${key} must be a boolean` });
|
|
168678
|
+
}
|
|
168679
|
+
}
|
|
168680
|
+
return valid;
|
|
168681
|
+
}
|
|
168682
|
+
case "list": {
|
|
168683
|
+
addUnknownPropertyIssues(value, /* @__PURE__ */ new Set(["type", "items", "ordered"]), path, issues);
|
|
168684
|
+
if (!Array.isArray(value.items) || value.items.length === 0) {
|
|
168685
|
+
issues.push({ path: `${path}.items`, code: "EMPTY_CONTENT", message: "items must be a non-empty string array" });
|
|
168686
|
+
return false;
|
|
168687
|
+
}
|
|
168688
|
+
let valid = true;
|
|
168689
|
+
value.items.forEach((item, index) => {
|
|
168690
|
+
if (nonBlankString(item))
|
|
168691
|
+
return;
|
|
168692
|
+
valid = false;
|
|
168693
|
+
issues.push({ path: `${path}.items[${index}]`, code: "EMPTY_CONTENT", message: "list item must be a non-empty string" });
|
|
168694
|
+
});
|
|
168695
|
+
if (value.ordered !== void 0 && typeof value.ordered !== "boolean") {
|
|
168696
|
+
issues.push({ path: `${path}.ordered`, code: "INVALID_TYPE", message: "ordered must be a boolean" });
|
|
168697
|
+
}
|
|
168698
|
+
return valid;
|
|
168699
|
+
}
|
|
168700
|
+
case "table": {
|
|
168701
|
+
addUnknownPropertyIssues(value, /* @__PURE__ */ new Set(["type", "rows", "header", "columnWidths"]), path, issues);
|
|
168702
|
+
if (!Array.isArray(value.rows) || value.rows.length === 0) {
|
|
168703
|
+
issues.push({ path: `${path}.rows`, code: "EMPTY_CONTENT", message: "rows must be a non-empty array" });
|
|
168704
|
+
return false;
|
|
168705
|
+
}
|
|
168706
|
+
let hasCellContent = false;
|
|
168707
|
+
value.rows.forEach((row, rowIndex) => {
|
|
168708
|
+
if (!Array.isArray(row) || row.length === 0) {
|
|
168709
|
+
issues.push({ path: `${path}.rows[${rowIndex}]`, code: "INVALID_TYPE", message: "table row must be a non-empty array" });
|
|
168710
|
+
return;
|
|
168711
|
+
}
|
|
168712
|
+
row.forEach((cell, columnIndex) => {
|
|
168713
|
+
if (typeof cell !== "string" && typeof cell !== "number") {
|
|
168714
|
+
issues.push({ path: `${path}.rows[${rowIndex}][${columnIndex}]`, code: "INVALID_TYPE", message: "table cell must be a string or number" });
|
|
168715
|
+
} else if (String(cell).trim().length > 0) {
|
|
168716
|
+
hasCellContent = true;
|
|
168717
|
+
}
|
|
168718
|
+
});
|
|
168719
|
+
});
|
|
168720
|
+
if (!hasCellContent) {
|
|
168721
|
+
issues.push({ path: `${path}.rows`, code: "EMPTY_CONTENT", message: "table must contain at least one non-empty cell" });
|
|
168722
|
+
}
|
|
168723
|
+
if (value.header !== void 0 && typeof value.header !== "boolean") {
|
|
168724
|
+
issues.push({ path: `${path}.header`, code: "INVALID_TYPE", message: "header must be a boolean" });
|
|
168725
|
+
}
|
|
168726
|
+
if (value.columnWidths !== void 0 && (!Array.isArray(value.columnWidths) || value.columnWidths.some((width) => typeof width !== "number" || !Number.isFinite(width) || width <= 0))) {
|
|
168727
|
+
issues.push({ path: `${path}.columnWidths`, code: "INVALID_VALUE", message: "columnWidths must contain only positive finite numbers" });
|
|
168728
|
+
}
|
|
168729
|
+
return hasCellContent;
|
|
168730
|
+
}
|
|
168731
|
+
case "image": {
|
|
168732
|
+
addUnknownPropertyIssues(value, /* @__PURE__ */ new Set(["type", "data", "imageType", "width", "height"]), path, issues);
|
|
168733
|
+
if (!nonBlankString(value.data)) {
|
|
168734
|
+
issues.push({ path: `${path}.data`, code: "EMPTY_CONTENT", message: "image data must be non-empty base64" });
|
|
168735
|
+
return false;
|
|
168736
|
+
}
|
|
168737
|
+
if (value.imageType !== void 0 && !["png", "jpg", "gif", "bmp"].includes(String(value.imageType))) {
|
|
168738
|
+
issues.push({ path: `${path}.imageType`, code: "INVALID_VALUE", message: "imageType must be png, jpg, gif, or bmp" });
|
|
168739
|
+
}
|
|
168740
|
+
for (const key of ["width", "height"]) {
|
|
168741
|
+
if (value[key] !== void 0 && (typeof value[key] !== "number" || !Number.isFinite(value[key]) || value[key] <= 0)) {
|
|
168742
|
+
issues.push({ path: `${path}.${key}`, code: "INVALID_VALUE", message: `${key} must be a positive finite number` });
|
|
168743
|
+
}
|
|
168744
|
+
}
|
|
168745
|
+
return true;
|
|
168746
|
+
}
|
|
168747
|
+
default:
|
|
168748
|
+
issues.push({ path: `${path}.type`, code: "INVALID_VALUE", message: `unhandled block type: ${value.type}` });
|
|
168749
|
+
return false;
|
|
168750
|
+
}
|
|
168751
|
+
}
|
|
168752
|
+
function inspectTextDocumentSpec(spec) {
|
|
168753
|
+
const issues = [];
|
|
168754
|
+
if (!isJsonRecord(spec)) {
|
|
168755
|
+
issues.push({ path: "$", code: "INVALID_TYPE", message: "spec must be a JSON object" });
|
|
168756
|
+
return { issues, hasBody: false, hasTitle: false, allowCoverOnly: false };
|
|
168757
|
+
}
|
|
168758
|
+
addUnknownPropertyIssues(spec, TEXT_DOCUMENT_KEYS, "$", issues);
|
|
168759
|
+
const hasTitle = nonBlankString(spec.title);
|
|
168760
|
+
if (spec.title !== void 0 && !hasTitle) {
|
|
168761
|
+
issues.push({ path: "$.title", code: "EMPTY_CONTENT", message: "title must be a non-empty string when provided" });
|
|
168762
|
+
}
|
|
168763
|
+
if (spec.allowCoverOnly !== void 0 && typeof spec.allowCoverOnly !== "boolean") {
|
|
168764
|
+
issues.push({ path: "$.allowCoverOnly", code: "INVALID_TYPE", message: "allowCoverOnly must be a boolean" });
|
|
168765
|
+
}
|
|
168766
|
+
if (spec.fontPath !== void 0 && !nonBlankString(spec.fontPath)) {
|
|
168767
|
+
issues.push({ path: "$.fontPath", code: "INVALID_TYPE", message: "fontPath must be a non-empty string" });
|
|
168768
|
+
}
|
|
168769
|
+
let hasBody = false;
|
|
168770
|
+
if (spec.paragraphs !== void 0) {
|
|
168771
|
+
if (!Array.isArray(spec.paragraphs)) {
|
|
168772
|
+
issues.push({ path: "$.paragraphs", code: "INVALID_TYPE", message: "paragraphs must be a string array" });
|
|
168773
|
+
} else {
|
|
168774
|
+
spec.paragraphs.forEach((paragraph, index) => {
|
|
168775
|
+
if (nonBlankString(paragraph))
|
|
168776
|
+
hasBody = true;
|
|
168777
|
+
else
|
|
168778
|
+
issues.push({ path: `$.paragraphs[${index}]`, code: "EMPTY_CONTENT", message: "paragraph must be a non-empty string" });
|
|
168779
|
+
});
|
|
168780
|
+
}
|
|
168781
|
+
}
|
|
168782
|
+
if (spec.blocks !== void 0) {
|
|
168783
|
+
if (!Array.isArray(spec.blocks)) {
|
|
168784
|
+
issues.push({ path: "$.blocks", code: "INVALID_TYPE", message: "blocks must be an array" });
|
|
168785
|
+
} else {
|
|
168786
|
+
spec.blocks.forEach((block, index) => {
|
|
168787
|
+
if (validateTextBlock(block, `$.blocks[${index}]`, issues))
|
|
168788
|
+
hasBody = true;
|
|
168789
|
+
});
|
|
168790
|
+
}
|
|
168791
|
+
}
|
|
168792
|
+
return { issues, hasBody, hasTitle, allowCoverOnly: spec.allowCoverOnly === true };
|
|
168793
|
+
}
|
|
168600
168794
|
function assertTextDocumentSpec(spec) {
|
|
168601
|
-
|
|
168795
|
+
const { issues } = inspectTextDocumentSpec(spec);
|
|
168796
|
+
if (issues.length > 0)
|
|
168797
|
+
throw new DocumentSpecValidationError(issues);
|
|
168798
|
+
}
|
|
168799
|
+
function validateRows(rows, path, issues) {
|
|
168800
|
+
if (!Array.isArray(rows) || rows.length === 0) {
|
|
168801
|
+
issues.push({ path, code: "EMPTY_CONTENT", message: "rows must be a non-empty array" });
|
|
168802
|
+
return false;
|
|
168803
|
+
}
|
|
168804
|
+
let hasContent = false;
|
|
168805
|
+
rows.forEach((row, rowIndex) => {
|
|
168806
|
+
if (!Array.isArray(row) || row.length === 0) {
|
|
168807
|
+
issues.push({ path: `${path}[${rowIndex}]`, code: "INVALID_TYPE", message: "row must be a non-empty array" });
|
|
168808
|
+
return;
|
|
168809
|
+
}
|
|
168810
|
+
row.forEach((cell, columnIndex) => {
|
|
168811
|
+
if (typeof cell !== "string" && typeof cell !== "number") {
|
|
168812
|
+
issues.push({ path: `${path}[${rowIndex}][${columnIndex}]`, code: "INVALID_TYPE", message: "cell must be a string or number" });
|
|
168813
|
+
} else if (String(cell).trim().length > 0) {
|
|
168814
|
+
hasContent = true;
|
|
168815
|
+
}
|
|
168816
|
+
});
|
|
168817
|
+
});
|
|
168818
|
+
if (!hasContent)
|
|
168819
|
+
issues.push({ path, code: "EMPTY_CONTENT", message: "rows must contain at least one non-empty cell" });
|
|
168820
|
+
return hasContent;
|
|
168821
|
+
}
|
|
168822
|
+
function assertDocumentSpec(kind, spec) {
|
|
168823
|
+
if (kind === "docx" || kind === "pdf") {
|
|
168824
|
+
const inspection = inspectTextDocumentSpec(spec);
|
|
168825
|
+
if (!inspection.hasBody && !(inspection.allowCoverOnly && inspection.hasTitle)) {
|
|
168826
|
+
inspection.issues.push({
|
|
168827
|
+
path: "$",
|
|
168828
|
+
code: "EMPTY_CONTENT",
|
|
168829
|
+
message: "document body is empty; provide non-empty paragraphs/blocks, or set allowCoverOnly=true with a non-empty title"
|
|
168830
|
+
});
|
|
168831
|
+
}
|
|
168832
|
+
if (inspection.issues.length > 0)
|
|
168833
|
+
throw new DocumentSpecValidationError(inspection.issues);
|
|
168602
168834
|
return;
|
|
168603
|
-
if (Object.prototype.hasOwnProperty.call(spec, "section")) {
|
|
168604
|
-
throw new Error("unsupported document field: section (use blocks for DOCX/PDF content; section is PPTX-only)");
|
|
168605
168835
|
}
|
|
168836
|
+
const issues = [];
|
|
168837
|
+
if (!isJsonRecord(spec)) {
|
|
168838
|
+
throw new DocumentSpecValidationError([{ path: "$", code: "INVALID_TYPE", message: "spec must be a JSON object" }]);
|
|
168839
|
+
}
|
|
168840
|
+
if (kind === "xlsx") {
|
|
168841
|
+
addUnknownPropertyIssues(spec, XLSX_DOCUMENT_KEYS, "$", issues);
|
|
168842
|
+
if (!isJsonRecord(spec.sheets) || Object.keys(spec.sheets).length === 0) {
|
|
168843
|
+
issues.push({ path: "$.sheets", code: "EMPTY_CONTENT", message: "sheets must be a non-empty object" });
|
|
168844
|
+
} else {
|
|
168845
|
+
for (const [name, sheet] of Object.entries(spec.sheets)) {
|
|
168846
|
+
const path = `$.sheets[${JSON.stringify(name)}]`;
|
|
168847
|
+
if (Array.isArray(sheet)) {
|
|
168848
|
+
validateRows(sheet, path, issues);
|
|
168849
|
+
} else if (isJsonRecord(sheet)) {
|
|
168850
|
+
addUnknownPropertyIssues(sheet, /* @__PURE__ */ new Set(["rows", "header", "columnWidths", "numFmt"]), path, issues);
|
|
168851
|
+
validateRows(sheet.rows, `${path}.rows`, issues);
|
|
168852
|
+
} else {
|
|
168853
|
+
issues.push({ path, code: "INVALID_TYPE", message: "sheet must be a row array or an object containing rows" });
|
|
168854
|
+
}
|
|
168855
|
+
}
|
|
168856
|
+
}
|
|
168857
|
+
} else if (kind === "pptx") {
|
|
168858
|
+
addUnknownPropertyIssues(spec, PPTX_DOCUMENT_KEYS, "$", issues);
|
|
168859
|
+
if (!Array.isArray(spec.slides) || spec.slides.length === 0) {
|
|
168860
|
+
issues.push({ path: "$.slides", code: "EMPTY_CONTENT", message: "slides must be a non-empty array" });
|
|
168861
|
+
} else {
|
|
168862
|
+
spec.slides.forEach((slide, index) => {
|
|
168863
|
+
const path = `$.slides[${index}]`;
|
|
168864
|
+
if (!isJsonRecord(slide)) {
|
|
168865
|
+
issues.push({ path, code: "INVALID_TYPE", message: "slide must be an object" });
|
|
168866
|
+
return;
|
|
168867
|
+
}
|
|
168868
|
+
addUnknownPropertyIssues(slide, PPTX_SLIDE_KEYS, path, issues);
|
|
168869
|
+
if (Object.keys(slide).length === 0) {
|
|
168870
|
+
issues.push({ path, code: "EMPTY_CONTENT", message: "slide must contain at least one supported field" });
|
|
168871
|
+
}
|
|
168872
|
+
});
|
|
168873
|
+
}
|
|
168874
|
+
} else {
|
|
168875
|
+
throw new Error(`unsupported kind: ${String(kind)}`);
|
|
168876
|
+
}
|
|
168877
|
+
if (issues.length > 0)
|
|
168878
|
+
throw new DocumentSpecValidationError(issues);
|
|
168606
168879
|
}
|
|
168607
168880
|
var HEADINGS = [HeadingLevel.HEADING_1, HeadingLevel.HEADING_2, HeadingLevel.HEADING_3, HeadingLevel.HEADING_4];
|
|
168608
168881
|
var ORDERED_REF = "ol";
|
|
@@ -169864,6 +170137,7 @@ async function generatePdf(spec, themeInput) {
|
|
|
169864
170137
|
return doc.save();
|
|
169865
170138
|
}
|
|
169866
170139
|
async function generateDocument(kind, spec, theme) {
|
|
170140
|
+
assertDocumentSpec(kind, spec);
|
|
169867
170141
|
switch (kind) {
|
|
169868
170142
|
case "docx":
|
|
169869
170143
|
return generateDocx(spec, theme);
|
|
@@ -169878,10 +170152,10 @@ async function generateDocument(kind, spec, theme) {
|
|
|
169878
170152
|
}
|
|
169879
170153
|
}
|
|
169880
170154
|
|
|
169881
|
-
// packages/core/dist/
|
|
170155
|
+
// packages/core/dist/document/doc-html.js
|
|
169882
170156
|
var FONT_STACK = 'TalosCJK, "Helvetica Neue", Helvetica, Arial, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Noto Sans CJK SC", "Source Han Sans SC", "WenQuanYi Zen Hei", sans-serif';
|
|
169883
170157
|
|
|
169884
|
-
// packages/core/dist/
|
|
170158
|
+
// packages/core/dist/document/doc-convert.js
|
|
169885
170159
|
var PRINT_STYLE = `
|
|
169886
170160
|
@page { size: A4; margin: 20mm 18mm; }
|
|
169887
170161
|
* { box-sizing: border-box; }
|
|
@@ -169992,7 +170266,7 @@ async function convertOfficeToPrintable(buffer, filename) {
|
|
|
169992
170266
|
return { error: `\u4E0D\u652F\u6301 ${filename} \u7684\u6587\u4EF6\u683C\u5F0F\uFF08doc-convert \u53EA\u652F\u6301 .docx/.pptx\uFF09` };
|
|
169993
170267
|
}
|
|
169994
170268
|
|
|
169995
|
-
// packages/core/dist/
|
|
170269
|
+
// packages/core/dist/document/soffice.js
|
|
169996
170270
|
var import_node_child_process = require("node:child_process");
|
|
169997
170271
|
var import_node_fs2 = require("node:fs");
|
|
169998
170272
|
var import_promises = require("node:fs/promises");
|
|
@@ -170138,7 +170412,7 @@ function runSoffice(bin, args, timeoutMs) {
|
|
|
170138
170412
|
});
|
|
170139
170413
|
}
|
|
170140
170414
|
|
|
170141
|
-
// packages/core/dist/
|
|
170415
|
+
// packages/core/dist/document/office-service.js
|
|
170142
170416
|
async function convertViaOfficeService(bytes, filename, url, options = {}) {
|
|
170143
170417
|
const doFetch = options.fetchImpl ?? fetch;
|
|
170144
170418
|
const view = bytes instanceof Uint8Array ? bytes : new Uint8Array(bytes);
|
|
@@ -16068,7 +16068,7 @@ var require_js = __commonJS({
|
|
|
16068
16068
|
var import_promises = require("node:fs/promises");
|
|
16069
16069
|
var import_node_path = require("node:path");
|
|
16070
16070
|
|
|
16071
|
-
// packages/core/dist/
|
|
16071
|
+
// packages/core/dist/document/doc-template.js
|
|
16072
16072
|
var import_docxtemplater = __toESM(require_docxtemplater(), 1);
|
|
16073
16073
|
var import_pizzip = __toESM(require_js(), 1);
|
|
16074
16074
|
var DEFAULT_DELIMITERS = { start: "{{", end: "}}" };
|