siluzan-tso-cli 1.1.48-beta.4 → 1.1.48-beta.5

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/README.md CHANGED
@@ -51,7 +51,7 @@ siluzan-tso init -d /path/to/skills # 写入自定义目录
51
51
  siluzan-tso init --force # 强制覆盖已存在文件
52
52
  ```
53
53
 
54
- > **注意**:当前为测试版(1.1.48-beta.4),供内部测试使用。正式发布后安装命令将改为 `npm install -g siluzan-tso-cli`。
54
+ > **注意**:当前为测试版(1.1.48-beta.5),供内部测试使用。正式发布后安装命令将改为 `npm install -g siluzan-tso-cli`。
55
55
 
56
56
  | 助手 | 建议 `--ai` |
57
57
  | ----------------------- | ------------------------------------ |
package/dist/index.js CHANGED
@@ -132486,9 +132486,46 @@ async function mergeFacebookSnapshotIntoReport(payload, snapshotDir) {
132486
132486
  };
132487
132487
  }
132488
132488
 
132489
- // src/commands/facebook-analysis/write-xlsx.ts
132489
+ // src/commands/facebook-analysis/xlsx-style-engine.ts
132490
132490
  import fs26 from "fs";
132491
132491
  import path33 from "path";
132492
+ var PLATFORM_ACCENTS = {
132493
+ default: "1F4E79",
132494
+ facebook: "1877F2",
132495
+ metaad: "1877F2",
132496
+ google: "1A73E8",
132497
+ tiktok: "FE2C55",
132498
+ yandex: "FC3F1D",
132499
+ bingv2: "0078D4",
132500
+ bing: "0078D4",
132501
+ kwai: "FF8300"
132502
+ };
132503
+ var NEUTRAL = {
132504
+ titleFill: "1F2937",
132505
+ subtitleFill: "DEEAF6",
132506
+ subtitleText: "404040",
132507
+ zebraFill: "F2F2F2",
132508
+ border: "BFBFBF",
132509
+ textDark: "262626",
132510
+ textMuted: "595959",
132511
+ white: "FFFFFF"
132512
+ };
132513
+ var FONT_NAME = "\u5FAE\u8F6F\u96C5\u9ED1";
132514
+ function resolveAccent(accent) {
132515
+ if (!accent) return PLATFORM_ACCENTS.default;
132516
+ const hex = accent.replace(/^#/, "");
132517
+ if (/^[0-9a-fA-F]{6}$/.test(hex)) return hex.toUpperCase();
132518
+ const key = accent.toLowerCase();
132519
+ return (PLATFORM_ACCENTS[key] || PLATFORM_ACCENTS.default).toUpperCase();
132520
+ }
132521
+ function relLuminance(hex) {
132522
+ const c = [0, 2, 4].map((i) => parseInt(hex.slice(i, i + 2), 16) / 255);
132523
+ const lin = (v) => v <= 0.03928 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4);
132524
+ return 0.2126 * lin(c[0]) + 0.7152 * lin(c[1]) + 0.0722 * lin(c[2]);
132525
+ }
132526
+ function textColorFor(bgHex) {
132527
+ return relLuminance(bgHex) > 0.55 ? NEUTRAL.textDark : NEUTRAL.white;
132528
+ }
132492
132529
  function xmlEscape(text) {
132493
132530
  return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
132494
132531
  }
@@ -132512,40 +132549,6 @@ function crc32(buf) {
132512
132549
  }
132513
132550
  return (crc ^ 4294967295) >>> 0;
132514
132551
  }
132515
- function sheetXml(rows) {
132516
- const body = rows.map((row, rIdx) => {
132517
- const r = rIdx + 1;
132518
- const cells = row.map((value, cIdx) => {
132519
- if (value == null || value === "") return "";
132520
- const ref = `${colLetter(cIdx)}${r}`;
132521
- if (typeof value === "number" && Number.isFinite(value)) {
132522
- return `<c r="${ref}"><v>${value}</v></c>`;
132523
- }
132524
- return `<c r="${ref}" t="inlineStr"><is><t>${xmlEscape(String(value))}</t></is></c>`;
132525
- }).join("");
132526
- return `<row r="${r}">${cells}</row>`;
132527
- }).join("");
132528
- return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?><worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"><sheetData>${body}</sheetData></worksheet>`;
132529
- }
132530
- function workbookXml(sheetNames) {
132531
- const sheets = sheetNames.map(
132532
- (name2, i) => `<sheet name="${xmlEscape(name2)}" sheetId="${i + 1}" r:id="rId${i + 1}"/>`
132533
- ).join("");
132534
- return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?><workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships"><sheets>${sheets}</sheets></workbook>`;
132535
- }
132536
- function workbookRelsXml(count) {
132537
- const rels = Array.from({ length: count }, (_, i) => {
132538
- return `<Relationship Id="rId${i + 1}" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet" Target="worksheets/sheet${i + 1}.xml"/>`;
132539
- }).join("");
132540
- return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?><Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">${rels}</Relationships>`;
132541
- }
132542
- function contentTypesXml(count) {
132543
- const overrides = Array.from({ length: count }, (_, i) => {
132544
- return `<Override PartName="/xl/worksheets/sheet${i + 1}.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"/>`;
132545
- }).join("");
132546
- return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?><Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types"><Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/><Default Extension="xml" ContentType="application/xml"/><Override PartName="/xl/workbook.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml"/>${overrides}</Types>`;
132547
- }
132548
- var ROOT_RELS = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?><Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"><Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="xl/workbook.xml"/></Relationships>`;
132549
132552
  function zipStore(files) {
132550
132553
  const locals = [];
132551
132554
  const centrals = [];
@@ -132556,34 +132559,20 @@ function zipStore(files) {
132556
132559
  const local = Buffer.alloc(30 + nameBuf.length);
132557
132560
  local.writeUInt32LE(67324752, 0);
132558
132561
  local.writeUInt16LE(20, 4);
132559
- local.writeUInt16LE(0, 6);
132560
- local.writeUInt16LE(0, 8);
132561
- local.writeUInt16LE(0, 10);
132562
- local.writeUInt16LE(0, 12);
132563
132562
  local.writeUInt32LE(crc, 14);
132564
132563
  local.writeUInt32LE(file.data.length, 18);
132565
132564
  local.writeUInt32LE(file.data.length, 22);
132566
132565
  local.writeUInt16LE(nameBuf.length, 26);
132567
- local.writeUInt16LE(0, 28);
132568
132566
  nameBuf.copy(local, 30);
132569
132567
  locals.push(local, file.data);
132570
132568
  const central = Buffer.alloc(46 + nameBuf.length);
132571
132569
  central.writeUInt32LE(33639248, 0);
132572
132570
  central.writeUInt16LE(20, 4);
132573
132571
  central.writeUInt16LE(20, 6);
132574
- central.writeUInt16LE(0, 8);
132575
- central.writeUInt16LE(0, 10);
132576
- central.writeUInt16LE(0, 12);
132577
- central.writeUInt16LE(0, 14);
132578
132572
  central.writeUInt32LE(crc, 16);
132579
132573
  central.writeUInt32LE(file.data.length, 20);
132580
132574
  central.writeUInt32LE(file.data.length, 24);
132581
132575
  central.writeUInt16LE(nameBuf.length, 28);
132582
- central.writeUInt16LE(0, 30);
132583
- central.writeUInt16LE(0, 32);
132584
- central.writeUInt16LE(0, 34);
132585
- central.writeUInt16LE(0, 36);
132586
- central.writeUInt32LE(0, 38);
132587
132576
  central.writeUInt32LE(offset, 42);
132588
132577
  nameBuf.copy(central, 46);
132589
132578
  centrals.push(central);
@@ -132592,38 +132581,441 @@ function zipStore(files) {
132592
132581
  const centralBuf = Buffer.concat(centrals);
132593
132582
  const end = Buffer.alloc(22);
132594
132583
  end.writeUInt32LE(101010256, 0);
132595
- end.writeUInt16LE(0, 4);
132596
- end.writeUInt16LE(0, 6);
132597
132584
  end.writeUInt16LE(files.length, 8);
132598
132585
  end.writeUInt16LE(files.length, 10);
132599
132586
  end.writeUInt32LE(centralBuf.length, 12);
132600
132587
  end.writeUInt32LE(offset, 16);
132601
- end.writeUInt16LE(0, 20);
132602
132588
  return Buffer.concat([...locals, centralBuf, end]);
132603
132589
  }
132604
- function writeSimpleXlsx(filePath, sheets) {
132590
+ var StyleRegistry = class {
132591
+ fonts = [{ size: 11, color: "000000", name: "\u5B8B\u4F53", bold: false }];
132592
+ fills = [{ kind: "none" }, { kind: "gray125" }];
132593
+ borders = [{ thin: false }];
132594
+ xfs = [{ fontId: 0, fillId: 0, borderId: 0, numFmtId: 0, align: null }];
132595
+ fontCache = /* @__PURE__ */ new Map();
132596
+ fillCache = /* @__PURE__ */ new Map();
132597
+ borderCache = /* @__PURE__ */ new Map();
132598
+ xfCache = /* @__PURE__ */ new Map();
132599
+ fontId(spec) {
132600
+ const key = JSON.stringify({ ...spec, name: spec.name || FONT_NAME });
132601
+ const cached = this.fontCache.get(key);
132602
+ if (cached !== void 0) return cached;
132603
+ const id = this.fonts.length;
132604
+ this.fonts.push({
132605
+ size: spec.size,
132606
+ color: spec.color,
132607
+ bold: spec.bold,
132608
+ name: spec.name || FONT_NAME
132609
+ });
132610
+ this.fontCache.set(key, id);
132611
+ return id;
132612
+ }
132613
+ fillId(hex) {
132614
+ if (!hex) return 0;
132615
+ const key = hex.toUpperCase();
132616
+ const cached = this.fillCache.get(key);
132617
+ if (cached !== void 0) return cached;
132618
+ const id = this.fills.length;
132619
+ this.fills.push({ kind: "solid", color: key });
132620
+ this.fillCache.set(key, id);
132621
+ return id;
132622
+ }
132623
+ borderId(thin) {
132624
+ const key = thin ? "thin" : "none";
132625
+ const cached = this.borderCache.get(key);
132626
+ if (cached !== void 0) return cached;
132627
+ const id = this.borders.length;
132628
+ this.borders.push({ thin });
132629
+ this.borderCache.set(key, id);
132630
+ return id;
132631
+ }
132632
+ xfId(spec) {
132633
+ const key = JSON.stringify(spec);
132634
+ const cached = this.xfCache.get(key);
132635
+ if (cached !== void 0) return cached;
132636
+ const id = this.xfs.length;
132637
+ this.xfs.push(spec);
132638
+ this.xfCache.set(key, id);
132639
+ return id;
132640
+ }
132641
+ resolve(opts) {
132642
+ const {
132643
+ fontSize = 10,
132644
+ fontColor = NEUTRAL.textDark,
132645
+ bold = false,
132646
+ fill,
132647
+ border = true,
132648
+ hAlign = "left",
132649
+ vAlign = "center",
132650
+ wrap = true,
132651
+ forceText = false
132652
+ } = opts;
132653
+ const fontId = this.fontId({ size: fontSize, color: fontColor, bold });
132654
+ const fillId = fill ? this.fillId(fill) : 0;
132655
+ const borderId = this.borderId(border);
132656
+ return this.xfId({
132657
+ fontId,
132658
+ fillId,
132659
+ borderId,
132660
+ numFmtId: forceText ? 49 : 0,
132661
+ align: { h: hAlign, v: vAlign, wrap }
132662
+ });
132663
+ }
132664
+ };
132665
+ function fontXml(f) {
132666
+ return `<font>${f.bold ? "<b/>" : ""}<sz val="${f.size}"/><color rgb="FF${f.color}"/><name val="${xmlEscape(f.name)}"/><charset val="134"/></font>`;
132667
+ }
132668
+ function fillXml(f) {
132669
+ if (f.kind === "none") return `<fill><patternFill patternType="none"/></fill>`;
132670
+ if (f.kind === "gray125") return `<fill><patternFill patternType="gray125"/></fill>`;
132671
+ return `<fill><patternFill patternType="solid"><fgColor rgb="FF${f.color}"/><bgColor indexed="64"/></patternFill></fill>`;
132672
+ }
132673
+ function borderXml(b) {
132674
+ if (!b.thin) return `<border><left/><right/><top/><bottom/><diagonal/></border>`;
132675
+ const side = `<color rgb="FF${NEUTRAL.border}"/>`;
132676
+ return `<border><left style="thin">${side}</left><right style="thin">${side}</right><top style="thin">${side}</top><bottom style="thin">${side}</bottom><diagonal/></border>`;
132677
+ }
132678
+ function xfXml(x) {
132679
+ const applies = [];
132680
+ let alignXml = "";
132681
+ if (x.align) {
132682
+ alignXml = `<alignment horizontal="${x.align.h}" vertical="${x.align.v}" wrapText="${x.align.wrap ? 1 : 0}"/>`;
132683
+ applies.push('applyAlignment="1"');
132684
+ }
132685
+ if (x.fontId) applies.push('applyFont="1"');
132686
+ if (x.fillId) applies.push('applyFill="1"');
132687
+ if (x.borderId) applies.push('applyBorder="1"');
132688
+ if (x.numFmtId) applies.push('applyNumberFormat="1"');
132689
+ return `<xf numFmtId="${x.numFmtId}" fontId="${x.fontId}" fillId="${x.fillId}" borderId="${x.borderId}" xfId="0" ${applies.join(" ")}>${alignXml}</xf>`;
132690
+ }
132691
+ function stylesXml(reg) {
132692
+ return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?><styleSheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"><fonts count="${reg.fonts.length}">${reg.fonts.map(fontXml).join("")}</fonts><fills count="${reg.fills.length}">${reg.fills.map(fillXml).join("")}</fills><borders count="${reg.borders.length}">${reg.borders.map(borderXml).join("")}</borders><cellStyleXfs count="1"><xf numFmtId="0" fontId="0" fillId="0" borderId="0"/></cellStyleXfs><cellXfs count="${reg.xfs.length}">${reg.xfs.map(xfXml).join("")}</cellXfs><cellStyles count="1"><cellStyle name="\u5E38\u89C4" xfId="0" builtinId="0"/></cellStyles></styleSheet>`;
132693
+ }
132694
+ function cellXml(ref, cell) {
132695
+ if (cell == null) return "";
132696
+ const { value, styleId } = cell;
132697
+ const sAttr = styleId ? ` s="${styleId}"` : "";
132698
+ if (value == null || value === "") {
132699
+ return styleId ? `<c r="${ref}"${sAttr}/>` : "";
132700
+ }
132701
+ if (typeof value === "number" && Number.isFinite(value)) {
132702
+ return `<c r="${ref}"${sAttr}><v>${value}</v></c>`;
132703
+ }
132704
+ return `<c r="${ref}"${sAttr} t="inlineStr"><is><t xml:space="preserve">${xmlEscape(String(value))}</t></is></c>`;
132705
+ }
132706
+ function sheetXml(sheet) {
132707
+ const colsXml = sheet.colWidths.length ? `<cols>${sheet.colWidths.map((w, i) => `<col min="${i + 1}" max="${i + 1}" width="${w}" customWidth="1"/>`).join("")}</cols>` : "";
132708
+ const rowsXml = sheet.rows.map((row, i) => {
132709
+ const r = i + 1;
132710
+ const htAttr = row.height ? ` ht="${row.height}" customHeight="1"` : "";
132711
+ const cells = row.cells.map((cell, cIdx) => cellXml(`${colLetter(cIdx)}${r}`, cell)).join("");
132712
+ return `<row r="${r}"${htAttr}>${cells}</row>`;
132713
+ }).join("");
132714
+ const mergesXml = sheet.merges.length ? `<mergeCells count="${sheet.merges.length}">${sheet.merges.map((m) => `<mergeCell ref="${m}"/>`).join("")}</mergeCells>` : "";
132715
+ const linkEntries = Object.entries(sheet.hyperlinks);
132716
+ const hyperlinksXml = linkEntries.length ? `<hyperlinks>${linkEntries.map(([ref, i]) => `<hyperlink ref="${ref}" r:id="rIdHlink${i}"/>`).join("")}</hyperlinks>` : "";
132717
+ return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?><worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">${colsXml}<sheetData>${rowsXml}</sheetData>${mergesXml}${hyperlinksXml}</worksheet>`;
132718
+ }
132719
+ function sheetRelsXml(sheet) {
132720
+ const entries = Object.entries(sheet.hyperlinks);
132721
+ if (!entries.length) return null;
132722
+ const rels = entries.map(
132723
+ ([, i]) => `<Relationship Id="rIdHlink${i}" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink" Target="${xmlEscape(sheet.hyperlinkTargets[i])}" TargetMode="External"/>`
132724
+ ).join("");
132725
+ return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?><Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">${rels}</Relationships>`;
132726
+ }
132727
+ function estimateWrapHeight(text, colWidthChars) {
132728
+ const str2 = String(text ?? "");
132729
+ if (!str2) return void 0;
132730
+ const lines = str2.split("\n").reduce((sum, line) => {
132731
+ const width = Math.max(colWidthChars || 20, 8);
132732
+ return sum + Math.max(1, Math.ceil(line.length / (width * 1.8)));
132733
+ }, 0);
132734
+ if (lines <= 1) return void 0;
132735
+ return Math.min(400, Math.max(20, lines * 15));
132736
+ }
132737
+ var SheetBuilder = class {
132738
+ constructor(name2, registry, accentHex) {
132739
+ this.registry = registry;
132740
+ this.accentHex = accentHex;
132741
+ this.name = name2.replace(/[\\/?*[\]:]/g, " ").slice(0, 31) || "Sheet";
132742
+ }
132743
+ name;
132744
+ rows = [];
132745
+ merges = [];
132746
+ colWidths = [];
132747
+ hyperlinks = {};
132748
+ hyperlinkTargets = {};
132749
+ hlSeq = 0;
132750
+ zebra = 0;
132751
+ get accentText() {
132752
+ return textColorFor(this.accentHex);
132753
+ }
132754
+ setColWidths(widths) {
132755
+ this.colWidths = widths;
132756
+ return this;
132757
+ }
132758
+ get colCount() {
132759
+ return this.colWidths.length || 1;
132760
+ }
132761
+ pushRow(cells, opts = {}) {
132762
+ this.rows.push({ cells, height: opts.height });
132763
+ return this.rows.length;
132764
+ }
132765
+ mergeCurrentRow(span) {
132766
+ const r = this.rows.length;
132767
+ if (span > 1) this.merges.push(`A${r}:${colLetter(span - 1)}${r}`);
132768
+ }
132769
+ blankRow(n = 1) {
132770
+ for (let i = 0; i < n; i++) this.pushRow([]);
132771
+ return this;
132772
+ }
132773
+ titleBar(text, span) {
132774
+ const s = this.registry.resolve({
132775
+ fontSize: 16,
132776
+ fontColor: textColorFor(NEUTRAL.titleFill),
132777
+ bold: true,
132778
+ fill: NEUTRAL.titleFill,
132779
+ border: false
132780
+ });
132781
+ this.pushRow([{ value: text, styleId: s }], { height: 28 });
132782
+ this.mergeCurrentRow(span || this.colCount);
132783
+ return this;
132784
+ }
132785
+ subtitleBar(text, span) {
132786
+ const s = this.registry.resolve({
132787
+ fontSize: 10.5,
132788
+ fontColor: NEUTRAL.subtitleText,
132789
+ fill: NEUTRAL.subtitleFill,
132790
+ border: false
132791
+ });
132792
+ this.pushRow([{ value: text, styleId: s }], { height: 20 });
132793
+ this.mergeCurrentRow(span || this.colCount);
132794
+ return this;
132795
+ }
132796
+ sectionBar(text, span) {
132797
+ const s = this.registry.resolve({
132798
+ fontSize: 11,
132799
+ fontColor: this.accentText,
132800
+ bold: true,
132801
+ fill: this.accentHex,
132802
+ border: false,
132803
+ hAlign: "center"
132804
+ });
132805
+ this.pushRow([{ value: text, styleId: s }], { height: 22 });
132806
+ this.mergeCurrentRow(span || this.colCount);
132807
+ return this;
132808
+ }
132809
+ noteRow(text, span) {
132810
+ const s = this.registry.resolve({
132811
+ fontSize: 9,
132812
+ fontColor: NEUTRAL.textMuted,
132813
+ fill: NEUTRAL.subtitleFill,
132814
+ border: true,
132815
+ vAlign: "top"
132816
+ });
132817
+ const height = estimateWrapHeight(text, this.colWidths.reduce((a, b) => a + b, 0) || 60);
132818
+ this.pushRow([{ value: text, styleId: s }], { height });
132819
+ this.mergeCurrentRow(span || this.colCount);
132820
+ return this;
132821
+ }
132822
+ tableHeader(headers) {
132823
+ this.zebra = 0;
132824
+ const s = this.registry.resolve({
132825
+ fontSize: 10,
132826
+ fontColor: this.accentText,
132827
+ bold: true,
132828
+ fill: this.accentHex,
132829
+ border: true,
132830
+ hAlign: "center"
132831
+ });
132832
+ this.pushRow(
132833
+ headers.map((h) => ({ value: h, styleId: s })),
132834
+ { height: 20 }
132835
+ );
132836
+ return this;
132837
+ }
132838
+ dataRow(cells, opts = {}) {
132839
+ const { textColumns = [], centerColumns = [] } = opts;
132840
+ const shaded = this.zebra % 2 === 1;
132841
+ this.zebra += 1;
132842
+ const fill = shaded ? NEUTRAL.zebraFill : void 0;
132843
+ let maxHeight;
132844
+ const rowCells = cells.map((value, idx) => {
132845
+ const forceText = textColumns.includes(idx);
132846
+ const hAlign = centerColumns.includes(idx) ? "center" : "left";
132847
+ const s = this.registry.resolve({
132848
+ fontSize: 10,
132849
+ fontColor: NEUTRAL.textDark,
132850
+ fill,
132851
+ border: true,
132852
+ hAlign,
132853
+ forceText
132854
+ });
132855
+ const h = estimateWrapHeight(value, this.colWidths[idx]);
132856
+ if (h && (!maxHeight || h > maxHeight)) maxHeight = h;
132857
+ return { value: forceText && value != null ? String(value) : value, styleId: s };
132858
+ });
132859
+ this.pushRow(rowCells, { height: maxHeight });
132860
+ return this;
132861
+ }
132862
+ kvRow(label, value, opts = {}) {
132863
+ const isShaded = opts.shaded ?? this.zebra % 2 === 1;
132864
+ this.zebra += 1;
132865
+ const labelStyle = this.registry.resolve({
132866
+ fontSize: 10,
132867
+ fontColor: NEUTRAL.textDark,
132868
+ bold: true,
132869
+ fill: isShaded ? NEUTRAL.zebraFill : NEUTRAL.subtitleFill,
132870
+ border: true,
132871
+ vAlign: "top"
132872
+ });
132873
+ const valueStyle = this.registry.resolve({
132874
+ fontSize: 10,
132875
+ fontColor: NEUTRAL.textDark,
132876
+ fill: isShaded ? NEUTRAL.zebraFill : void 0,
132877
+ border: true,
132878
+ vAlign: "top"
132879
+ });
132880
+ const colSpan = this.colCount - 1;
132881
+ const height = estimateWrapHeight(value, this.colWidths[1] || 40);
132882
+ const rowIndex = this.pushRow(
132883
+ [
132884
+ { value: label, styleId: labelStyle },
132885
+ { value: value ?? "", styleId: valueStyle }
132886
+ ],
132887
+ { height }
132888
+ );
132889
+ if (colSpan > 1) this.merges.push(`B${rowIndex}:${colLetter(this.colCount - 1)}${rowIndex}`);
132890
+ if (opts.hyperlink) {
132891
+ const id = ++this.hlSeq;
132892
+ this.hyperlinks[`B${rowIndex}`] = id;
132893
+ this.hyperlinkTargets[id] = opts.hyperlink;
132894
+ }
132895
+ return this;
132896
+ }
132897
+ };
132898
+ var ExcelWorkbookBuilder = class {
132899
+ registry = new StyleRegistry();
132900
+ accentHex;
132901
+ sheets = [];
132902
+ constructor(opts = {}) {
132903
+ this.accentHex = resolveAccent(opts.accent);
132904
+ }
132905
+ addSheet(name2) {
132906
+ const sheet = new SheetBuilder(name2, this.registry, this.accentHex);
132907
+ this.sheets.push(sheet);
132908
+ return sheet;
132909
+ }
132910
+ toBuffer() {
132911
+ if (this.sheets.length === 0) throw new Error("Excel \u5DE5\u4F5C\u7C3F\u81F3\u5C11\u9700\u8981 1 \u4E2A Sheet");
132912
+ const files = [];
132913
+ const sheetOverrides = this.sheets.map(
132914
+ (_, i) => `<Override PartName="/xl/worksheets/sheet${i + 1}.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"/>`
132915
+ ).join("");
132916
+ files.push({
132917
+ name: "[Content_Types].xml",
132918
+ data: Buffer.from(
132919
+ `<?xml version="1.0" encoding="UTF-8" standalone="yes"?><Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types"><Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/><Default Extension="xml" ContentType="application/xml"/><Override PartName="/xl/workbook.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml"/><Override PartName="/xl/styles.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml"/>${sheetOverrides}</Types>`,
132920
+ "utf8"
132921
+ )
132922
+ });
132923
+ files.push({
132924
+ name: "_rels/.rels",
132925
+ data: Buffer.from(
132926
+ `<?xml version="1.0" encoding="UTF-8" standalone="yes"?><Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"><Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="xl/workbook.xml"/></Relationships>`,
132927
+ "utf8"
132928
+ )
132929
+ });
132930
+ const sheetTags = this.sheets.map((s, i) => `<sheet name="${xmlEscape(s.name)}" sheetId="${i + 1}" r:id="rId${i + 1}"/>`).join("");
132931
+ files.push({
132932
+ name: "xl/workbook.xml",
132933
+ data: Buffer.from(
132934
+ `<?xml version="1.0" encoding="UTF-8" standalone="yes"?><workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships"><sheets>${sheetTags}</sheets></workbook>`,
132935
+ "utf8"
132936
+ )
132937
+ });
132938
+ const stylesRid = this.sheets.length + 1;
132939
+ const wbRels = this.sheets.map(
132940
+ (_, i) => `<Relationship Id="rId${i + 1}" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet" Target="worksheets/sheet${i + 1}.xml"/>`
132941
+ ).join("");
132942
+ files.push({
132943
+ name: "xl/_rels/workbook.xml.rels",
132944
+ data: Buffer.from(
132945
+ `<?xml version="1.0" encoding="UTF-8" standalone="yes"?><Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">${wbRels}<Relationship Id="rId${stylesRid}" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles" Target="styles.xml"/></Relationships>`,
132946
+ "utf8"
132947
+ )
132948
+ });
132949
+ files.push({ name: "xl/styles.xml", data: Buffer.from(stylesXml(this.registry), "utf8") });
132950
+ this.sheets.forEach((sheet, i) => {
132951
+ files.push({
132952
+ name: `xl/worksheets/sheet${i + 1}.xml`,
132953
+ data: Buffer.from(sheetXml(sheet), "utf8")
132954
+ });
132955
+ const relsXml = sheetRelsXml(sheet);
132956
+ if (relsXml) {
132957
+ files.push({
132958
+ name: `xl/worksheets/_rels/sheet${i + 1}.xml.rels`,
132959
+ data: Buffer.from(relsXml, "utf8")
132960
+ });
132961
+ }
132962
+ });
132963
+ return zipStore(files);
132964
+ }
132965
+ writeFile(filePath) {
132966
+ const abs = path33.resolve(filePath);
132967
+ fs26.mkdirSync(path33.dirname(abs), { recursive: true });
132968
+ fs26.writeFileSync(abs, this.toBuffer());
132969
+ return abs;
132970
+ }
132971
+ };
132972
+ function createExcelWorkbook(opts = {}) {
132973
+ return new ExcelWorkbookBuilder(opts);
132974
+ }
132975
+
132976
+ // src/commands/facebook-analysis/write-xlsx.ts
132977
+ var MIN_COL_WIDTH = 10;
132978
+ var MAX_COL_WIDTH = 60;
132979
+ function estimateColWidths(rows) {
132980
+ const colCount = rows.reduce((max, row) => Math.max(max, row.length), 0);
132981
+ const widths = new Array(colCount).fill(MIN_COL_WIDTH);
132982
+ for (const row of rows) {
132983
+ row.forEach((cell, idx) => {
132984
+ if (cell == null) return;
132985
+ const longestLine = Math.max(
132986
+ ...String(cell).split("\n").map((l) => l.length)
132987
+ );
132988
+ const width = Math.min(MAX_COL_WIDTH, Math.max(MIN_COL_WIDTH, Math.ceil(longestLine * 1.3)));
132989
+ if (width > widths[idx]) widths[idx] = width;
132990
+ });
132991
+ }
132992
+ return widths;
132993
+ }
132994
+ function writeSimpleXlsx(filePath, sheets, opts = {}) {
132605
132995
  if (sheets.length === 0) {
132606
132996
  throw new Error("xlsx \u81F3\u5C11\u9700\u8981 1 \u4E2A Sheet");
132607
132997
  }
132608
- const names = sheets.map((s, i) => {
132609
- const raw = (s.name || `Sheet${i + 1}`).replace(/[\\/?*[\]]/g, " ").slice(0, 31);
132610
- return raw || `Sheet${i + 1}`;
132611
- });
132612
- const files = [
132613
- { name: "[Content_Types].xml", data: Buffer.from(contentTypesXml(sheets.length), "utf8") },
132614
- { name: "_rels/.rels", data: Buffer.from(ROOT_RELS, "utf8") },
132615
- { name: "xl/workbook.xml", data: Buffer.from(workbookXml(names), "utf8") },
132616
- { name: "xl/_rels/workbook.xml.rels", data: Buffer.from(workbookRelsXml(sheets.length), "utf8") }
132617
- ];
132618
- sheets.forEach((sheet, i) => {
132619
- files.push({
132620
- name: `xl/worksheets/sheet${i + 1}.xml`,
132621
- data: Buffer.from(sheetXml(sheet.rows), "utf8")
132998
+ const wb = createExcelWorkbook({ accent: opts.accent ?? "facebook" });
132999
+ for (const rawSheet of sheets) {
133000
+ const sheet = wb.addSheet(rawSheet.name);
133001
+ sheet.setColWidths(estimateColWidths(rawSheet.rows));
133002
+ rawSheet.rows.forEach((row, rowIdx) => {
133003
+ if (row.length === 0) {
133004
+ sheet.blankRow();
133005
+ return;
133006
+ }
133007
+ if (rowIdx === 0 && row.length > 1) {
133008
+ sheet.tableHeader(row);
133009
+ return;
133010
+ }
133011
+ if (row.length === 1) {
133012
+ sheet.noteRow(String(row[0] ?? ""));
133013
+ return;
133014
+ }
133015
+ sheet.dataRow(row);
132622
133016
  });
132623
- });
132624
- const abs = path33.resolve(filePath);
132625
- fs26.mkdirSync(path33.dirname(abs), { recursive: true });
132626
- fs26.writeFileSync(abs, zipStore(files));
133017
+ }
133018
+ wb.writeFile(filePath);
132627
133019
  }
132628
133020
 
132629
133021
  // src/commands/facebook-analysis/render-excel.ts
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "slug": "siluzan-tso",
3
- "version": "1.1.48-beta.4",
4
- "publishedAt": 1787636305972
3
+ "version": "1.1.48-beta.5",
4
+ "publishedAt": 1787710698259
5
5
  }
@@ -125,6 +125,8 @@ Description:`{{shots[0].description}}`
125
125
 
126
126
  对照客户方案 xlsx(`方案总览` / `1-客户画像` / `2-账户结构` / `3-表单询盘C1`)。Agent 脚本写 `.xlsx`,**无** CLI excel 子命令。ID 列用文本。
127
127
 
128
+ **样式(必须)**:脚本 `import` `report-templates/excel-style-kit.mjs`,用 `createExcelWorkbook({ accent: "facebook" })` + `titleBar`/`subtitleBar`/`sectionBar`/`tableHeader`/`dataRow`/`kvRow` 组件搭版面,**禁止**裸写无样式单元格。规范与色板见 `report-templates/excel-style-guide.md`。
129
+
128
130
  ### Sheet `方案总览`
129
131
 
130
132
  | 项目 | 内容 |
@@ -6,8 +6,10 @@
6
6
  | ----------------------- | ----------------------------------------------- |
7
7
  | `*.md` | 报告内容纲要:默认维度、可选维度、对应 CLI 命令 |
8
8
  | `report-template*.html` | 视觉样式参考:排版/色彩/图表方案,不定义内容 |
9
+ | `excel-style-guide.md` | Excel 通用样式规范(色板/字体/组件),所有平台共用 |
10
+ | `excel-style-kit.mjs` | 配套代码:Agent 写 xlsx 脚本直接 `import` 使用,零依赖 |
9
11
 
10
- > 冲突时以 `*.md` 的内容要求为准,样式文件仅影响排版。
12
+ > 冲突时以 `*.md` 的内容要求为准,样式文件仅影响排版。任何「用户要 Excel/xlsx/表格」的模板,写脚本时都按 `excel-style-guide.md` 套样式,不再裸写无样式单元格。
11
13
 
12
14
  **图表库(全部 HTML 模板统一)**:ECharts 5 — `https://staticpn.siluzan.com/assets/slz/homeCDN/echarts.min.js`;初始化用 `echarts.init(el).setOption(...)`。勿使用 Chart.js。
13
15