koishi-plugin-chat-analyse 1.4.9 → 1.4.11
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/lib/index.d.ts +0 -4
- package/lib/index.js +31 -27
- package/package.json +1 -1
- package/readme.md +1 -3
package/lib/index.d.ts
CHANGED
|
@@ -25,13 +25,9 @@ export interface Config {
|
|
|
25
25
|
color: string;
|
|
26
26
|
shape: string;
|
|
27
27
|
ellipticity: number;
|
|
28
|
-
rotateRatio: number;
|
|
29
28
|
minRotation: number;
|
|
30
29
|
maxRotation: number;
|
|
31
30
|
rotationSteps: number;
|
|
32
|
-
minFontSize: number;
|
|
33
|
-
maxFontSize: number;
|
|
34
|
-
gridSize: number;
|
|
35
31
|
fontFamily: string;
|
|
36
32
|
maskImage: string;
|
|
37
33
|
}
|
package/lib/index.js
CHANGED
|
@@ -1509,7 +1509,7 @@ var Renderer = class {
|
|
|
1509
1509
|
.container {
|
|
1510
1510
|
display: inline-block; background: var(--card-bg); border-radius: 12px;
|
|
1511
1511
|
padding: 0; overflow: hidden; box-shadow: 0 4px 6px rgba(0,0,0,.05);
|
|
1512
|
-
width:
|
|
1512
|
+
min-width: 480px; max-width: 640px;
|
|
1513
1513
|
}
|
|
1514
1514
|
.header {
|
|
1515
1515
|
padding: 12px 16px;
|
|
@@ -1520,7 +1520,7 @@ var Renderer = class {
|
|
|
1520
1520
|
}
|
|
1521
1521
|
.title-text {
|
|
1522
1522
|
font-size: 16px; font-weight: 600; color: var(--header-color);
|
|
1523
|
-
margin: 0; text-align: center;
|
|
1523
|
+
margin: 0 8px; text-align: center;
|
|
1524
1524
|
}
|
|
1525
1525
|
.stat-chip, .time-label {
|
|
1526
1526
|
display: inline-flex; align-items: baseline; padding: 4px 8px;
|
|
@@ -1562,7 +1562,7 @@ var Renderer = class {
|
|
|
1562
1562
|
async htmlToImage(fullHtmlContent) {
|
|
1563
1563
|
const page = await this.ctx.puppeteer.page();
|
|
1564
1564
|
try {
|
|
1565
|
-
await page.setViewport({ width:
|
|
1565
|
+
await page.setViewport({ width: 800, height: 600, deviceScaleFactor: 1 });
|
|
1566
1566
|
await page.setContent(fullHtmlContent, { waitUntil: "networkidle0" });
|
|
1567
1567
|
const { width, height } = await page.evaluate(() => ({
|
|
1568
1568
|
width: document.body.scrollWidth,
|
|
@@ -1693,56 +1693,61 @@ var Renderer = class {
|
|
|
1693
1693
|
const yTickValue = Math.ceil(maxVal / yTickCount);
|
|
1694
1694
|
const yAxisMax = yTickValue * yTickCount;
|
|
1695
1695
|
const getX = /* @__PURE__ */ __name((index) => {
|
|
1696
|
-
if (labels.length <= 1) return
|
|
1697
|
-
return
|
|
1696
|
+
if (labels.length <= 1) return 320;
|
|
1697
|
+
return 40 + index / (labels.length - 1) * 540;
|
|
1698
1698
|
}, "getX");
|
|
1699
1699
|
const getY = /* @__PURE__ */ __name((value) => 250 - value / yAxisMax * 240, "getY");
|
|
1700
1700
|
let svgElements = "";
|
|
1701
1701
|
for (let i = 0; i <= yTickCount; i++) {
|
|
1702
1702
|
const y = getY(i * yTickValue);
|
|
1703
1703
|
const value = i * yTickValue;
|
|
1704
|
-
svgElements += `<line x1="
|
|
1705
|
-
svgElements += `<text x="
|
|
1704
|
+
svgElements += `<line x1="40" y1="${y}" x2="580" y2="${y}" stroke="var(--border-color)" stroke-width="1"/>`;
|
|
1705
|
+
svgElements += `<text x="32" y="${y + 4}" font-size="10" fill="var(--sub-text-color)" text-anchor="end">${value}</text>`;
|
|
1706
1706
|
}
|
|
1707
1707
|
labels.forEach((label, index) => {
|
|
1708
|
-
if (index % Math.ceil(labels.length / 12) === 0) {
|
|
1708
|
+
if (labels.length > 1 && index % Math.ceil(labels.length / 12) === 0) {
|
|
1709
1709
|
const x = getX(index);
|
|
1710
1710
|
svgElements += `<text x="${x}" y="270" font-size="10" fill="var(--sub-text-color)" text-anchor="middle">${label}</text>`;
|
|
1711
1711
|
}
|
|
1712
1712
|
});
|
|
1713
1713
|
series.forEach((s, seriesIndex) => {
|
|
1714
|
-
const color = seriesColors[seriesIndex];
|
|
1714
|
+
const color = seriesColors[seriesIndex % seriesColors.length];
|
|
1715
1715
|
const points = s.data.map((value, index) => `${getX(index)},${getY(value)}`).join(" ");
|
|
1716
1716
|
svgElements += `<polyline points="${points}" fill="none" stroke="${color}" stroke-width="2"/>`;
|
|
1717
1717
|
});
|
|
1718
|
+
let legendHeight = 0;
|
|
1718
1719
|
if (series.length > 1) {
|
|
1719
|
-
const
|
|
1720
|
+
const legendRows = Math.ceil(series.length / 3);
|
|
1721
|
+
legendHeight = 15 + legendRows * 20;
|
|
1722
|
+
const LEGEND_START_Y = 300;
|
|
1720
1723
|
const columnWidth = 560 / 3;
|
|
1721
1724
|
series.forEach((s, seriesIndex) => {
|
|
1722
1725
|
const rowIndex = Math.floor(seriesIndex / 3);
|
|
1723
1726
|
const colIndex = seriesIndex % 3;
|
|
1724
|
-
const legendX =
|
|
1727
|
+
const legendX = 40 + colIndex * columnWidth;
|
|
1725
1728
|
const legendY = LEGEND_START_Y + rowIndex * 20;
|
|
1726
|
-
const color = seriesColors[seriesIndex];
|
|
1729
|
+
const color = seriesColors[seriesIndex % seriesColors.length];
|
|
1727
1730
|
svgElements += `<rect x="${legendX}" y="${legendY - 8}" width="12" height="8" fill="${color}" rx="2"/>`;
|
|
1728
1731
|
svgElements += `<text x="${legendX + 18}" y="${legendY}" font-size="12" fill="var(--text-color)">${s.name}</text>`;
|
|
1729
1732
|
});
|
|
1730
1733
|
}
|
|
1734
|
+
const totalLegendSpace = legendHeight > 0 ? legendHeight + 15 : 0;
|
|
1735
|
+
const svgHeight = 280 + totalLegendSpace;
|
|
1731
1736
|
const totalMessages = series.reduce((sum, s) => sum + s.data.reduce((a, b) => a + b, 0), 0);
|
|
1732
1737
|
const cardHtml = `
|
|
1733
|
-
<div class="container">
|
|
1738
|
+
<div class="container" style="width: 600px;">
|
|
1734
1739
|
<div class="header">
|
|
1735
1740
|
<div class="stat-chip">总计: <span>${totalMessages.toLocaleString()}</span></div>
|
|
1736
1741
|
<h1 class="title-text">${title}</h1>
|
|
1737
1742
|
<div class="time-label">${time.toLocaleString("zh-CN", { hour12: false })}</div>
|
|
1738
1743
|
</div>
|
|
1739
1744
|
<div class="chart-wrapper">
|
|
1740
|
-
<svg width="600" height="
|
|
1745
|
+
<svg width="600" height="${svgHeight}" xmlns="http://www.w3.org/2000/svg">
|
|
1741
1746
|
${svgElements}
|
|
1742
1747
|
</svg>
|
|
1743
1748
|
</div>
|
|
1744
1749
|
</div>`;
|
|
1745
|
-
const chartStyles = ` .chart-wrapper { padding: 10px; } `;
|
|
1750
|
+
const chartStyles = ` .chart-wrapper { padding: 10px; box-sizing: border-box; } `;
|
|
1746
1751
|
const fullHtml = this.generateFullHtml(cardHtml, chartStyles);
|
|
1747
1752
|
const imageBuffer = await this.htmlToImage(fullHtml);
|
|
1748
1753
|
if (imageBuffer) yield imageBuffer;
|
|
@@ -1762,14 +1767,17 @@ var Renderer = class {
|
|
|
1762
1767
|
const weights = words.map((w) => w[1]);
|
|
1763
1768
|
const maxWeight = Math.max(...weights, 1);
|
|
1764
1769
|
const minWeight = Math.min(...weights);
|
|
1770
|
+
const wordCount = words.length;
|
|
1771
|
+
const maxFontSize = Math.max(20, Math.round(512 / Math.log1p(wordCount)));
|
|
1772
|
+
const minFontSize = Math.max(6, Math.round(maxFontSize / 10));
|
|
1765
1773
|
const cardHtml = `
|
|
1766
|
-
<div class="container">
|
|
1774
|
+
<div class="container" style="width: 600px;">
|
|
1767
1775
|
<div class="header">
|
|
1768
1776
|
<div class="stat-chip">词数: <span>${words.length}</span></div>
|
|
1769
1777
|
<h1 class="title-text">${title}</h1>
|
|
1770
1778
|
<div class="time-label">${time.toLocaleString("zh-CN", { hour12: false })}</div>
|
|
1771
1779
|
</div>
|
|
1772
|
-
<div style="width: 600px; height: 600px; margin: auto;">
|
|
1780
|
+
<div style="width: 600px; height: 600px; margin: auto; padding: 10px 0;">
|
|
1773
1781
|
<canvas id="wordcloud-container" width="600" height="600"></canvas>
|
|
1774
1782
|
</div>
|
|
1775
1783
|
<script>${wordCloudScript}</script>
|
|
@@ -1779,22 +1787,22 @@ var Renderer = class {
|
|
|
1779
1787
|
list: ${wordsJson},
|
|
1780
1788
|
fontFamily: ${JSON.stringify(config.fontFamily)},
|
|
1781
1789
|
weightFactor: (size) => {
|
|
1782
|
-
if (${maxWeight} === ${minWeight}) return (${
|
|
1790
|
+
if (${maxWeight} === ${minWeight}) return (${minFontSize} + ${maxFontSize}) / 2;
|
|
1783
1791
|
const normalizedWeight = (size - ${minWeight}) / (${maxWeight} - ${minWeight});
|
|
1784
|
-
return ${
|
|
1792
|
+
return ${minFontSize} + normalizedWeight * (${maxFontSize} - ${minFontSize});
|
|
1785
1793
|
},
|
|
1786
1794
|
color: ${JSON.stringify(config.color)},
|
|
1787
1795
|
shape: ${JSON.stringify(config.shape)},
|
|
1788
|
-
gridSize: ${config.gridSize},
|
|
1789
1796
|
ellipticity: ${config.ellipticity},
|
|
1790
|
-
rotateRatio: ${config.rotateRatio},
|
|
1791
1797
|
minRotation: ${config.minRotation},
|
|
1792
1798
|
maxRotation: ${config.maxRotation},
|
|
1793
1799
|
rotationSteps: ${config.rotationSteps},
|
|
1794
1800
|
backgroundColor: 'transparent',
|
|
1795
1801
|
clearCanvas: true,
|
|
1796
1802
|
shrinkToFit: true,
|
|
1803
|
+
rotateRatio: 1,
|
|
1797
1804
|
shuffle: true,
|
|
1805
|
+
gridSize: 1,
|
|
1798
1806
|
};
|
|
1799
1807
|
|
|
1800
1808
|
const maskImageUrl = ${JSON.stringify(config.maskImage)};
|
|
@@ -2521,16 +2529,12 @@ var Config3 = import_koishi7.Schema.intersect([
|
|
|
2521
2529
|
}).description("高级分析配置"),
|
|
2522
2530
|
import_koishi7.Schema.object({
|
|
2523
2531
|
ellipticity: import_koishi7.Schema.number().min(0).max(1).default(1).description("长宽比"),
|
|
2524
|
-
rotateRatio: import_koishi7.Schema.number().min(0).max(1).default(0.5).description("旋转比"),
|
|
2525
2532
|
rotationSteps: import_koishi7.Schema.number().min(0).default(3).description("旋转步数"),
|
|
2526
|
-
minRotation: import_koishi7.Schema.number().default(
|
|
2533
|
+
minRotation: import_koishi7.Schema.number().default(-Math.PI / 2).description("最小旋转角"),
|
|
2527
2534
|
maxRotation: import_koishi7.Schema.number().default(Math.PI / 2).description("最大旋转角"),
|
|
2528
|
-
minFontSize: import_koishi7.Schema.number().min(1).default(4).description("最小字号"),
|
|
2529
|
-
maxFontSize: import_koishi7.Schema.number().min(1).default(64).description("最大字号"),
|
|
2530
|
-
gridSize: import_koishi7.Schema.number().min(0).default(1).description("词语间距"),
|
|
2531
2535
|
color: import_koishi7.Schema.string().default("random-light").description("词云颜色"),
|
|
2532
2536
|
shape: import_koishi7.Schema.string().default("square").description("词云形状"),
|
|
2533
|
-
fontFamily: import_koishi7.Schema.string().default('"Noto Sans CJK SC",
|
|
2537
|
+
fontFamily: import_koishi7.Schema.string().default('"Noto Sans CJK SC", Arial, sans-serif').description("词云字体"),
|
|
2534
2538
|
maskImage: import_koishi7.Schema.string().role("link").description("蒙版图片")
|
|
2535
2539
|
}).description("词云生成配置")
|
|
2536
2540
|
]);
|
package/package.json
CHANGED
package/readme.md
CHANGED
|
@@ -166,10 +166,8 @@
|
|
|
166
166
|
| `fontFamily` | **词云字体**:用于渲染词云的字体列表。 | `"Noto Sans CJK SC", "Arial", sans-serif` |
|
|
167
167
|
| `minFontSize` | **最小字号**:权重最小的单词所使用的字号(px)。 | `4` |
|
|
168
168
|
| `maxFontSize` | **最大字号**:权重最大的单词所使用的字号(px)。 | `64` |
|
|
169
|
-
| `gridSize` | **词语间距**:用于分隔单词的网格大小(px)。值越大,单词间距越大。 | `1` |
|
|
170
169
|
| `ellipticity` | **长宽比**:形状的扁平程度(0-1),仅对非方形形状有效。 | `1` |
|
|
171
|
-
| `
|
|
172
|
-
| `minRotation` | **最小旋转角**:单词随机旋转的最小角度(弧度)。 | `0` |
|
|
170
|
+
| `minRotation` | **最小旋转角**:单词随机旋转的最小角度(弧度)。 | `-1.570796` (-π/2) |
|
|
173
171
|
| `maxRotation` | **最大旋转角**:单词随机旋转的最大角度(弧度)。 | `1.570796` (π/2) |
|
|
174
172
|
| `rotationSteps`| **旋转步数**:旋转角度的选择方式。0表示随机,2表示只在最小/最大角度中二选一。 | `3` |
|
|
175
173
|
|