koishi-plugin-chat-analyse 1.4.9 → 1.4.10
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 +30 -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,60 @@ 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 = 285;
|
|
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 svgHeight = 280 + legendHeight;
|
|
1731
1735
|
const totalMessages = series.reduce((sum, s) => sum + s.data.reduce((a, b) => a + b, 0), 0);
|
|
1732
1736
|
const cardHtml = `
|
|
1733
|
-
<div class="container">
|
|
1737
|
+
<div class="container" style="width: 600px;">
|
|
1734
1738
|
<div class="header">
|
|
1735
1739
|
<div class="stat-chip">总计: <span>${totalMessages.toLocaleString()}</span></div>
|
|
1736
1740
|
<h1 class="title-text">${title}</h1>
|
|
1737
1741
|
<div class="time-label">${time.toLocaleString("zh-CN", { hour12: false })}</div>
|
|
1738
1742
|
</div>
|
|
1739
1743
|
<div class="chart-wrapper">
|
|
1740
|
-
<svg width="600" height="
|
|
1744
|
+
<svg width="600" height="${svgHeight}" xmlns="http://www.w3.org/2000/svg">
|
|
1741
1745
|
${svgElements}
|
|
1742
1746
|
</svg>
|
|
1743
1747
|
</div>
|
|
1744
1748
|
</div>`;
|
|
1745
|
-
const chartStyles = ` .chart-wrapper { padding: 10px; } `;
|
|
1749
|
+
const chartStyles = ` .chart-wrapper { padding: 10px; box-sizing: border-box; } `;
|
|
1746
1750
|
const fullHtml = this.generateFullHtml(cardHtml, chartStyles);
|
|
1747
1751
|
const imageBuffer = await this.htmlToImage(fullHtml);
|
|
1748
1752
|
if (imageBuffer) yield imageBuffer;
|
|
@@ -1762,14 +1766,17 @@ var Renderer = class {
|
|
|
1762
1766
|
const weights = words.map((w) => w[1]);
|
|
1763
1767
|
const maxWeight = Math.max(...weights, 1);
|
|
1764
1768
|
const minWeight = Math.min(...weights);
|
|
1769
|
+
const wordCount = words.length;
|
|
1770
|
+
const maxFontSize = Math.max(20, Math.round(400 / Math.log1p(wordCount)));
|
|
1771
|
+
const minFontSize = Math.max(4, Math.round(maxFontSize / 12));
|
|
1765
1772
|
const cardHtml = `
|
|
1766
|
-
<div class="container">
|
|
1773
|
+
<div class="container" style="width: 600px;">
|
|
1767
1774
|
<div class="header">
|
|
1768
1775
|
<div class="stat-chip">词数: <span>${words.length}</span></div>
|
|
1769
1776
|
<h1 class="title-text">${title}</h1>
|
|
1770
1777
|
<div class="time-label">${time.toLocaleString("zh-CN", { hour12: false })}</div>
|
|
1771
1778
|
</div>
|
|
1772
|
-
<div style="width: 600px; height: 600px; margin: auto;">
|
|
1779
|
+
<div style="width: 600px; height: 600px; margin: auto; padding: 10px 0;">
|
|
1773
1780
|
<canvas id="wordcloud-container" width="600" height="600"></canvas>
|
|
1774
1781
|
</div>
|
|
1775
1782
|
<script>${wordCloudScript}</script>
|
|
@@ -1779,22 +1786,22 @@ var Renderer = class {
|
|
|
1779
1786
|
list: ${wordsJson},
|
|
1780
1787
|
fontFamily: ${JSON.stringify(config.fontFamily)},
|
|
1781
1788
|
weightFactor: (size) => {
|
|
1782
|
-
if (${maxWeight} === ${minWeight}) return (${
|
|
1789
|
+
if (${maxWeight} === ${minWeight}) return (${minFontSize} + ${maxFontSize}) / 2;
|
|
1783
1790
|
const normalizedWeight = (size - ${minWeight}) / (${maxWeight} - ${minWeight});
|
|
1784
|
-
return ${
|
|
1791
|
+
return ${minFontSize} + normalizedWeight * (${maxFontSize} - ${minFontSize});
|
|
1785
1792
|
},
|
|
1786
1793
|
color: ${JSON.stringify(config.color)},
|
|
1787
1794
|
shape: ${JSON.stringify(config.shape)},
|
|
1788
|
-
gridSize: ${config.gridSize},
|
|
1789
1795
|
ellipticity: ${config.ellipticity},
|
|
1790
|
-
rotateRatio: ${config.rotateRatio},
|
|
1791
1796
|
minRotation: ${config.minRotation},
|
|
1792
1797
|
maxRotation: ${config.maxRotation},
|
|
1793
1798
|
rotationSteps: ${config.rotationSteps},
|
|
1794
1799
|
backgroundColor: 'transparent',
|
|
1795
1800
|
clearCanvas: true,
|
|
1796
1801
|
shrinkToFit: true,
|
|
1802
|
+
rotateRatio: 1,
|
|
1797
1803
|
shuffle: true,
|
|
1804
|
+
gridSize: 1,
|
|
1798
1805
|
};
|
|
1799
1806
|
|
|
1800
1807
|
const maskImageUrl = ${JSON.stringify(config.maskImage)};
|
|
@@ -2521,16 +2528,12 @@ var Config3 = import_koishi7.Schema.intersect([
|
|
|
2521
2528
|
}).description("高级分析配置"),
|
|
2522
2529
|
import_koishi7.Schema.object({
|
|
2523
2530
|
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
2531
|
rotationSteps: import_koishi7.Schema.number().min(0).default(3).description("旋转步数"),
|
|
2526
|
-
minRotation: import_koishi7.Schema.number().default(
|
|
2532
|
+
minRotation: import_koishi7.Schema.number().default(-Math.PI / 2).description("最小旋转角"),
|
|
2527
2533
|
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
2534
|
color: import_koishi7.Schema.string().default("random-light").description("词云颜色"),
|
|
2532
2535
|
shape: import_koishi7.Schema.string().default("square").description("词云形状"),
|
|
2533
|
-
fontFamily: import_koishi7.Schema.string().default('"Noto Sans CJK SC",
|
|
2536
|
+
fontFamily: import_koishi7.Schema.string().default('"Noto Sans CJK SC", Arial, sans-serif').description("词云字体"),
|
|
2534
2537
|
maskImage: import_koishi7.Schema.string().role("link").description("蒙版图片")
|
|
2535
2538
|
}).description("词云生成配置")
|
|
2536
2539
|
]);
|
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
|
|