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 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: 600px;
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: 1080, height: 720, deviceScaleFactor: 1 });
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 300;
1697
- return 20 + index / (labels.length - 1) * 560;
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="${20}" y1="${y}" x2="580" y2="${y}" stroke="var(--border-color)" stroke-width="1"/>`;
1705
- svgElements += `<text x="${20 - 8}" y="${y + 4}" font-size="10" fill="var(--sub-text-color)" text-anchor="end">${value}</text>`;
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 LEGEND_START_Y = 295;
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 = 20 + colIndex * columnWidth;
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="320" xmlns="http://www.w3.org/2000/svg">
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 (${config.minFontSize} + ${config.maxFontSize}) / 2;
1790
+ if (${maxWeight} === ${minWeight}) return (${minFontSize} + ${maxFontSize}) / 2;
1783
1791
  const normalizedWeight = (size - ${minWeight}) / (${maxWeight} - ${minWeight});
1784
- return ${config.minFontSize} + normalizedWeight * (${config.maxFontSize} - ${config.minFontSize});
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(0).description("最小旋转角"),
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", "Arial", sans-serif').description("词云字体"),
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
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "koishi-plugin-chat-analyse",
3
3
  "description": "强大而全面的聊天数据分析插件。支持多维度统计(命令、发言、消息类型、活跃度),可生成发言排行、词云图,并提供完善的数据管理。",
4
- "version": "1.4.9",
4
+ "version": "1.4.11",
5
5
  "contributors": [
6
6
  "Yis_Rime <yis_rime@outlook.com>"
7
7
  ],
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
- | `rotateRatio` | **旋转比**:随机旋转的单词所占的比例(0-1)。 | `0.5` |
172
- | `minRotation` | **最小旋转角**:单词随机旋转的最小角度(弧度)。 | `0` |
170
+ | `minRotation` | **最小旋转角**:单词随机旋转的最小角度(弧度)。 | `-1.570796` (-π/2) |
173
171
  | `maxRotation` | **最大旋转角**:单词随机旋转的最大角度(弧度)。 | `1.570796` (π/2) |
174
172
  | `rotationSteps`| **旋转步数**:旋转角度的选择方式。0表示随机,2表示只在最小/最大角度中二选一。 | `3` |
175
173