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 +1 -1
- package/dist/index.js +463 -71
- package/dist/skill/_meta.json +2 -2
- package/dist/skill/references/meta-ads/meta-lead-launch-plan-template.md +2 -0
- package/dist/skill/references/report-templates/README.md +3 -1
- package/dist/skill/references/report-templates/excel-style-guide.md +93 -0
- package/dist/skill/references/report-templates/google-inquiry-analysis.md +1 -0
- package/dist/skill/references/report-templates/google-period-report-excel.md +1 -0
- package/dist/skill/references/report-templates/okki-weekly-google-client.md +1 -0
- package/dist/skill/references/report-templates/stats-daily-excel.md +2 -0
- package/dist/skill/references/report-templates/yandex-period-report-excel.md +6 -0
- package/dist/skill/report-templates/README.md +3 -1
- package/dist/skill/report-templates/excel-style-guide.md +93 -0
- package/dist/skill/report-templates/excel-style-kit.mjs +670 -0
- package/dist/skill/report-templates/google-inquiry-analysis.md +1 -0
- package/dist/skill/report-templates/google-period-report-excel.md +1 -0
- package/dist/skill/report-templates/okki-weekly-google-client.md +1 -0
- package/dist/skill/report-templates/stats-daily-excel.md +2 -0
- package/dist/skill/report-templates/yandex-period-report-excel.md +6 -0
- package/dist/skill/scripts/install.ps1 +1 -1
- package/dist/skill/scripts/install.sh +1 -1
- package/package.json +1 -1
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.
|
|
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/
|
|
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, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
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
|
-
|
|
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
|
|
132609
|
-
|
|
132610
|
-
|
|
132611
|
-
|
|
132612
|
-
|
|
132613
|
-
|
|
132614
|
-
|
|
132615
|
-
|
|
132616
|
-
|
|
132617
|
-
|
|
132618
|
-
|
|
132619
|
-
|
|
132620
|
-
|
|
132621
|
-
|
|
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
|
-
|
|
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
|
package/dist/skill/_meta.json
CHANGED
|
@@ -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
|
|