koishi-plugin-ll-mc 3.1.4 → 3.1.6

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.
Files changed (2) hide show
  1. package/lib/index.js +27 -11
  2. package/package.json +1 -1
package/lib/index.js CHANGED
@@ -357,6 +357,7 @@ async function apply(ctx, config) {
357
357
  const fontsPath = import_path.default.join(messageCounterRoot, "fonts");
358
358
  const avatarsPath = import_path.default.join(messageCounterRoot, "avatars");
359
359
  const emptyHtmlPath = import_path.default.join(messageCounterRoot, "emptyHtml.html").replace(/\\/g, "/");
360
+ const templateHtmlPath = import_path.default.join(messageCounterRoot, "leaderboard-template.html").replace(/\\/g, "/");
360
361
  const oldIconsPath = import_path.default.join(dataRoot, "messageCounterIcons");
361
362
  const oldBarBgImgsPath = import_path.default.join(dataRoot, "messageCounterBarBgImgs");
362
363
  await fs.mkdir(fontsPath, { recursive: true });
@@ -2519,7 +2520,6 @@ ${targetUserRecord[0].username}
2519
2520
  }[ch]));
2520
2521
  }
2521
2522
  __name(escapeHtml, "escapeHtml");
2522
- const BAR_LIGHT_TRACK = "#F3F4F6";
2523
2523
  const BAR_FILL_COLORS = [
2524
2524
  "#F472B6", // pink
2525
2525
  "#A78BFA", // violet
@@ -2540,8 +2540,7 @@ ${targetUserRecord[0].username}
2540
2540
  const avatar = escapeHtml(item.avatar || "");
2541
2541
  const name = escapeHtml(item.name || "");
2542
2542
  const count = item.count;
2543
- // 柱子总背景固定为浅色,发言部分单独用颜色区分,这样更显眼
2544
- const barBg = BAR_LIGHT_TRACK;
2543
+ // 柱子未发言部分用半透明玻璃(毛玻璃),发言部分用彩色区分
2545
2544
  const fill = isHero
2546
2545
  ? 'linear-gradient(to right, #6366F1, #A855F7, #EC4899)'
2547
2546
  : fillColor;
@@ -2551,7 +2550,7 @@ ${targetUserRecord[0].username}
2551
2550
  : "";
2552
2551
  return `<div class="row">
2553
2552
  <div class="avatar"><img src="${avatar}" alt="avatar"></div>
2554
- <div class="bar" style="background:${barBg};">
2553
+ <div class="bar">
2555
2554
  <div class="fill" style="width:${width}%;background:${fill};"></div>
2556
2555
  <div class="bar-content" style="color:${textColor};">
2557
2556
  <span class="row-name${isHero ? " is-hero" : ""}">${star}${name}</span>
@@ -2561,7 +2560,7 @@ ${targetUserRecord[0].username}
2561
2560
  </div>`;
2562
2561
  }
2563
2562
  __name(buildLeaderboardRow, "buildLeaderboardRow");
2564
- function buildLeaderboardHtml(title, date, items, backgroundCss) {
2563
+ function buildLeaderboardHtml(title, date, items, backgroundCss, fontFaces) {
2565
2564
  const maxCount = Math.max(1, ...items.map((item) => item.count || 0));
2566
2565
  const rows = items.map((item, index) => buildLeaderboardRow(item, index, maxCount)).join("\n");
2567
2566
  return `<!DOCTYPE html>
@@ -2570,6 +2569,7 @@ ${targetUserRecord[0].username}
2570
2569
  <meta charset="UTF-8">
2571
2570
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
2572
2571
  <style>
2572
+ ${fontFaces || ""}
2573
2573
  * { box-sizing: border-box; margin: 0; padding: 0; }
2574
2574
  html, body {
2575
2575
  width: 900px;
@@ -2578,7 +2578,7 @@ ${targetUserRecord[0].username}
2578
2578
  background-color: #F5F7FA;
2579
2579
  }
2580
2580
  body {
2581
- font-family: "PingFang SC", "Microsoft YaHei", "Noto Sans SC", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
2581
+ font-family: "PingFang SC", "Microsoft YaHei", "Noto Sans SC", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "NotoEmoji", "Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji";
2582
2582
  -webkit-font-smoothing: antialiased;
2583
2583
  }
2584
2584
  .card {
@@ -2594,7 +2594,7 @@ ${targetUserRecord[0].username}
2594
2594
  .row { display: flex; align-items: center; gap: 12px; width: 100%; }
2595
2595
  .avatar { width: 40px; height: 40px; flex-shrink: 0; }
2596
2596
  .avatar img { width: 100%; height: 100%; border-radius: 50%; object-fit: cover; box-shadow: 0 1px 2px rgba(0,0,0,0.08); background: #fff; padding: 2px; }
2597
- .bar { position: relative; flex-grow: 1; height: 40px; border-radius: 8px; overflow: hidden; display: flex; align-items: center; justify-content: space-between; padding: 0 12px; }
2597
+ .bar { position: relative; flex-grow: 1; height: 40px; border-radius: 8px; overflow: hidden; display: flex; align-items: center; justify-content: space-between; padding: 0 12px; background: rgba(255,255,255,0.34); -webkit-backdrop-filter: blur(10px); backdrop-filter: blur(10px); border: 1px solid rgba(255,255,255,0.45); box-shadow: 0 1px 2px rgba(0,0,0,0.04); }
2598
2598
  .bar::after { content: ''; position: absolute; inset: 0; background-image: linear-gradient(to right, transparent 99%, rgba(255,255,255,0.2) 100%); background-size: 10% 100%; pointer-events: none; }
2599
2599
  .fill { position: absolute; left: 0; top: 0; height: 100%; border-radius: 8px 0 0 8px; z-index: 0; }
2600
2600
  .bar-content { position: relative; z-index: 1; display: flex; align-items: center; justify-content: space-between; width: 100%; }
@@ -2623,14 +2623,30 @@ ${rows}
2623
2623
  if (typeof ctx.puppeteer?.page !== "function") return null;
2624
2624
  let page;
2625
2625
  try {
2626
- const html = buildLeaderboardHtml(title, date, items, backgroundCss);
2626
+ // 复用图表的内置字体(含纯 emoji NotoEmoji),以相对路径 fonts/<file> 加载,
2627
+ // 所以必须把模板写到 data/messageCounter/ 目录里再用 file:// 访问,相对路径才能解析。
2628
+ const fontFaces = generateFontFacesCSS(["HarmonyOS_Sans_Medium.ttf", "NotoEmoji.ttf"]);
2629
+ const html = buildLeaderboardHtml(title, date, items, backgroundCss, fontFaces);
2627
2630
  page = await ctx.puppeteer.page();
2628
2631
  await page.setViewport({ width: 900, height: 400, deviceScaleFactor: 2 });
2632
+ const pageUrl = templateHtmlPath.includes(":") ? `file:///${templateHtmlPath}` : `file://${templateHtmlPath}`;
2629
2633
  try {
2630
- await page.setContent(html, { waitUntil: "networkidle0", timeout: 20000 });
2631
- } catch (e) {
2632
- logger.warn("Leaderboard template wait timed out, screenshotting current content.");
2634
+ await fs.writeFile(templateHtmlPath, html);
2635
+ } catch (writeError) {
2636
+ logger.warn("无法写入排行榜模板文件,改用 setContent:", writeError?.message);
2633
2637
  }
2638
+ if (typeof page.goto === "function") {
2639
+ try {
2640
+ await page.goto(pageUrl, { waitUntil: "load", timeout: 20000 });
2641
+ } catch (e) {
2642
+ logger.warn("Leaderboard template goto failed, using setContent:", e?.message);
2643
+ await page.setContent(html, { waitUntil: "networkidle0", timeout: 20000 }).catch(() => {});
2644
+ }
2645
+ } else {
2646
+ await page.setContent(html, { waitUntil: "networkidle0", timeout: 20000 }).catch(() => {});
2647
+ }
2648
+ // 确保 emoji 字体就绪再截图
2649
+ await page.evaluate(() => document.fonts.ready).catch(() => {});
2634
2650
  await page.waitForSelector(".card").catch(() => {});
2635
2651
  // 背景(自定义 CSS/API 背景)是加在 html 上的,必须截整页才能把卡片外的背景一起截进来;
2636
2652
  // body 高度由内容撑开(无 min-height),所以 fullPage 不会产生多余空白。
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "koishi-plugin-ll-mc",
3
3
  "description": "消息数量统计插件(基于 koishi-plugin-message-counter 重做,官方QQ机器人兼容)",
4
- "version": "3.1.4",
4
+ "version": "3.1.6",
5
5
  "main": "lib/index.js",
6
6
  "typings": "lib/index.d.ts",
7
7
  "files": [