@taloswork/ose-shared 0.1.10 → 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 (126) 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
  120. package/skills/cozy-static-svg/SKILL.md +0 -76
  121. package/skills/cozy-static-svg/references/approved-preview.png +0 -0
  122. package/skills/cozy-static-svg/references/cross-platform.md +0 -73
  123. package/skills/cozy-static-svg/references/moon_keeps_watch.svg +0 -229
  124. package/skills/cozy-static-svg/references/style-anchor.md +0 -271
  125. package/skills/cozy-static-svg/scripts/artkit.py +0 -321
  126. package/skills/cozy-static-svg/scripts/to_png.py +0 -333
@@ -4320,7 +4320,7 @@ var require_BufferList = __commonJS({
4320
4320
  this.head = this.tail = null;
4321
4321
  this.length = 0;
4322
4322
  };
4323
- BufferList.prototype.join = function join4(s) {
4323
+ BufferList.prototype.join = function join5(s) {
4324
4324
  if (this.length === 0) return "";
4325
4325
  var p = this.head;
4326
4326
  var ret2 = "" + p.data;
@@ -13959,7 +13959,7 @@ var require_buffer_list2 = __commonJS({
13959
13959
  }
13960
13960
  }, {
13961
13961
  key: "join",
13962
- value: function join4(s) {
13962
+ value: function join5(s) {
13963
13963
  if (this.length === 0) return "";
13964
13964
  var p = this.head;
13965
13965
  var ret2 = "" + p.data;
@@ -41204,7 +41204,7 @@ var require_buffer_list3 = __commonJS({
41204
41204
  }
41205
41205
  }, {
41206
41206
  key: "join",
41207
- value: function join4(s) {
41207
+ value: function join5(s) {
41208
41208
  if (this.length === 0) return "";
41209
41209
  var p = this.head;
41210
41210
  var ret2 = "" + p.data;
@@ -44570,7 +44570,7 @@ var require_buffer_list4 = __commonJS({
44570
44570
  }
44571
44571
  }, {
44572
44572
  key: "join",
44573
- value: function join4(s) {
44573
+ value: function join5(s) {
44574
44574
  if (this.length === 0) return "";
44575
44575
  var p = this.head;
44576
44576
  var ret2 = "" + p.data;
@@ -47366,7 +47366,7 @@ var require_buffer_list5 = __commonJS({
47366
47366
  }
47367
47367
  }, {
47368
47368
  key: "join",
47369
- value: function join4(s) {
47369
+ value: function join5(s) {
47370
47370
  if (this.length === 0) return "";
47371
47371
  var p = this.head;
47372
47372
  var ret2 = "" + p.data;
@@ -50236,7 +50236,7 @@ var require_buffer_list6 = __commonJS({
50236
50236
  }
50237
50237
  }, {
50238
50238
  key: "join",
50239
- value: function join4(s) {
50239
+ value: function join5(s) {
50240
50240
  if (this.length === 0) return "";
50241
50241
  var p = this.head;
50242
50242
  var ret2 = "" + p.data;
@@ -53025,7 +53025,7 @@ var require_buffer_list7 = __commonJS({
53025
53025
  }
53026
53026
  }, {
53027
53027
  key: "join",
53028
- value: function join4(s) {
53028
+ value: function join5(s) {
53029
53029
  if (this.length === 0) return "";
53030
53030
  var p = this.head;
53031
53031
  var ret2 = "" + p.data;
@@ -56015,7 +56015,7 @@ var require_buffer_list8 = __commonJS({
56015
56015
  }
56016
56016
  }, {
56017
56017
  key: "join",
56018
- value: function join4(s) {
56018
+ value: function join5(s) {
56019
56019
  if (this.length === 0) return "";
56020
56020
  var p = this.head;
56021
56021
  var ret2 = "" + p.data;
@@ -66273,7 +66273,7 @@ var require_BufferList3 = __commonJS({
66273
66273
  this.head = this.tail = null;
66274
66274
  this.length = 0;
66275
66275
  };
66276
- BufferList.prototype.join = function join4(s) {
66276
+ BufferList.prototype.join = function join5(s) {
66277
66277
  if (this.length === 0) return "";
66278
66278
  var p = this.head;
66279
66279
  var ret2 = "" + p.data;
@@ -90687,6 +90687,393 @@ var require_cjs = __commonJS({
90687
90687
  }
90688
90688
  });
90689
90689
 
90690
+ // packages/core/dist/document/pdf-encrypted.js
90691
+ var ENCRYPTED_PDF_MESSAGE, PdfEncryptedError;
90692
+ var init_pdf_encrypted = __esm({
90693
+ "packages/core/dist/document/pdf-encrypted.js"() {
90694
+ "use strict";
90695
+ ENCRYPTED_PDF_MESSAGE = "PDF is password-protected (encrypted); decrypt first: pdf-ops decrypt <in> <password> <out>";
90696
+ PdfEncryptedError = class extends Error {
90697
+ constructor() {
90698
+ super(ENCRYPTED_PDF_MESSAGE);
90699
+ this.name = "PdfEncryptedError";
90700
+ }
90701
+ };
90702
+ }
90703
+ });
90704
+
90705
+ // packages/core/dist/document/pdf-extract.js
90706
+ var pdf_extract_exports = {};
90707
+ __export(pdf_extract_exports, {
90708
+ analyzePdfDocument: () => analyzePdfDocument,
90709
+ countVectorOperators: () => countVectorOperators,
90710
+ decryptPdf: () => decryptPdf,
90711
+ encryptPdf: () => encryptPdf,
90712
+ extractPdfImages: () => extractPdfImages,
90713
+ extractPdfTables: () => extractPdfTables,
90714
+ extractPdfText: () => extractPdfText,
90715
+ extractPdfTextLines: () => extractPdfTextLines,
90716
+ pdfNeedsPassword: () => pdfNeedsPassword,
90717
+ renderPdfPages: () => renderPdfPages,
90718
+ scanPdfSignals: () => scanPdfSignals,
90719
+ textMathHint: () => textMathHint,
90720
+ textTableHint: () => textTableHint
90721
+ });
90722
+ function loadMupdf() {
90723
+ if (!_mupdf) {
90724
+ const deps = process.env.TALOS_SKILL_DEPS;
90725
+ const spec = deps ? (0, import_node_url.pathToFileURL)((0, import_node_path.join)(deps, "mupdf", "dist", "mupdf.js")).href : "mupdf";
90726
+ _mupdf = import(spec);
90727
+ }
90728
+ return _mupdf;
90729
+ }
90730
+ function textMathHint(text) {
90731
+ if (!text)
90732
+ return false;
90733
+ const hits = (text.match(MATH_CHARS) || []).length;
90734
+ return hits >= 3 || hits / Math.max(text.length, 1) > 0.02;
90735
+ }
90736
+ function textTableHint(text) {
90737
+ if (!text)
90738
+ return false;
90739
+ const gaps = (text.match(/ {2,}|\t/g) || []).length;
90740
+ return gaps >= 6;
90741
+ }
90742
+ async function openDoc(bytes) {
90743
+ const mupdf = await loadMupdf();
90744
+ const doc = mupdf.Document.openDocument(bytes, "application/pdf");
90745
+ if (doc.needsPassword())
90746
+ throw new PdfEncryptedError();
90747
+ if (doc.countPages() === 0) {
90748
+ throw new Error("PDF has no readable pages (corrupt, encrypted, or empty)");
90749
+ }
90750
+ return { mupdf, doc };
90751
+ }
90752
+ async function analyzePdfDocument(bytes, options = {}) {
90753
+ const wantText = options.text !== false;
90754
+ const wantSignals = options.signals === true;
90755
+ const { doc } = await openDoc(bytes);
90756
+ const pageCount = doc.countPages();
90757
+ const signals = [];
90758
+ let out = "";
90759
+ for (let i = 0; i < pageCount; i++) {
90760
+ const page = doc.loadPage(i);
90761
+ if (wantText) {
90762
+ out += `
90763
+ [page ${i + 1}]
90764
+ ${page.toStructuredText().asText()}`;
90765
+ }
90766
+ if (wantSignals) {
90767
+ signals.push(pageSignalsFrom(page, i + 1));
90768
+ }
90769
+ }
90770
+ return { text: wantText ? out.trim() : "", pageCount, signals };
90771
+ }
90772
+ async function extractPdfText(bytes) {
90773
+ const { text, pageCount } = await analyzePdfDocument(bytes, { text: true });
90774
+ return { text, pageCount };
90775
+ }
90776
+ function countVectorOperators(content) {
90777
+ let ops = 0;
90778
+ let curves = 0;
90779
+ let fills = 0;
90780
+ let i = 0;
90781
+ const n = content.length;
90782
+ while (i < n) {
90783
+ const ch = content[i];
90784
+ if (ch === "(") {
90785
+ let depth = 1;
90786
+ i++;
90787
+ while (i < n && depth > 0) {
90788
+ if (content[i] === "\\")
90789
+ i++;
90790
+ else if (content[i] === "(")
90791
+ depth++;
90792
+ else if (content[i] === ")")
90793
+ depth--;
90794
+ i++;
90795
+ }
90796
+ continue;
90797
+ }
90798
+ if (ch === "<" && content[i + 1] !== "<") {
90799
+ while (i < n && content[i] !== ">")
90800
+ i++;
90801
+ i++;
90802
+ continue;
90803
+ }
90804
+ if (ch === "I" && content[i + 1] === "D" && isDelimiter(content[i + 2])) {
90805
+ const end = content.indexOf("EI", i + 2);
90806
+ i = end === -1 ? n : end + 2;
90807
+ continue;
90808
+ }
90809
+ if (!isDelimiter(ch)) {
90810
+ const start = i;
90811
+ while (i < n && !isDelimiter(content[i]))
90812
+ i++;
90813
+ const token = content.slice(start, i);
90814
+ switch (token) {
90815
+ case "m":
90816
+ case "l":
90817
+ case "re":
90818
+ ops++;
90819
+ break;
90820
+ case "c":
90821
+ case "v":
90822
+ case "y":
90823
+ ops++;
90824
+ curves++;
90825
+ break;
90826
+ case "f":
90827
+ case "F":
90828
+ case "f*":
90829
+ fills++;
90830
+ break;
90831
+ case "B":
90832
+ case "B*":
90833
+ case "b":
90834
+ case "b*":
90835
+ fills++;
90836
+ break;
90837
+ default:
90838
+ break;
90839
+ }
90840
+ continue;
90841
+ }
90842
+ i++;
90843
+ }
90844
+ return { ops, curves, fills };
90845
+ }
90846
+ function isDelimiter(ch) {
90847
+ return ch === void 0 || ch === " " || ch === "\n" || ch === "\r" || ch === " " || ch === "\f" || ch === "\0" || ch === "[" || ch === "]" || ch === "/" || ch === "{" || ch === "}";
90848
+ }
90849
+ function pageContentStream(page) {
90850
+ try {
90851
+ const obj2 = page.getObject?.();
90852
+ const contents = obj2?.get?.("Contents");
90853
+ if (!contents)
90854
+ return "";
90855
+ const streams = contents.isArray?.() && contents.get ? Array.from({ length: contents.length ?? 0 }, (_2, i) => contents.get(i)) : [contents];
90856
+ let out = "";
90857
+ for (const stream of streams) {
90858
+ const buf = stream?.readStream?.();
90859
+ const bytes = buf?.asUint8Array?.();
90860
+ if (bytes)
90861
+ out += Buffer.from(bytes).toString("latin1");
90862
+ }
90863
+ return out;
90864
+ } catch {
90865
+ return "";
90866
+ }
90867
+ }
90868
+ function pageSignalsFrom(page, pageNumber) {
90869
+ const json = JSON.parse(page.toStructuredText("preserve-images").asJSON());
90870
+ const blocks = json.blocks ?? [];
90871
+ const text = blocks.flatMap((b) => (b.lines ?? []).map((l2) => l2.text ?? "")).join(" ");
90872
+ const [x0, y0, x1, y1] = page.getBounds();
90873
+ const pageArea = Math.max((x1 - x0) * (y1 - y0), 1);
90874
+ const imgArea = blocks.filter((b) => b.type === "image").reduce((a, b) => a + Math.max((b.bbox?.w ?? 0) * (b.bbox?.h ?? 0), 0), 0);
90875
+ const vector = countVectorOperators(pageContentStream(page));
90876
+ return {
90877
+ page: pageNumber,
90878
+ textChars: text.trim().length,
90879
+ imageCoverage: Math.min(imgArea / pageArea, 1),
90880
+ vectorOps: vector.ops,
90881
+ vectorCurves: vector.curves,
90882
+ vectorFills: vector.fills,
90883
+ mathHint: textMathHint(text),
90884
+ tableHint: textTableHint(text)
90885
+ };
90886
+ }
90887
+ async function scanPdfSignals(bytes) {
90888
+ const { signals } = await analyzePdfDocument(bytes, { text: false, signals: true });
90889
+ return signals;
90890
+ }
90891
+ function median(nums) {
90892
+ if (!nums.length)
90893
+ return 0;
90894
+ const s = [...nums].sort((a, b) => a - b);
90895
+ return s[Math.floor(s.length / 2)];
90896
+ }
90897
+ async function extractPdfTables(bytes, pageNum, minColumns = 2) {
90898
+ const { doc } = await openDoc(bytes);
90899
+ const total = doc.countPages();
90900
+ const from = pageNum ? pageNum : 1;
90901
+ const to = pageNum ? pageNum : total;
90902
+ const out = [];
90903
+ for (let p = from; p <= to; p++) {
90904
+ if (p < 1 || p > total)
90905
+ continue;
90906
+ const json = JSON.parse(doc.loadPage(p - 1).toStructuredText().asJSON());
90907
+ const cells = [];
90908
+ for (const b of json.blocks ?? []) {
90909
+ if (b.type === "image")
90910
+ continue;
90911
+ for (const l2 of b.lines ?? []) {
90912
+ const text = (l2.text ?? "").trim();
90913
+ if (!text)
90914
+ continue;
90915
+ cells.push({ text, x: l2.bbox?.x ?? 0, y: l2.bbox?.y ?? 0, h: l2.bbox?.h ?? 12 });
90916
+ }
90917
+ }
90918
+ if (cells.length < 2)
90919
+ continue;
90920
+ const medH = median(cells.map((c) => c.h)) || 12;
90921
+ const rowTol = Math.max(4, medH * 0.6);
90922
+ const colTol = Math.max(6, medH * 0.9);
90923
+ const byY = [...cells].sort((a, b) => a.y - b.y);
90924
+ const rowGroups = [];
90925
+ let rowRef = Number.NEGATIVE_INFINITY;
90926
+ for (const c of byY) {
90927
+ if (c.y - rowRef > rowTol) {
90928
+ rowGroups.push([]);
90929
+ rowRef = c.y;
90930
+ }
90931
+ rowGroups[rowGroups.length - 1].push(c);
90932
+ }
90933
+ const colRefs = [];
90934
+ for (const x of cells.map((c) => c.x).sort((a, b) => a - b)) {
90935
+ if (!colRefs.length || x - colRefs[colRefs.length - 1] > colTol)
90936
+ colRefs.push(x);
90937
+ }
90938
+ const colIndex = (x) => {
90939
+ let best = 0, bd = Infinity;
90940
+ colRefs.forEach((r, i) => {
90941
+ const d = Math.abs(x - r);
90942
+ if (d < bd) {
90943
+ bd = d;
90944
+ best = i;
90945
+ }
90946
+ });
90947
+ return best;
90948
+ };
90949
+ const rows = [];
90950
+ for (const group of rowGroups) {
90951
+ const arr = Array(colRefs.length).fill("");
90952
+ for (const c of group.sort((a, b) => a.x - b.x)) {
90953
+ const i = colIndex(c.x);
90954
+ arr[i] = arr[i] ? `${arr[i]} ${c.text}` : c.text;
90955
+ }
90956
+ if (arr.filter((v) => v).length >= minColumns)
90957
+ rows.push(arr);
90958
+ }
90959
+ if (rows.length)
90960
+ out.push({ page: p, rows });
90961
+ }
90962
+ return out;
90963
+ }
90964
+ async function extractPdfTextLines(bytes) {
90965
+ const { doc } = await openDoc(bytes);
90966
+ const out = [];
90967
+ for (let p = 1; p <= doc.countPages(); p++) {
90968
+ const page = doc.loadPage(p - 1);
90969
+ const [x0, y0, x1, y1] = page.getBounds();
90970
+ const json = JSON.parse(page.toStructuredText().asJSON());
90971
+ const lines = [];
90972
+ for (const b of json.blocks ?? []) {
90973
+ if (b.type === "image")
90974
+ continue;
90975
+ for (const l2 of b.lines ?? []) {
90976
+ const text = l2.text ?? "";
90977
+ if (!text.trim() || !l2.bbox)
90978
+ continue;
90979
+ lines.push({
90980
+ x: l2.bbox.x ?? 0,
90981
+ y: l2.bbox.y ?? 0,
90982
+ w: l2.bbox.w ?? 0,
90983
+ h: l2.bbox.h ?? 0,
90984
+ size: l2.font?.size ?? 12,
90985
+ text
90986
+ });
90987
+ }
90988
+ }
90989
+ out.push({ page: p, width: x1 - x0, height: y1 - y0, lines });
90990
+ }
90991
+ return out;
90992
+ }
90993
+ async function renderPdfPages(bytes, pages, scale = 2, maxEdge) {
90994
+ const { mupdf, doc } = await openDoc(bytes);
90995
+ const total = doc.countPages();
90996
+ const wanted = pages === "all" ? Array.from({ length: total }, (_2, i) => i + 1) : pages;
90997
+ const out = [];
90998
+ for (const p of wanted) {
90999
+ if (p < 1 || p > total)
91000
+ continue;
91001
+ const page = doc.loadPage(p - 1);
91002
+ const [x0, y0, x1, y1] = page.getBounds();
91003
+ const longest = Math.max(x1 - x0, y1 - y0);
91004
+ const s = maxEdge && longest > 0 ? Math.min(scale, maxEdge / longest) : scale;
91005
+ const pix = page.toPixmap(mupdf.Matrix.scale(s, s), mupdf.ColorSpace.DeviceRGB, false, true);
91006
+ out.push({ page: p, png: pix.asPNG() });
91007
+ }
91008
+ return out;
91009
+ }
91010
+ async function extractPdfImages(bytes, pages) {
91011
+ const { doc } = await openDoc(bytes);
91012
+ const total = doc.countPages();
91013
+ const wanted = pages && pages.length ? pages : Array.from({ length: total }, (_2, i) => i + 1);
91014
+ const images = [];
91015
+ let skipped = 0;
91016
+ for (const p of wanted) {
91017
+ if (p < 1 || p > total)
91018
+ continue;
91019
+ const stext = doc.loadPage(p - 1).toStructuredText("preserve-images");
91020
+ let index = 0;
91021
+ stext.walk({
91022
+ onImageBlock(bbox, _matrix, image) {
91023
+ const slot = index++;
91024
+ try {
91025
+ const png = image.toPixmap().asPNG();
91026
+ images.push({
91027
+ page: p,
91028
+ index: slot,
91029
+ bbox: [bbox[0], bbox[1], bbox[2], bbox[3]],
91030
+ width: image.getWidth(),
91031
+ height: image.getHeight(),
91032
+ png
91033
+ });
91034
+ } catch {
91035
+ skipped++;
91036
+ }
91037
+ }
91038
+ });
91039
+ }
91040
+ return { images, skipped };
91041
+ }
91042
+ async function encryptPdf(bytes, userPassword, ownerPassword) {
91043
+ if (!userPassword)
91044
+ throw new Error("a non-empty user password is required");
91045
+ const { doc } = await openDoc(bytes);
91046
+ const opts = `encrypt=aes-256,user-password=${userPassword},owner-password=${ownerPassword || userPassword}`;
91047
+ return doc.saveToBuffer(opts).asUint8Array();
91048
+ }
91049
+ async function pdfNeedsPassword(bytes) {
91050
+ const mupdf = await loadMupdf();
91051
+ const doc = mupdf.Document.openDocument(bytes, "application/pdf");
91052
+ return doc.needsPassword();
91053
+ }
91054
+ async function decryptPdf(bytes, password) {
91055
+ const mupdf = await loadMupdf();
91056
+ const doc = mupdf.Document.openDocument(bytes, "application/pdf");
91057
+ if (doc.needsPassword()) {
91058
+ if (doc.authenticatePassword(password) === 0)
91059
+ throw new Error("wrong password");
91060
+ }
91061
+ if (doc.countPages() === 0)
91062
+ throw new Error("PDF has no readable pages (corrupt or empty)");
91063
+ return doc.saveToBuffer("encrypt=none").asUint8Array();
91064
+ }
91065
+ var import_node_path, import_node_url, _mupdf, MATH_CHARS;
91066
+ var init_pdf_extract = __esm({
91067
+ "packages/core/dist/document/pdf-extract.js"() {
91068
+ "use strict";
91069
+ import_node_path = require("node:path");
91070
+ import_node_url = require("node:url");
91071
+ init_pdf_encrypted();
91072
+ _mupdf = null;
91073
+ MATH_CHARS = /[=∑∫√≤≥≠±∞πθλμσΩ×÷∂∇→←↔∈∉⊂⊃∀∃]/g;
91074
+ }
91075
+ });
91076
+
90690
91077
  // apps/admin/data/skills.lib/document/bin-src/doc-gen.mjs
90691
91078
  var import_promises2 = require("node:fs/promises");
90692
91079
 
@@ -94311,7 +94698,7 @@ var require_buffer_list = /* @__PURE__ */ __commonJSMin(((exports2, module2) =>
94311
94698
  },
94312
94699
  {
94313
94700
  key: "join",
94314
- value: function join4(s) {
94701
+ value: function join5(s) {
94315
94702
  if (this.length === 0) return "";
94316
94703
  var p = this.head;
94317
94704
  var ret2 = "" + p.data;
@@ -98748,6 +99135,46 @@ var createShading = ({ fill, color, type }) => new BuilderElement({
98748
99135
  }
98749
99136
  }
98750
99137
  });
99138
+ var ShadingType = {
99139
+ /** Clear shading - no pattern, fill color only */
99140
+ CLEAR: "clear",
99141
+ DIAGONAL_CROSS: "diagCross",
99142
+ DIAGONAL_STRIPE: "diagStripe",
99143
+ HORIZONTAL_CROSS: "horzCross",
99144
+ HORIZONTAL_STRIPE: "horzStripe",
99145
+ NIL: "nil",
99146
+ PERCENT_5: "pct5",
99147
+ PERCENT_10: "pct10",
99148
+ PERCENT_12: "pct12",
99149
+ PERCENT_15: "pct15",
99150
+ PERCENT_20: "pct20",
99151
+ PERCENT_25: "pct25",
99152
+ PERCENT_30: "pct30",
99153
+ PERCENT_35: "pct35",
99154
+ PERCENT_37: "pct37",
99155
+ PERCENT_40: "pct40",
99156
+ PERCENT_45: "pct45",
99157
+ PERCENT_50: "pct50",
99158
+ PERCENT_55: "pct55",
99159
+ PERCENT_60: "pct60",
99160
+ PERCENT_62: "pct62",
99161
+ PERCENT_65: "pct65",
99162
+ PERCENT_70: "pct70",
99163
+ PERCENT_75: "pct75",
99164
+ PERCENT_80: "pct80",
99165
+ PERCENT_85: "pct85",
99166
+ PERCENT_87: "pct87",
99167
+ PERCENT_90: "pct90",
99168
+ PERCENT_95: "pct95",
99169
+ REVERSE_DIAGONAL_STRIPE: "reverseDiagStripe",
99170
+ SOLID: "solid",
99171
+ THIN_DIAGONAL_CROSS: "thinDiagCross",
99172
+ THIN_DIAGONAL_STRIPE: "thinDiagStripe",
99173
+ THIN_HORIZONTAL_CROSS: "thinHorzCross",
99174
+ THIN_REVERSE_DIAGONAL_STRIPE: "thinReverseDiagStripe",
99175
+ THIN_VERTICAL_STRIPE: "thinVertStripe",
99176
+ VERTICAL_STRIPE: "vertStripe"
99177
+ };
98751
99178
  var ChangeAttributes = class extends XmlAttributeComponent {
98752
99179
  constructor(..._args) {
98753
99180
  super(..._args);
@@ -110210,8 +110637,9 @@ function docxBlock(b, theme) {
110210
110637
  const isHeader = Boolean(b.header && i === 0);
110211
110638
  return new TableCell({
110212
110639
  width: { size: columnWidths[columnIndex] ?? 0, type: WidthType.DXA },
110213
- // Themed header row background (the visible "not cheap" cue).
110214
- ...isHeader ? { shading: { fill: bareHex(theme.colors.tableHeaderBg) } } : {},
110640
+ // Themed header row background (the visible "not cheap" cue). w:shd
110641
+ // requires w:val — a fill-only shading fails the strict XSD gate.
110642
+ ...isHeader ? { shading: { type: ShadingType.CLEAR, color: "auto", fill: bareHex(theme.colors.tableHeaderBg) } } : {},
110215
110643
  children: [new Paragraph({ children: [new TextRun({ text: String(c), bold: isHeader })] })]
110216
110644
  });
110217
110645
  })
@@ -110424,6 +110852,17 @@ async function validatePdf(bytes) {
110424
110852
  try {
110425
110853
  const doc = await import_pdf_lib.PDFDocument.load(bytes, { ignoreEncryption: true, throwOnInvalidObject: true, updateMetadata: false });
110426
110854
  pages = doc.getPageCount();
110855
+ if (doc.isEncrypted) {
110856
+ const { pdfNeedsPassword: pdfNeedsPassword2 } = await Promise.resolve().then(() => (init_pdf_extract(), pdf_extract_exports));
110857
+ if (await pdfNeedsPassword2(bytes).catch(() => true)) {
110858
+ errors.push({
110859
+ code: "pdf-encrypted",
110860
+ message: "the PDF needs a password to open, so its content cannot be checked or rendered \u2014 validate (and render) the unencrypted file before `pdf-ops encrypt`; to inspect this one, `pdf-ops decrypt <in> <password> <out>` first"
110861
+ });
110862
+ return { ok: false, kind: "pdf", errors, warnings, pages };
110863
+ }
110864
+ warnings.push({ code: "pdf-encrypted", message: 'the PDF is encrypted with an owner password only: it opens without a password, but pdf-form / pdf-ops cannot edit it until `pdf-ops decrypt <in> "" <out>`' });
110865
+ }
110427
110866
  if (pages === 0)
110428
110867
  errors.push({ code: "pdf-no-pages", message: "the PDF parses but has no pages" });
110429
110868
  else
@@ -110652,40 +111091,8 @@ async function validateOoxml(bytes) {
110652
111091
  return result(root.kind);
110653
111092
  }
110654
111093
 
110655
- // packages/core/dist/document/pdf-extract.js
110656
- var import_node_path = require("node:path");
110657
- var import_node_url = require("node:url");
110658
- var _mupdf = null;
110659
- function loadMupdf() {
110660
- if (!_mupdf) {
110661
- const deps = process.env.TALOS_SKILL_DEPS;
110662
- const spec = deps ? (0, import_node_url.pathToFileURL)((0, import_node_path.join)(deps, "mupdf", "dist", "mupdf.js")).href : "mupdf";
110663
- _mupdf = import(spec);
110664
- }
110665
- return _mupdf;
110666
- }
110667
- async function openDoc(bytes) {
110668
- const mupdf = await loadMupdf();
110669
- const doc = mupdf.Document.openDocument(bytes, "application/pdf");
110670
- if (doc.countPages() === 0) {
110671
- throw new Error("PDF has no readable pages (corrupt, encrypted, or empty)");
110672
- }
110673
- return { mupdf, doc };
110674
- }
110675
- async function renderPdfPages(bytes, pages, scale = 2) {
110676
- const { mupdf, doc } = await openDoc(bytes);
110677
- const total = doc.countPages();
110678
- const wanted = pages === "all" ? Array.from({ length: total }, (_2, i) => i + 1) : pages;
110679
- const out = [];
110680
- const matrix = mupdf.Matrix.scale(scale, scale);
110681
- for (const p of wanted) {
110682
- if (p < 1 || p > total)
110683
- continue;
110684
- const pix = doc.loadPage(p - 1).toPixmap(matrix, mupdf.ColorSpace.DeviceRGB, false, true);
110685
- out.push({ page: p, png: pix.asPNG() });
110686
- }
110687
- return out;
110688
- }
111094
+ // apps/admin/data/skills.lib/document/bin-src/doc-gen.mjs
111095
+ init_pdf_extract();
110689
111096
 
110690
111097
  // packages/core/dist/document/html-escape.js
110691
111098
  function escapeHtml(value) {
@@ -110870,6 +111277,11 @@ async function fontManifest() {
110870
111277
  }
110871
111278
  return _fontManifest;
110872
111279
  }
111280
+ var _faceB64 = /* @__PURE__ */ new Map();
111281
+ async function faceBase64(path) {
111282
+ if (!_faceB64.has(path)) _faceB64.set(path, (await (0, import_promises.readFile)(path).catch(() => null))?.toString("base64") ?? null);
111283
+ return _faceB64.get(path);
111284
+ }
110873
111285
  async function faceCss(entries, range, doc) {
110874
111286
  const { dir } = await fontManifest();
110875
111287
  const rules = [];
@@ -110878,7 +111290,7 @@ async function faceCss(entries, range, doc) {
110878
111290
  if (!families.length) continue;
110879
111291
  const path = (0, import_node_path2.join)(dir, entry.file);
110880
111292
  if (!(0, import_node_fs.existsSync)(path)) continue;
110881
- const b64 = (await (0, import_promises.readFile)(path).catch(() => null))?.toString("base64");
111293
+ const b64 = await faceBase64(path);
110882
111294
  if (!b64) continue;
110883
111295
  for (const family of families) {
110884
111296
  rules.push(
@@ -110888,22 +111300,21 @@ async function faceCss(entries, range, doc) {
110888
111300
  }
110889
111301
  return rules.join("");
110890
111302
  }
110891
- var _cjkFontCss;
111303
+ var _faceCssBySet = /* @__PURE__ */ new Map();
111304
+ async function cachedFaceCss(group, entries, range, doc) {
111305
+ const named = (entries ?? []).flatMap((e) => (e.families ?? []).filter((f) => doc.includes(f)));
111306
+ const key = `${group}|${named.join("|")}`;
111307
+ if (!_faceCssBySet.has(key)) _faceCssBySet.set(key, await faceCss(entries, range, doc));
111308
+ return _faceCssBySet.get(key);
111309
+ }
110892
111310
  async function cjkFontFaceCss(doc) {
110893
- if (_cjkFontCss === void 0) {
110894
- const m = await fontManifest();
110895
- _cjkFontCss = await faceCss(m.cjk, m.cjkUnicodeRange, doc);
110896
- }
110897
- return _cjkFontCss;
111311
+ if (process.env.TALOS_CJK_FONTS === "system") return "";
111312
+ const m = await fontManifest();
111313
+ return cachedFaceCss("cjk", m.cjk, m.cjkUnicodeRange, doc);
110898
111314
  }
110899
- var _latinFontCss;
110900
111315
  async function latinFontFaceCss(doc) {
110901
- if (_latinFontCss === void 0) {
110902
- const m = await fontManifest();
110903
- const wanted = (m.latin ?? []).filter((e) => (e.families ?? []).some((f) => doc.includes(f)));
110904
- _latinFontCss = await faceCss(wanted, void 0);
110905
- }
110906
- return _latinFontCss;
111316
+ const m = await fontManifest();
111317
+ return cachedFaceCss("latin", m.latin, void 0, doc);
110907
111318
  }
110908
111319
  function injectStyle(html, css) {
110909
111320
  const tag = `<style>${css}</style>`;
@@ -110927,8 +111338,8 @@ async function renderPdfHeadless(html, print = {}) {
110927
111338
  }
110928
111339
 
110929
111340
  // apps/admin/data/skills.lib/document/bin-src/doc-gen.mjs
110930
- var import_node_path3 = require("node:path");
110931
- var import_node_fs2 = require("node:fs");
111341
+ var import_node_path4 = require("node:path");
111342
+ var import_node_fs3 = require("node:fs");
110932
111343
  var import_node_crypto = require("node:crypto");
110933
111344
 
110934
111345
  // packages/core/dist/document/doc-palette.js
@@ -111166,6 +111577,142 @@ function listPalettes() {
111166
111577
  return PALETTE_LIBRARY.map(({ name, mood, dark }) => ({ name, mood, dark }));
111167
111578
  }
111168
111579
 
111580
+ // packages/core/dist/document/deck-catalog.js
111581
+ var KINDS = ["skin", "pack"];
111582
+ var MODES = ["light", "dark"];
111583
+ var RENDER_MODES = ["classic-v1", "theme-v2"];
111584
+ var ID_RE = /^[a-z0-9][a-z0-9-]*$/;
111585
+ var COMMIT_RE = /^[0-9a-f]{7,40}$/;
111586
+ function isStr(v) {
111587
+ return typeof v === "string" && v.length > 0;
111588
+ }
111589
+ function isStrArr(v) {
111590
+ return Array.isArray(v) && v.every((x) => typeof x === "string");
111591
+ }
111592
+ function parseCatalog(raw) {
111593
+ const errors = [];
111594
+ const o = raw;
111595
+ if (!o || typeof o !== "object")
111596
+ return { catalog: null, errors: ["catalog is not an object"] };
111597
+ const groups = [];
111598
+ const rawGroups = Array.isArray(o.groups) ? o.groups : [];
111599
+ if (!Array.isArray(o.groups))
111600
+ errors.push("catalog.groups must be an array");
111601
+ rawGroups.forEach((g, i) => {
111602
+ const at = `group[${i}]`;
111603
+ if (!isStr(g?.id) || !ID_RE.test(g.id))
111604
+ errors.push(`${at}: bad id`);
111605
+ if (!isStr(g?.name_cn))
111606
+ errors.push(`${at}: missing name_cn`);
111607
+ if (!isStr(g?.sample))
111608
+ errors.push(`${at}: missing sample`);
111609
+ if (!isStrArr(g?.scenarios))
111610
+ errors.push(`${at}: scenarios must be a string array`);
111611
+ if (isStr(g?.id) && isStr(g?.name_cn) && isStr(g?.sample) && isStrArr(g?.scenarios)) {
111612
+ groups.push({ id: g.id, name_cn: g.name_cn, sample: g.sample, scenarios: g.scenarios });
111613
+ }
111614
+ });
111615
+ const collections = [];
111616
+ const rawCollections = o.collections === void 0 ? [] : Array.isArray(o.collections) ? o.collections : [];
111617
+ if (o.collections !== void 0 && !Array.isArray(o.collections))
111618
+ errors.push("catalog.collections must be an array when present");
111619
+ rawCollections.forEach((c, i) => {
111620
+ const at = `collection[${i}]`;
111621
+ const before = errors.length;
111622
+ if (!isStr(c?.id) || !ID_RE.test(c.id))
111623
+ errors.push(`${at}: bad id`);
111624
+ if (!isStr(c?.name_cn))
111625
+ errors.push(`${at}: missing name_cn`);
111626
+ if (!isStr(c?.renderMode) || !RENDER_MODES.includes(c.renderMode))
111627
+ errors.push(`${at}: bad renderMode`);
111628
+ if (!isStrArr(c?.entryIds) || c.entryIds.length === 0)
111629
+ errors.push(`${at}: entryIds must be a non-empty string array`);
111630
+ if (c?.sourceCommit !== void 0 && (!isStr(c.sourceCommit) || !COMMIT_RE.test(c.sourceCommit)))
111631
+ errors.push(`${at}: bad sourceCommit`);
111632
+ if (c?.renderMode === "classic-v1" && c?.sourceCommit === void 0)
111633
+ errors.push(`${at}: classic-v1 requires sourceCommit`);
111634
+ if (errors.length === before) {
111635
+ collections.push({
111636
+ id: c.id,
111637
+ name_cn: c.name_cn,
111638
+ renderMode: c.renderMode,
111639
+ entryIds: c.entryIds,
111640
+ ...c.sourceCommit !== void 0 ? { sourceCommit: c.sourceCommit } : {}
111641
+ });
111642
+ }
111643
+ });
111644
+ const entries = [];
111645
+ const rawEntries = Array.isArray(o.entries) ? o.entries : [];
111646
+ if (!Array.isArray(o.entries))
111647
+ errors.push("catalog.entries must be an array");
111648
+ rawEntries.forEach((e, i) => {
111649
+ const at = `entry[${i}]${isStr(e?.id) ? ` (${e.id})` : ""}`;
111650
+ const before = errors.length;
111651
+ if (!isStr(e?.id) || !ID_RE.test(e.id))
111652
+ errors.push(`${at}: bad id`);
111653
+ if (!isStr(e?.kind) || !KINDS.includes(e.kind))
111654
+ errors.push(`${at}: kind must be "skin" or "pack"`);
111655
+ if (!isStr(e?.group))
111656
+ errors.push(`${at}: missing group`);
111657
+ if (!isStr(e?.name_cn))
111658
+ errors.push(`${at}: missing name_cn`);
111659
+ if (!isStr(e?.oneliner))
111660
+ errors.push(`${at}: missing oneliner`);
111661
+ if (!isStrArr(e?.scenarios))
111662
+ errors.push(`${at}: scenarios must be a string array`);
111663
+ if (!isStr(e?.mode) || !MODES.includes(e.mode))
111664
+ errors.push(`${at}: mode must be "light" or "dark"`);
111665
+ if (!isStr(e?.sample))
111666
+ errors.push(`${at}: missing sample`);
111667
+ if (e?.motion !== void 0 && !isStr(e.motion))
111668
+ errors.push(`${at}: motion must be a string when present`);
111669
+ if (errors.length === before) {
111670
+ entries.push({
111671
+ id: e.id,
111672
+ kind: e.kind,
111673
+ group: e.group,
111674
+ name_cn: e.name_cn,
111675
+ oneliner: e.oneliner,
111676
+ scenarios: e.scenarios,
111677
+ mode: e.mode,
111678
+ sample: e.sample,
111679
+ ...e.motion !== void 0 ? { motion: e.motion } : {}
111680
+ });
111681
+ }
111682
+ });
111683
+ return { catalog: errors.length ? null : { groups, ...collections.length ? { collections } : {}, entries }, errors };
111684
+ }
111685
+ function resolveEntry(catalog, id) {
111686
+ return catalog.entries.find((e) => e.id === id) ?? null;
111687
+ }
111688
+ function assetDirOf(entry) {
111689
+ return entry.kind === "skin" ? `styles/${entry.id}` : `packs/${entry.id}`;
111690
+ }
111691
+ function cssFileOf(entry) {
111692
+ return entry.kind === "skin" ? "skin.css" : "pack.css";
111693
+ }
111694
+
111695
+ // apps/admin/data/skills.lib/document/bin-src/lib/user-styles.mjs
111696
+ var import_node_fs2 = require("node:fs");
111697
+ var import_node_path3 = require("node:path");
111698
+ var SAFE_NAME = /^[A-Za-z0-9][A-Za-z0-9_-]*$/;
111699
+ function userStylesDir() {
111700
+ return process.env.TALOS_DOC_STYLES_DIR?.trim() || null;
111701
+ }
111702
+ function userSkinDir(name) {
111703
+ const root = userStylesDir();
111704
+ if (!root || !SAFE_NAME.test(name)) return null;
111705
+ const dir = (0, import_node_path3.join)(root, "styles", name);
111706
+ return (0, import_node_fs2.existsSync)((0, import_node_path3.join)(dir, "skin.css")) ? dir : null;
111707
+ }
111708
+ function userThemePath(name) {
111709
+ const root = userStylesDir();
111710
+ const base = name.replace(/\.json$/i, "");
111711
+ if (!root || !SAFE_NAME.test(base)) return null;
111712
+ const path = (0, import_node_path3.join)(root, "themes", `${base}.json`);
111713
+ return (0, import_node_fs2.existsSync)(path) ? path : null;
111714
+ }
111715
+
111169
111716
  // apps/admin/data/skills.lib/document/bin-src/doc-gen.mjs
111170
111717
  function die(msg, code = 1) {
111171
111718
  process.stderr.write(`doc-gen: ${msg}
@@ -111173,7 +111720,7 @@ function die(msg, code = 1) {
111173
111720
  process.exit(code);
111174
111721
  }
111175
111722
  async function writeArtifactAtomically(out, bytes) {
111176
- const temp = (0, import_node_path3.join)((0, import_node_path3.dirname)(out), `.${(0, import_node_path3.basename)(out)}.${process.pid}.${(0, import_node_crypto.randomUUID)()}.tmp`);
111723
+ const temp = (0, import_node_path4.join)((0, import_node_path4.dirname)(out), `.${(0, import_node_path4.basename)(out)}.${process.pid}.${(0, import_node_crypto.randomUUID)()}.tmp`);
111177
111724
  try {
111178
111725
  await (0, import_promises2.writeFile)(temp, Buffer.from(bytes), { flag: "wx" });
111179
111726
  await (0, import_promises2.rename)(temp, out);
@@ -111237,6 +111784,7 @@ async function main() {
111237
111784
  themePath = args[themeIdx + 1];
111238
111785
  if (!themePath) die("--theme needs a path to a theme.json", 64);
111239
111786
  args.splice(themeIdx, 2);
111787
+ if (!(0, import_node_fs3.existsSync)(themePath)) themePath = userThemePath(themePath) ?? themePath;
111240
111788
  }
111241
111789
  const topicFlag = takeFlag(args, "--topic");
111242
111790
  const paletteFlag = takeFlag(args, "--palette");
@@ -111272,9 +111820,18 @@ async function main() {
111272
111820
  } else if (accentFlag || styleFlag) {
111273
111821
  let accent = accentFlag;
111274
111822
  if (!accent && styleFlag) {
111275
- const skillDir = (0, import_node_path3.dirname)((0, import_node_path3.dirname)(process.argv[1] || ""));
111276
- const styleDir = (0, import_node_fs2.existsSync)((0, import_node_path3.join)(styleFlag, "skin.css")) ? styleFlag : (0, import_node_path3.join)(skillDir, "styles", styleFlag);
111277
- const skinPath = (0, import_node_path3.join)(styleDir, "skin.css");
111823
+ const skillDir = (0, import_node_path4.dirname)((0, import_node_path4.dirname)(process.argv[1] || ""));
111824
+ let skinPath = (0, import_node_path4.join)(styleFlag, "skin.css");
111825
+ if (!(0, import_node_fs3.existsSync)(skinPath)) {
111826
+ const user = userSkinDir(styleFlag);
111827
+ skinPath = user ? (0, import_node_path4.join)(user, "skin.css") : (0, import_node_path4.join)(skillDir, "styles", styleFlag, "skin.css");
111828
+ try {
111829
+ const { catalog } = parseCatalog(JSON.parse(await (0, import_promises2.readFile)((0, import_node_path4.join)(skillDir, "catalog.json"), "utf-8")));
111830
+ const entry = catalog ? resolveEntry(catalog, styleFlag) : null;
111831
+ if (entry) skinPath = (0, import_node_path4.join)(skillDir, assetDirOf(entry), cssFileOf(entry));
111832
+ } catch {
111833
+ }
111834
+ }
111278
111835
  let skinCss;
111279
111836
  try {
111280
111837
  skinCss = await (0, import_promises2.readFile)(skinPath, "utf-8");
@@ -111282,7 +111839,7 @@ async function main() {
111282
111839
  die(`--style: cannot read ${skinPath}`, 66);
111283
111840
  }
111284
111841
  accent = skinAccentColor(skinCss);
111285
- if (!accent) die(`--style ${styleFlag}: no hex --accent in skin.css`, 65);
111842
+ if (!accent) die(`--style ${styleFlag}: no hex --accent in ${skinPath}`, 65);
111286
111843
  }
111287
111844
  rawTheme = accentOnlyTheme(accent);
111288
111845
  if (!rawTheme.colors) process.stderr.write(`doc-gen: --accent ${accent}: not a #rrggbb hex; using plain black-on-white
@@ -111333,7 +111890,7 @@ async function reportPdf(out, bytes, spec, noPreview) {
111333
111890
  if (!noPreview && validation.ok) {
111334
111891
  try {
111335
111892
  for (const r of await renderPdfPages(bytes, "all", 1)) {
111336
- const dest = (0, import_node_path3.join)((0, import_node_path3.dirname)(out), `${(0, import_node_path3.basename)(out).replace(/\.[^.]+$/, "")}_p${r.page}.png`);
111893
+ const dest = (0, import_node_path4.join)((0, import_node_path4.dirname)(out), `${(0, import_node_path4.basename)(out).replace(/\.[^.]+$/, "")}_p${r.page}.png`);
111337
111894
  await (0, import_promises2.writeFile)(dest, Buffer.from(r.png));
111338
111895
  previews.push(dest);
111339
111896
  }