koishi-plugin-ciyi 2.2.1 → 2.3.0

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/CHANGELOG.md CHANGED
@@ -1,3 +1,7 @@
1
+ # 2.3.0 · 2026-09-26
2
+
3
+ 四张卡片重新排版:页脚贴合卡片圆角、提示不再被截成省略号,图例单独成行,结果卡去掉压边的字印,量高与绘制共用同一组版式常量。 主指令在 help 列表里补回描述;去掉「.显示」显示模式指令,有图只发图、出图失败才退回文字;多轮输入不再追加计时说明。
4
+
1
5
  # 2.2.0 · 2026-09-26
2
6
 
3
7
  图片同时保留文字线索与排行榜;修复奖牌数字对比度;裸词冲突保护。
package/DESIGN_SYSTEM.md CHANGED
@@ -13,12 +13,12 @@
13
13
 
14
14
  ## 输出、交互与内容
15
15
 
16
- 1. 图文模式只发渲染图,不附文字;完整文字只在用户切到「文字」模式时出现。图片的等价文字随图片标记,切换到文字模式时展开,因此不会在群里重复刷屏。
17
- 2. `插件主指令.显示 文字` / `插件主指令.显示 图文` 切换个人显示偏好;同一机器人中的这些插件共享选择。设置保存在内存,重启恢复图文。文本回复不能把后续条目无提示地压掉;较长管理列表分页。
16
+ 1. 有渲染图时只发图片,不附同内容的文字,避免在群里重复刷屏;图片生成失败时才退回文字。插件不提供个人显示模式开关。
17
+ 2. 每个插件的主指令都带描述,`help` 列表右侧不留空;主指令先于任何子指令注册(Koishi 会忽略已存在指令的描述)。文本回复不能把后续条目无提示地压掉;较长管理列表分页。
18
18
  3. 裸输入必须符合词法、对局状态及参与条件。多个插件匹配时不执行,要求明确完整指令;完整指令继续可用。
19
19
  4. 回复说明当前结果、未完成原因和可执行的下一步,不用管理员日志替代用户可用的反馈。退款、赔付与渲染失败不能伪装成功。
20
20
  5. 破坏性清空需展示范围、不可撤销性和限时确认码。确认码限制到同一机器人、平台、用户与频道,使用后立即失效。
21
- 6. 多轮输入允许 5 分钟等待,发送「延长」重新计时,发送「取消」退出。斗牛、21 点提供延长等待入口。文字模式不主动撤回已发出的说明。
21
+ 6. 多轮输入等待 5 分钟,提问里写明可回复的内容与「取消」,不再追加计时说明。对局内的短窗口(如 21 点保险、投降)提供显式跳过,全员表态后立即进入下一阶段,而不是提供「延长」。
22
22
  7. 货币发放先写入核对记录,入账结果不明确时保留待核对状态,不自动重发。管理员核对实际流水并按需人工补发后标记确认;确认操作自身不转账。
23
23
 
24
24
  ## 内容边界
@@ -44,7 +44,7 @@ node markdown-to-image-service/scripts/sync-design-system.mjs --check
44
44
 
45
45
  官方色彩算法可在源仓库安装开发依赖后通过 `node scripts/build-material-colors.mjs` 重建,再同步消费者。
46
46
 
47
- 回归检查覆盖:24 个源色相的明暗角色对比度、奖牌前景、图表填充/轨道差异、浏览器/服务端 HCT 一致性、文字输出与身份隔离、输入冲突、可延长输入、入账异常、清空确认和 Wordle 并发/渲染失败。工作区截图脚本为 `scripts/verify-rendering.ts`,需 Puppeteer Core 和可用 Chromium。
47
+ 回归检查覆盖:24 个源色相的明暗角色对比度、奖牌前景、图表填充/轨道差异、浏览器/服务端 HCT 一致性、主指令描述、输入冲突、入账异常、清空确认和 Wordle 并发/渲染失败。工作区截图脚本为 `scripts/verify-rendering.ts`,需 Puppeteer Core 和可用 Chromium。
48
48
 
49
49
  ## 本轮改动对应关系
50
50
 
@@ -53,9 +53,9 @@ node markdown-to-image-service/scripts/sync-design-system.mjs --check
53
53
  | 共享设计规范 | 官方 HCT、2025 动态颜色角色、共享字阶与间距、同步与哈希校验 |
54
54
  | Markdown | M3 明暗主题;旧主题名按明暗迁移;代码高亮与 Mermaid 使用 M3 角色;长代码可换行 |
55
55
  | message-counter / monetary-rank | M3 角色与共享图表字阶、HCT 头像配色、完整文字榜单;保留昵称截断 |
56
- | Wordle | 替换复制的 NYT 外观;状态符号与图例;明暗角色;文字棋盘;异常释放并发锁;安全解析模式编号 |
57
- | MCDLE | 历史猜测完整文字属性;两列属性卡减少超宽表格 |
56
+ | Wordle | 替换复制的 NYT 外观;状态符号与图例;明暗角色;异常释放并发锁;安全解析模式编号;裸词只在确定是猜测时回应 |
57
+ | MCDLE | 猜测按行排成一张表,方便逐项对照;单句页脚居中 |
58
58
  | 2048 / ciyi | 数字与奖牌前景按实际对比度选色 |
59
- | 斗牛 / 21 点 | 付款核对记录、如实反馈退款失败、明确下注金额、延长操作窗口 |
59
+ | 斗牛 / 21 点 | 付款核对记录、如实反馈退款失败、明确下注金额、可显式跳过的短窗口 |
60
60
  | sentinel | 完整封印列表按每页 10 位展示 |
61
- | 其余插件 | 图文双通道、个人文字模式、可取消/延长输入与上下文反馈 |
61
+ | 其余插件 | 渲染失败退回文字、可取消输入与上下文反馈 |
package/README.md CHANGED
@@ -27,6 +27,6 @@ yarn add koishi-plugin-ciyi
27
27
 
28
28
  ## 显示与交互
29
29
 
30
- 发送 `ciyi.显示 文字` 或 `ciyi.显示 图文` 切换个人显示偏好。同一机器人中的配套插件共享选择,重启后恢复图文。图文模式中的信息图片附带文字说明;作品素材与感官测试的适用边界见 [设计系统](./DESIGN_SYSTEM.md)。
30
+ 有渲染图时只发图片,不再附带同内容的文字;图片生成失败时才退回文字。作品素材与感官测试的适用边界见 [设计系统](./DESIGN_SYSTEM.md)。
31
31
 
32
- 本次更新:图片同时保留文字线索与排行榜;修复奖牌数字对比度;裸词冲突保护。
32
+ 本次更新:四张卡片重新排版:页脚贴合卡片圆角、提示不再被截成省略号,图例单独成行,结果卡去掉压边的字印,量高与绘制共用同一组版式常量。 主指令在 help 列表里补回描述;去掉「.显示」显示模式指令,有图只发图、出图失败才退回文字;多轮输入不再追加计时说明。
package/lib/index.js CHANGED
@@ -36,28 +36,13 @@ var import_koishi = require("koishi");
36
36
  var KEY = Symbol.for("araea.koishi.ux.v1");
37
37
  function state(ctx) {
38
38
  const root = ctx.root;
39
- return root[KEY] ??= { modes: /* @__PURE__ */ new Map(), owners: /* @__PURE__ */ new Set() };
39
+ return root[KEY] ??= { owners: /* @__PURE__ */ new Set() };
40
40
  }
41
41
  __name(state, "state");
42
- var userKey = /* @__PURE__ */ __name((session) => `${session.platform}:${session.selfId}:${session.userId}`, "userKey");
43
- function usePresentation(ctx, command) {
44
- const shared = state(ctx);
45
- ctx.command(`${command}.显示 [mode:string]`, "选择图文或文字输出").action(({ session }, mode) => {
46
- if (!mode) return `当前:${shared.modes.get(userKey(session)) ? "文字" : "图文"}。发送「${command}.显示 文字」或「${command}.显示 图文」。设置对本机器人各插件生效,重启后恢复图文。`;
47
- if (!["文字", "图文"].includes(mode)) return "请选择「文字」或「图文」。";
48
- shared.modes.set(userKey(session), mode === "文字");
49
- return `已切换为${mode}输出。`;
50
- });
51
- return {
52
- textOnly: /* @__PURE__ */ __name((session) => shared.modes.get(userKey(session)) === true, "textOnly"),
53
- /** 图文模式只给图片;图片缺失或用户选了文字模式时才退回文字。 */
54
- present(session, image, text) {
55
- if (!image || shared.modes.get(userKey(session))) return text;
56
- return import_koishi.h.normalize(image);
57
- }
58
- };
42
+ function present(image, text) {
43
+ return image ? import_koishi.h.normalize(image) : text;
59
44
  }
60
- __name(usePresentation, "usePresentation");
45
+ __name(present, "present");
61
46
  function registerDirectInput(ctx, name2, matches) {
62
47
  const owner = { name: name2, matches };
63
48
  state(ctx).owners.add(owner);
@@ -5340,12 +5325,86 @@ function textLeftFit(ctx, text, x, y, maxWidth, font, color, offsetY = 0) {
5340
5325
  textLeft(ctx, ellipsize(text, maxWidth, fontSizeOf(font)), x, y, font, color, offsetY);
5341
5326
  }
5342
5327
  __name(textLeftFit, "textLeftFit");
5343
- function drawCard(ctx, x, y, w, h3) {
5344
- fillRound(ctx, x, y, w, h3, s(SHAPE.extraLarge), C.surfaceContainerLowest);
5328
+ var L = {
5329
+ margin: 16,
5330
+ pad: 24,
5331
+ gap: 20,
5332
+ headerTop: 20,
5333
+ seal: 48,
5334
+ headerBottom: 18,
5335
+ footerPadY: 14,
5336
+ footerLine: 20,
5337
+ sectionTitle: 20,
5338
+ sectionGap: 12
5339
+ };
5340
+ var HEADER_H = L.headerTop + L.seal + L.headerBottom;
5341
+ function cornerRect(ctx, x, y, w, h3, [tl, tr, br, bl]) {
5342
+ ctx.beginPath();
5343
+ ctx.moveTo(x + tl, y);
5344
+ ctx.lineTo(x + w - tr, y);
5345
+ if (tr) ctx.arcTo(x + w, y, x + w, y + tr, tr);
5346
+ else ctx.lineTo(x + w, y);
5347
+ ctx.lineTo(x + w, y + h3 - br);
5348
+ if (br) ctx.arcTo(x + w, y + h3, x + w - br, y + h3, br);
5349
+ else ctx.lineTo(x + w, y + h3);
5350
+ ctx.lineTo(x + bl, y + h3);
5351
+ if (bl) ctx.arcTo(x, y + h3, x, y + h3 - bl, bl);
5352
+ else ctx.lineTo(x, y + h3);
5353
+ ctx.lineTo(x, y + tl);
5354
+ if (tl) ctx.arcTo(x, y, x + tl, y, tl);
5355
+ else ctx.lineTo(x, y);
5356
+ ctx.closePath();
5357
+ }
5358
+ __name(cornerRect, "cornerRect");
5359
+ function hline(ctx, x0, x1, y, color, dash) {
5360
+ ctx.save();
5361
+ ctx.strokeStyle = color;
5362
+ ctx.lineWidth = s(1);
5363
+ if (dash) ctx.setLineDash(dash);
5364
+ ctx.beginPath();
5365
+ ctx.moveTo(x0, y);
5366
+ ctx.lineTo(x1, y);
5367
+ ctx.stroke();
5368
+ ctx.restore();
5369
+ }
5370
+ __name(hline, "hline");
5371
+ function footerRows(innerW, lines) {
5372
+ const fs = s(TYPE.labelMedium.size);
5373
+ const avail = innerW - s(L.pad) * 2 - s(24);
5374
+ const rows = [];
5375
+ for (const line of lines) {
5376
+ const lw = line.left ? textWidth(line.left, fs) : 0;
5377
+ const rw = line.right ? textWidth(line.right, fs) : 0;
5378
+ if (line.left && line.right && lw + rw > avail) rows.push({ left: line.left }, { left: line.right });
5379
+ else rows.push(line);
5380
+ }
5381
+ return rows;
5345
5382
  }
5346
- __name(drawCard, "drawCard");
5383
+ __name(footerRows, "footerRows");
5384
+ function footerHeight(innerW, lines) {
5385
+ return s(L.footerPadY) * 2 + footerRows(innerW, lines).length * s(L.footerLine);
5386
+ }
5387
+ __name(footerHeight, "footerHeight");
5388
+ function drawFooter(ctx, ix, iy, iw, ih, lines) {
5389
+ const rows = footerRows(iw, lines);
5390
+ const h3 = footerHeight(iw, lines);
5391
+ const y = iy + ih - h3;
5392
+ const r = s(SHAPE.extraLarge);
5393
+ ctx.fillStyle = C.surfaceContainerLow;
5394
+ cornerRect(ctx, ix, y, iw, h3, [0, 0, r, r]);
5395
+ ctx.fill();
5396
+ hline(ctx, ix, ix + iw, y, C.outlineVariant);
5397
+ const font = `${s(TYPE.labelMedium.size)}px ${FONT_SANS}`;
5398
+ const maxW = iw - s(L.pad) * 2;
5399
+ rows.forEach((row, i) => {
5400
+ const cy = y + s(L.footerPadY) + s(L.footerLine) * (i + 0.5);
5401
+ if (row.left) textLeft(ctx, ellipsize(row.left, maxW, s(TYPE.labelMedium.size)), ix + s(L.pad), cy, font, C.onSurfaceVariant);
5402
+ if (row.right) textRight(ctx, row.right, ix + iw - s(L.pad), cy, font, C.onSurfaceVariant);
5403
+ });
5404
+ }
5405
+ __name(drawFooter, "drawFooter");
5347
5406
  async function toPng(service, innerW, innerH, draw) {
5348
- const margin = s(18);
5407
+ const margin = s(L.margin);
5349
5408
  const canvasW = margin * 2 + innerW;
5350
5409
  const canvasH = margin * 2 + innerH;
5351
5410
  const canvas = await service.createCanvas(canvasW, canvasH);
@@ -5353,7 +5412,7 @@ async function toPng(service, innerW, innerH, draw) {
5353
5412
  try {
5354
5413
  ctx.fillStyle = C.surface;
5355
5414
  ctx.fillRect(0, 0, canvasW, canvasH);
5356
- drawCard(ctx, margin, margin, innerW, innerH);
5415
+ fillRound(ctx, margin, margin, innerW, innerH, s(SHAPE.extraLarge), C.surfaceContainerLowest);
5357
5416
  draw(ctx, margin, margin, innerW, innerH);
5358
5417
  return await canvas.toBuffer("image/png");
5359
5418
  } finally {
@@ -5451,700 +5510,354 @@ function drawWordGrid(ctx, x, y, word, size, mask) {
5451
5510
  }
5452
5511
  __name(drawWordGrid, "drawWordGrid");
5453
5512
  function drawSeal(ctx, x, y, size, lines) {
5454
- fillRound(ctx, x, y, size, size, s(SHAPE.extraLarge), C.primary);
5455
- const fs = s(TYPE.titleLarge.size);
5456
- const lh = fs * 1.06;
5457
- const total = lh * lines.length;
5458
- let cy = y + size / 2 - total / 2 + lh / 2;
5513
+ fillRound(ctx, x, y, size, size, s(SHAPE.large), C.primary);
5514
+ const fs = s(TYPE.titleMedium.size);
5515
+ const lh = fs * 1.1;
5516
+ let cy = y + size / 2 - lh * lines.length / 2 + lh / 2;
5459
5517
  for (const line of lines) {
5460
- textCenter(ctx, line, x + size / 2, cy, `${EMPHASIZED_WEIGHT.title} ${fs}px ${FONT_SANS}`, C.onPrimary);
5518
+ textCenter(ctx, line, x + size / 2, cy, `${EMPHASIZED_WEIGHT.title} ${fs}px ${FONT_SANS}`, C.onPrimary, cjkOffset(fs));
5461
5519
  cy += lh;
5462
5520
  }
5463
5521
  }
5464
5522
  __name(drawSeal, "drawSeal");
5465
- function drawHeader(ctx, innerX, innerY, innerW, sub, right) {
5466
- const padX = s(22);
5467
- const padTop = s(19);
5468
- const padBottom = s(16);
5469
- const sealSize = s(52);
5470
- const h3 = padTop + sealSize + padBottom;
5471
- drawSeal(ctx, innerX + padX, innerY + padTop, sealSize, ["词", "意"]);
5472
- const brandX = innerX + padX + sealSize + s(15);
5473
- textLeft(
5474
- ctx,
5475
- "词意",
5476
- brandX,
5477
- innerY + padTop + s(14),
5478
- `${EMPHASIZED_WEIGHT.headline} ${s(TYPE.headlineMedium.size)}px ${FONT_SANS}`,
5479
- C.onSurface
5480
- );
5481
- textLeft(
5482
- ctx,
5483
- "ci yi",
5484
- brandX,
5485
- innerY + padTop + s(36),
5486
- `italic ${s(TYPE.labelSmall.size)}px ${FONT_SANS}`,
5487
- C.onSurfaceVariant
5488
- );
5489
- const subRight = right ? innerX + innerW - padX - s(150) : innerX + innerW - padX;
5490
- textLeftFit(
5491
- ctx,
5492
- sub,
5493
- brandX,
5494
- innerY + padTop + s(58),
5495
- subRight - brandX,
5496
- `${s(TYPE.bodySmall.size)}px ${FONT_SANS}`,
5497
- C.onSurfaceVariant
5498
- );
5523
+ function drawHeader(ctx, ix, iy, iw, sub, right) {
5524
+ const x = ix + s(L.pad);
5525
+ const top = iy + s(L.headerTop);
5526
+ const seal = s(L.seal);
5527
+ drawSeal(ctx, x, top, seal, ["词", "意"]);
5528
+ const tx = x + seal + s(16);
5529
+ const rightW = right ? Math.max(textWidth(right.big, s(TYPE.headlineMedium.size)), textWidth(right.cap, s(TYPE.labelMedium.size))) + s(24) : 0;
5530
+ textLeft(ctx, "词意", tx, top + s(14), `${EMPHASIZED_WEIGHT.headline} ${s(TYPE.titleLarge.size)}px ${FONT_SANS}`, C.onSurface);
5531
+ textLeftFit(ctx, sub, tx, top + s(37), ix + iw - s(L.pad) - rightW - tx, `${s(TYPE.bodyMedium.size)}px ${FONT_SANS}`, C.onSurfaceVariant);
5499
5532
  if (right) {
5500
- const rx = innerX + innerW - padX;
5501
- textRight(
5502
- ctx,
5503
- right.big,
5504
- rx,
5505
- innerY + padTop + s(16),
5506
- `${EMPHASIZED_WEIGHT.headline} ${s(TYPE.headlineLarge.size)}px ${FONT_NUM}`,
5507
- C.primary
5508
- );
5509
- textRight(
5510
- ctx,
5511
- right.cap,
5512
- rx,
5513
- innerY + padTop + s(44),
5514
- `${s(TYPE.labelSmall.size)}px ${FONT_SANS}`,
5515
- C.onSurfaceVariant
5516
- );
5533
+ const rx = ix + iw - s(L.pad);
5534
+ textRight(ctx, right.big, rx, top + s(16), `${EMPHASIZED_WEIGHT.headline} ${s(TYPE.headlineMedium.size)}px ${FONT_NUM}`, C.primary);
5535
+ textRight(ctx, right.cap, rx, top + s(40), `${s(TYPE.labelMedium.size)}px ${FONT_SANS}`, C.onSurfaceVariant);
5517
5536
  }
5518
- const lineY = innerY + h3;
5519
- ctx.strokeStyle = C.outlineVariant;
5520
- ctx.lineWidth = s(1);
5521
- ctx.beginPath();
5522
- ctx.moveTo(innerX, lineY);
5523
- ctx.lineTo(innerX + innerW, lineY);
5524
- ctx.stroke();
5525
- fillRound(ctx, innerX + padX, lineY - s(1.5), s(54), s(3), s(SHAPE.full), C.primary);
5526
- return h3;
5537
+ hline(ctx, ix, ix + iw, iy + s(HEADER_H), C.outlineVariant);
5538
+ return s(HEADER_H);
5527
5539
  }
5528
5540
  __name(drawHeader, "drawHeader");
5529
- function drawFooter(ctx, innerX, y, innerW, legendItems, tip) {
5530
- const padX = s(22);
5531
- const padY = s(12);
5532
- const h3 = s(36);
5533
- const inset = s(10);
5534
- fillRound(
5535
- ctx,
5536
- innerX + inset,
5537
- y,
5538
- innerW - inset * 2,
5539
- h3 + padY * 2,
5540
- s(SHAPE.large),
5541
- C.surfaceContainerLow
5542
- );
5543
- ctx.strokeStyle = C.outlineVariant;
5544
- ctx.lineWidth = s(1);
5545
- ctx.beginPath();
5546
- ctx.moveTo(innerX, y);
5547
- ctx.lineTo(innerX + innerW, y);
5548
- ctx.stroke();
5549
- let lx = innerX + padX;
5550
- const cy = y + padY + h3 / 2;
5551
- for (const { tier, range } of legendItems) {
5552
- fillRound(ctx, lx, cy - s(4.5), s(9), s(9), s(SHAPE.full), tier.color);
5553
- lx += s(9) + s(6);
5554
- textLeft(ctx, tier.name, lx, cy, `${s(TYPE.labelSmall.size)}px ${FONT_SANS}`, C.onSurfaceVariant);
5555
- lx += textWidth(tier.name, s(TYPE.labelSmall.size)) + s(4);
5556
- textLeft(ctx, range, lx, cy, `${s(TYPE.labelSmall.size)}px ${FONT_NUM}`, C.onSurfaceVariant);
5557
- lx += textWidth(range, s(TYPE.labelSmall.size)) + s(13);
5558
- }
5559
- const tipFont = `${s(TYPE.labelSmall.size)}px ${FONT_SANS}`;
5560
- const maxTipWidth = legendItems.length ? s(145) : innerW - padX * 2;
5561
- textRight(
5562
- ctx,
5563
- ellipsize(tip, maxTipWidth, s(TYPE.labelSmall.size)),
5564
- innerX + innerW - padX,
5565
- cy,
5566
- tipFont,
5567
- C.onSurfaceVariant
5568
- );
5569
- return h3 + padY * 2;
5570
- }
5571
- __name(drawFooter, "drawFooter");
5572
- function drawLegendFull(ctx, x, y, w) {
5573
- const colW = w / 2;
5574
- const rowH = s(30);
5575
- for (let i = 0; i < TIERS.length; i++) {
5576
- const t = TIERS[i];
5577
- const cx = x + i % 2 * colW;
5578
- const cy = y + Math.floor(i / 2) * rowH;
5579
- fillRound(ctx, cx, cy - s(4.5), s(9), s(9), s(SHAPE.full), t.color);
5580
- textLeft(ctx, t.name, cx + s(15), cy, `${s(TYPE.labelSmall.size)}px ${FONT_SANS}`, C.onSurfaceVariant);
5581
- textLeft(ctx, tierRange(i), cx + s(52), cy, `${s(TYPE.labelSmall.size)}px ${FONT_NUM}`, C.onSurfaceVariant);
5582
- textLeft(ctx, t.note, cx + s(100), cy, `${s(TYPE.labelSmall.size)}px ${FONT_SANS}`, C.onSurfaceVariant);
5583
- }
5584
- return rowH * Math.ceil(TIERS.length / 2) + s(8);
5585
- }
5586
- __name(drawLegendFull, "drawLegendFull");
5587
- function drawSectionTitle(ctx, x, y, w, title) {
5588
- fillRound(ctx, x, y - s(1), s(3), s(12), s(SHAPE.full), C.primary);
5589
- textLeft(
5590
- ctx,
5591
- title,
5592
- x + s(15),
5593
- y + s(5),
5594
- `${EMPHASIZED_WEIGHT.label} ${s(TYPE.labelMedium.size)}px ${FONT_SANS}`,
5595
- C.onSurfaceVariant
5596
- );
5597
- const tw = textWidth(title, s(TYPE.labelMedium.size));
5598
- ctx.strokeStyle = C.outlineVariant;
5599
- ctx.lineWidth = s(1);
5600
- ctx.beginPath();
5601
- ctx.moveTo(x + s(15) + tw + s(9), y + s(5));
5602
- ctx.lineTo(x + w, y + s(5));
5603
- ctx.stroke();
5604
- return s(24);
5541
+ function drawSectionTitle(ctx, x, y, title) {
5542
+ const h3 = s(L.sectionTitle);
5543
+ fillRound(ctx, x, y + h3 / 2 - s(7), s(4), s(14), s(SHAPE.full), C.primary);
5544
+ textLeft(ctx, title, x + s(12), y + h3 / 2, `${EMPHASIZED_WEIGHT.title} ${s(TYPE.titleSmall.size)}px ${FONT_SANS}`, C.onSurface);
5545
+ return h3 + s(L.sectionGap);
5605
5546
  }
5606
5547
  __name(drawSectionTitle, "drawSectionTitle");
5607
5548
  function drawPanel(ctx, x, y, w, h3) {
5608
5549
  fillRound(ctx, x, y, w, h3, s(SHAPE.large), C.surfaceContainer);
5609
5550
  }
5610
5551
  __name(drawPanel, "drawPanel");
5552
+ var NEAR_BAR_W = 80;
5611
5553
  function drawNearBar(ctx, x, y, pct, tier) {
5612
- const barW = s(88);
5554
+ const barW = s(NEAR_BAR_W);
5613
5555
  const barH = s(8);
5614
5556
  const gap = s(3);
5615
5557
  const fillW = Math.max(barH, barW * pct / 100);
5616
5558
  const top = y - barH / 2;
5617
5559
  fillRound(ctx, x, top, fillW, barH, s(SHAPE.full), tier.color);
5618
5560
  if (fillW + gap < barW) {
5619
- fillRound(
5620
- ctx,
5621
- x + fillW + gap,
5622
- top,
5623
- barW - fillW - gap,
5624
- barH,
5625
- s(SHAPE.full),
5626
- C.surfaceContainerHighest
5627
- );
5561
+ fillRound(ctx, x + fillW + gap, top, barW - fillW - gap, barH, s(SHAPE.full), C.surfaceContainerHighest);
5628
5562
  }
5629
- textLeft(
5630
- ctx,
5631
- tier.name,
5632
- x + barW + s(9),
5633
- y,
5634
- `${EMPHASIZED_WEIGHT.label} ${s(TYPE.labelMedium.size)}px ${FONT_SANS}`,
5635
- tier.color
5636
- );
5563
+ textLeft(ctx, tier.name, x + barW + s(10), y, `${EMPHASIZED_WEIGHT.label} ${s(TYPE.labelLarge.size)}px ${FONT_SANS}`, tier.color);
5637
5564
  }
5638
5565
  __name(drawNearBar, "drawNearBar");
5639
- function measureBoard(opts) {
5640
- const innerW = s(680);
5641
- const rowH = s(50);
5642
- const gapH = s(28);
5643
- const headerH = s(87);
5644
- const tableHeadH = s(32);
5645
- const padV = s(20);
5646
- const footerH = s(60);
5647
- let rows = opts.rows.length;
5648
- for (const r of opts.rows) if (r.gapBefore) rows += 1;
5649
- const innerH = headerH + padV + tableHeadH + rows * rowH + (rows > 0 ? 0 : gapH) + padV + footerH;
5650
- return { innerW, innerH, rowH };
5566
+ var TABLE = { head: 32, row: 56, gap: 32 };
5567
+ var COLS = [
5568
+ { key: "no", label: "序", w: 30 },
5569
+ { key: "nb", label: "更近的词", w: 84 },
5570
+ { key: "gw", label: "猜测", w: 96 },
5571
+ { key: "fb", label: "更远的词", w: 84 },
5572
+ { key: "rk", label: "排名", w: 88 },
5573
+ { key: "mt", label: "亲疏", w: 136 }
5574
+ ];
5575
+ function boardHeight(rows) {
5576
+ const gaps = rows.filter((r) => r.gapBefore).length;
5577
+ return s(TABLE.head) + rows.length * s(TABLE.row) + gaps * s(TABLE.gap);
5651
5578
  }
5652
- __name(measureBoard, "measureBoard");
5653
- function drawBoardTable(ctx, x, y, w, opts, rowH) {
5654
- const accentW = s(3);
5655
- const gutter = s(10);
5656
- const tableW = w - s(44);
5657
- const cx = x + s(22);
5658
- const usable = Math.max(tableW - gutter, s(1));
5659
- const natural = [32, 72, 88, 72, 72, 148];
5660
- const naturalSum = natural.reduce((a, b) => a + b, 0);
5661
- const scale = usable / naturalSum;
5662
- const cols = {
5663
- no: natural[0] * scale,
5664
- nb: natural[1] * scale,
5665
- gw: natural[2] * scale,
5666
- rk: natural[4] * scale,
5667
- mt: natural[5] * scale
5668
- };
5669
- const bx = cx + gutter;
5670
- const nb0 = bx + cols.no;
5671
- const gw0 = nb0 + cols.nb;
5672
- const nb1 = gw0 + cols.gw;
5673
- const rk0 = nb1 + cols.nb;
5674
- const mt0 = rk0 + cols.rk;
5675
- const noX = bx + s(2);
5676
- const meterBlockW = s(88) + s(9) + textWidth("咫尺", s(TYPE.labelMedium.size));
5677
- const mtX = mt0 + Math.max(0, (cols.mt - meterBlockW) / 2);
5678
- const headY = y;
5679
- const fs = s(TYPE.labelSmall.size);
5680
- textLeft(ctx, "序", noX, headY, `${fs}px ${FONT_SANS}`, C.onSurfaceVariant);
5681
- textCenter(ctx, "更近 ◀", nb0 + cols.nb / 2, headY, `${fs}px ${FONT_SANS}`, C.onSurfaceVariant);
5682
- textCenter(ctx, "猜测", gw0 + cols.gw / 2, headY, `${fs}px ${FONT_SANS}`, C.onSurfaceVariant);
5683
- textCenter(ctx, "▶ 更远", nb1 + cols.nb / 2, headY, `${fs}px ${FONT_SANS}`, C.onSurfaceVariant);
5684
- textCenter(ctx, "排名", rk0 + cols.rk / 2, headY, `${fs}px ${FONT_SANS}`, C.onSurfaceVariant);
5685
- textCenter(ctx, "亲疏", mt0 + cols.mt / 2, headY, `${fs}px ${FONT_SANS}`, C.onSurfaceVariant);
5686
- let ry = y + s(22);
5579
+ __name(boardHeight, "boardHeight");
5580
+ function drawBoardTable(ctx, x, y, w, rows, total) {
5581
+ const sum = COLS.reduce((a, c) => a + c.w, 0);
5582
+ const scale = w / s(sum);
5583
+ const edges = {};
5584
+ let cx = x;
5585
+ for (const col of COLS) {
5586
+ const cw = s(col.w) * scale;
5587
+ edges[col.key] = { x: cx, w: cw };
5588
+ cx += cw;
5589
+ }
5590
+ const mid = /* @__PURE__ */ __name((key) => edges[key].x + edges[key].w / 2, "mid");
5591
+ const labelFont = `${s(TYPE.labelMedium.size)}px ${FONT_SANS}`;
5592
+ const headY = y + s(TABLE.head) / 2;
5593
+ textLeft(ctx, "序", x + s(8), headY, labelFont, C.onSurfaceVariant);
5594
+ for (const col of COLS.slice(1, 5)) textCenter(ctx, col.label, mid(col.key), headY, labelFont, C.onSurfaceVariant);
5595
+ const meterW = s(NEAR_BAR_W) + s(10) + textWidth("咫尺", s(TYPE.labelLarge.size));
5596
+ const meterX = edges.mt.x + Math.max(0, (edges.mt.w - meterW) / 2);
5597
+ textCenter(ctx, "亲疏", meterX + meterW / 2, headY, labelFont, C.onSurfaceVariant);
5598
+ hline(ctx, x, x + w, y + s(TABLE.head), C.outline);
5599
+ let ry = y + s(TABLE.head);
5687
5600
  let ordinal = 0;
5688
- for (const row of opts.rows) {
5601
+ for (const row of rows) {
5689
5602
  if (row.gapBefore) {
5690
- ry += s(8);
5691
- textCenter(
5692
- ctx,
5693
- `…… 另有 ${row.gapBefore} 词未列`,
5694
- cx + tableW / 2,
5695
- ry + s(10),
5696
- `${s(TYPE.labelSmall.size)}px ${FONT_SANS}`,
5697
- C.onSurfaceVariant
5698
- );
5699
- ctx.strokeStyle = C.outlineVariant;
5700
- ctx.beginPath();
5701
- ctx.moveTo(cx, ry + s(20));
5702
- ctx.lineTo(cx + tableW, ry + s(20));
5703
- ctx.stroke();
5704
- ry += s(28);
5603
+ const cy = ry + s(TABLE.gap) / 2;
5604
+ const label = `中间还有 ${row.gapBefore} 词`;
5605
+ const lw = textWidth(label, s(TYPE.labelMedium.size)) + s(24);
5606
+ hline(ctx, x, x + w / 2 - lw / 2, cy, C.outlineVariant, [s(4), s(4)]);
5607
+ hline(ctx, x + w / 2 + lw / 2, x + w, cy, C.outlineVariant, [s(4), s(4)]);
5608
+ textCenter(ctx, label, x + w / 2, cy, labelFont, C.onSurfaceVariant);
5609
+ ry += s(TABLE.gap);
5610
+ } else if (ordinal > 0) {
5611
+ hline(ctx, x, x + w, ry, C.outlineVariant);
5705
5612
  }
5706
5613
  ordinal += 1;
5707
5614
  const { history, fresh } = row;
5708
5615
  const tier = tierOf(history.rank);
5709
- const pct = nearness(history.rank, opts.total);
5616
+ const rowH = s(TABLE.row);
5710
5617
  const midY = ry + rowH / 2;
5711
5618
  if (fresh) {
5712
- fillRound(ctx, cx, ry, tableW, rowH, s(SHAPE.medium), FRESH);
5713
- fillRound(ctx, cx, ry + s(4), accentW, rowH - s(8), s(SHAPE.full), C.primary);
5619
+ fillRound(ctx, x, ry + s(3), w, rowH - s(6), s(SHAPE.medium), FRESH);
5620
+ fillRound(ctx, x, ry + s(12), s(4), rowH - s(24), s(SHAPE.full), C.primary);
5714
5621
  }
5715
- ctx.strokeStyle = ordinal === 1 ? C.outline : C.outlineVariant;
5716
- ctx.lineWidth = s(1);
5717
- ctx.beginPath();
5718
- ctx.moveTo(cx, ry);
5719
- ctx.lineTo(cx + tableW, ry);
5720
- ctx.stroke();
5721
- textLeft(
5722
- ctx,
5723
- String(ordinal).padStart(2, "0"),
5724
- noX,
5725
- midY,
5726
- `${s(TYPE.labelMedium.size)}px ${FONT_NUM}`,
5727
- C.onSurfaceVariant
5728
- );
5729
- const nbX = nb0 + (cols.nb - gridWidth(history.leftHint, "sm")) / 2;
5730
- drawWordGrid(ctx, nbX, midY - s(13), history.leftHint, "sm", 0);
5731
- const gwX = gw0 + (cols.gw - gridWidth(history.guess, "md")) / 2;
5732
- drawWordGrid(ctx, gwX, midY - s(17), history.guess, "md");
5733
- const rbX = nb1 + (cols.nb - gridWidth(history.rightHint, "sm")) / 2;
5734
- drawWordGrid(ctx, rbX, midY - s(13), history.rightHint, "sm", 1);
5622
+ textLeft(ctx, String(ordinal).padStart(2, "0"), x + s(8), midY, `${s(TYPE.labelLarge.size)}px ${FONT_NUM}`, C.onSurfaceVariant);
5623
+ drawWordGrid(ctx, mid("nb") - gridWidth(history.leftHint, "sm") / 2, midY - s(GRID.sm.cell) / 2, history.leftHint, "sm", 0);
5624
+ drawWordGrid(ctx, mid("gw") - gridWidth(history.guess, "md") / 2, midY - s(GRID.md.cell) / 2, history.guess, "md");
5625
+ drawWordGrid(ctx, mid("fb") - gridWidth(history.rightHint, "sm") / 2, midY - s(GRID.sm.cell) / 2, history.rightHint, "sm", 1);
5735
5626
  const rankText = String(history.rank);
5736
- const rankFont = `${EMPHASIZED_WEIGHT.title} ${s(TYPE.titleLarge.size)}px ${FONT_NUM}`;
5737
- const rankWidth = textWidth(rankText, s(TYPE.titleLarge.size));
5738
- const hashW = textWidth("#", s(TYPE.labelMedium.size));
5739
- const rankBlock = hashW + s(3) + rankWidth;
5740
- const rankLeft = rk0 + (cols.rk - rankBlock) / 2;
5741
- textLeft(ctx, "#", rankLeft, midY, `${s(TYPE.labelMedium.size)}px ${FONT_NUM}`, C.onSurfaceVariant);
5742
- textLeft(ctx, rankText, rankLeft + hashW + s(3), midY, rankFont, tier.color);
5743
- drawNearBar(ctx, mtX, midY, pct, tier);
5627
+ const hashFs = s(TYPE.labelLarge.size);
5628
+ const rankFs = s(TYPE.titleLarge.size);
5629
+ const block = textWidth("#", hashFs) + s(2) + textWidth(rankText, rankFs);
5630
+ const left = mid("rk") - block / 2;
5631
+ textLeft(ctx, "#", left, midY, `${hashFs}px ${FONT_NUM}`, C.onSurfaceVariant);
5632
+ textLeft(ctx, rankText, left + textWidth("#", hashFs) + s(2), midY, `${EMPHASIZED_WEIGHT.title} ${rankFs}px ${FONT_NUM}`, tier.color);
5633
+ drawNearBar(ctx, meterX, midY, nearness(history.rank, total), tier);
5744
5634
  ry += rowH;
5745
5635
  }
5746
5636
  return ry - y;
5747
5637
  }
5748
5638
  __name(drawBoardTable, "drawBoardTable");
5639
+ var LEGEND_H = 28;
5640
+ function drawLegendRow(ctx, x, y, w) {
5641
+ const slot = w / TIERS.length;
5642
+ const fs = s(TYPE.labelMedium.size);
5643
+ const cy = y + s(LEGEND_H) / 2;
5644
+ TIERS.forEach((tier, i) => {
5645
+ const label = `${tier.name} ${tierRange(i)}`;
5646
+ const itemW = s(8) + s(6) + textWidth(label, fs);
5647
+ const lx = x + slot * i + (slot - itemW) / 2;
5648
+ fillRound(ctx, lx, cy - s(4), s(8), s(8), s(SHAPE.full), tier.color);
5649
+ textLeft(ctx, tier.name, lx + s(14), cy, `${fs}px ${FONT_SANS}`, C.onSurfaceVariant);
5650
+ textLeft(ctx, tierRange(i), lx + s(14) + textWidth(tier.name + " ", fs), cy, `${fs}px ${FONT_NUM}`, C.onSurfaceVariant);
5651
+ });
5652
+ }
5653
+ __name(drawLegendRow, "drawLegendRow");
5654
+ var BOARD_W = 680;
5749
5655
  function renderBoardCard(service, opts) {
5750
- const { innerW, innerH, rowH } = measureBoard(opts);
5656
+ const innerW = s(BOARD_W);
5657
+ const footer = [{ left: opts.tip }];
5658
+ const tableH = boardHeight(opts.rows);
5659
+ const innerH = s(HEADER_H) + s(L.gap) + tableH + s(L.gap) + s(LEGEND_H) + s(L.gap) + footerHeight(innerW, footer);
5751
5660
  const best = opts.rows.length ? `#${opts.rows.reduce((m, r) => Math.min(m, r.history.rank), Infinity)}` : "—";
5752
5661
  return toPng(service, innerW, innerH, (ctx, ix, iy, iw, ih) => {
5753
- let y = iy + drawHeader(ctx, ix, iy, iw, `每日挑战 · 第 ${opts.attempts} 次猜测`, {
5754
- big: best,
5755
- cap: "当前最佳"
5756
- });
5757
- y += s(20);
5758
- drawBoardTable(ctx, ix, y, iw, opts, rowH);
5759
- drawFooter(
5760
- ctx,
5761
- ix,
5762
- iy + ih - s(60),
5763
- iw,
5764
- TIERS.map((tier, i) => ({ tier, range: tierRange(i) })),
5765
- opts.tip
5766
- );
5662
+ let y = iy + drawHeader(ctx, ix, iy, iw, `每日挑战 · 第 ${opts.attempts} 次猜测`, { big: best, cap: "当前最佳" });
5663
+ y += s(L.gap);
5664
+ y += drawBoardTable(ctx, ix + s(L.pad), y, iw - s(L.pad) * 2, opts.rows, opts.total);
5665
+ y += s(L.gap);
5666
+ drawLegendRow(ctx, ix + s(L.pad), y, iw - s(L.pad) * 2);
5667
+ drawFooter(ctx, ix, iy, iw, ih, footer);
5767
5668
  });
5768
5669
  }
5769
5670
  __name(renderBoardCard, "renderBoardCard");
5770
5671
  function renderIntroCard(service, opts) {
5771
- const innerW = s(720);
5772
- const innerH = s(716);
5672
+ const innerW = s(BOARD_W);
5673
+ const footer = [{ left: "一日一词,猜中即止,次日零点换题", right: "词库与语义排序来自「词影」" }];
5674
+ const usage2 = [
5675
+ ["开始", "ciyi.猜 山水", `开题并报一个两字词${opts.middleware ? ";开题后可直接发词" : ""}`],
5676
+ ["切换", "ciyi.裸词 开/关", "改本频道的续猜方式"],
5677
+ ["排行", "ciyi.排行榜", "看谁猜中得最多"]
5678
+ ];
5679
+ const notes = [
5680
+ "这一行是说:「企业」与今日答案的意思相近程度,排在第 467 位。",
5681
+ "两侧各藏一字:左边是比它更近的词,右边是比它更远的词。",
5682
+ "名次越小,离答案越近;#1 就是答案本身。"
5683
+ ];
5684
+ const sample = [{ history: { guess: "企业", rank: 467, leftHint: "良好", rightHint: "地产" } }];
5685
+ const panelPad = 16;
5686
+ const usageRow = 36;
5687
+ const noteLine = 22;
5688
+ const legendRow = 30;
5689
+ const sectionHead = s(L.sectionTitle) + s(L.sectionGap);
5690
+ const usageH = s(panelPad) * 2 + usage2.length * s(usageRow);
5691
+ const readH = s(panelPad) * 2 + boardHeight(sample) + s(12) + notes.length * s(noteLine);
5692
+ const tierH = s(panelPad) * 2 + Math.ceil(TIERS.length / 2) * s(legendRow);
5693
+ const innerH = s(HEADER_H) + s(L.gap) + sectionHead + usageH + s(L.gap) + sectionHead + readH + s(L.gap) + sectionHead + tierH + s(L.gap) + footerHeight(innerW, footer);
5773
5694
  return toPng(service, innerW, innerH, (ctx, ix, iy, iw, ih) => {
5774
- let y = iy + drawHeader(ctx, ix, iy, iw, "按意思远近找词 · 每日一题", {
5775
- big: opts.words.toLocaleString(),
5776
- cap: "词库容量"
5695
+ const x = ix + s(L.pad);
5696
+ const w = iw - s(L.pad) * 2;
5697
+ let y = iy + drawHeader(ctx, ix, iy, iw, "按意思远近找词 · 每日一题", { big: opts.words.toLocaleString(), cap: "词库容量" });
5698
+ y += s(L.gap);
5699
+ y += drawSectionTitle(ctx, x, y, "玩法");
5700
+ drawPanel(ctx, x, y, w, usageH);
5701
+ usage2.forEach(([key, command, desc], i) => {
5702
+ const cy = y + s(panelPad) + s(usageRow) * (i + 0.5);
5703
+ textLeft(ctx, key, x + s(panelPad), cy, `${EMPHASIZED_WEIGHT.label} ${s(TYPE.labelLarge.size)}px ${FONT_SANS}`, C.primary);
5704
+ textLeft(ctx, command, x + s(72), cy, `${s(TYPE.bodyMedium.size)}px ${FONT_SANS}`, C.onSurface);
5705
+ textLeftFit(ctx, desc, x + s(200), cy, w - s(200) - s(panelPad), `${s(TYPE.bodyMedium.size)}px ${FONT_SANS}`, C.onSurfaceVariant);
5777
5706
  });
5778
- y += s(20);
5779
- y += drawSectionTitle(ctx, ix + s(22), y, iw - s(44), "玩法");
5780
- y += s(11);
5781
- const p1h = s(104);
5782
- drawPanel(ctx, ix + s(22), y, iw - s(44), p1h);
5783
- textLeft(ctx, "开始", ix + s(38), y + s(24), `${s(TYPE.labelLarge.size)}px ${FONT_SANS}`, C.primary);
5784
- textLeft(
5785
- ctx,
5786
- "ciyi.猜 山水",
5787
- ix + s(96),
5788
- y + s(24),
5789
- `${s(TYPE.labelLarge.size)}px ${FONT_SANS}`,
5790
- C.onSurface
5791
- );
5792
- textLeftFit(
5793
- ctx,
5794
- `开题并报一个两字词${opts.middleware ? ";开题后可直接发词" : ""}`,
5795
- ix + s(230),
5796
- y + s(24),
5797
- iw - s(268),
5798
- `${s(TYPE.bodyMedium.size)}px ${FONT_SANS}`,
5799
- C.onSurface
5800
- );
5801
- textLeft(ctx, "切换", ix + s(38), y + s(52), `${s(TYPE.labelLarge.size)}px ${FONT_SANS}`, C.primary);
5802
- textLeft(
5803
- ctx,
5804
- "ciyi.裸词 开/关",
5805
- ix + s(96),
5806
- y + s(52),
5807
- `${s(TYPE.labelLarge.size)}px ${FONT_SANS}`,
5808
- C.onSurface
5809
- );
5810
- textLeftFit(
5811
- ctx,
5812
- "临时改本频道的续猜方式",
5813
- ix + s(230),
5814
- y + s(52),
5815
- iw - s(268),
5816
- `${s(TYPE.bodyMedium.size)}px ${FONT_SANS}`,
5817
- C.onSurface
5818
- );
5819
- textLeft(ctx, "排行", ix + s(38), y + s(80), `${s(TYPE.labelLarge.size)}px ${FONT_SANS}`, C.primary);
5820
- textLeft(
5821
- ctx,
5822
- "ciyi.排行榜",
5823
- ix + s(96),
5824
- y + s(80),
5825
- `${s(TYPE.labelLarge.size)}px ${FONT_SANS}`,
5826
- C.onSurface
5827
- );
5828
- textLeft(
5829
- ctx,
5830
- "看谁猜中得最多",
5831
- ix + s(230),
5832
- y + s(80),
5833
- `${s(TYPE.bodyMedium.size)}px ${FONT_SANS}`,
5834
- C.onSurface
5835
- );
5836
- y += p1h + s(19);
5837
- y += drawSectionTitle(ctx, ix + s(22), y, iw - s(44), "读板");
5838
- y += s(11);
5839
- const sampleH = s(172);
5840
- drawPanel(ctx, ix + s(22), y, iw - s(44), sampleH);
5841
- drawBoardTable(
5842
- ctx,
5843
- ix + s(22),
5844
- y + s(10),
5845
- iw - s(44),
5846
- {
5847
- rows: [{ history: { guess: "企业", rank: 467, leftHint: "良好", rightHint: "地产" } }],
5848
- total: opts.total,
5849
- attempts: 1,
5850
- tip: ""
5851
- },
5852
- s(50)
5853
- );
5854
- textLeft(
5855
- ctx,
5856
- "这一行是说:「企业」与今日答案的意思相近程度,排在第 467 位。",
5857
- ix + s(38),
5858
- y + s(104),
5859
- `${s(TYPE.bodySmall.size)}px ${FONT_SANS}`,
5860
- C.onSurfaceVariant
5861
- );
5862
- textLeft(
5863
- ctx,
5864
- "两侧空格各藏一字:左邻更近,右邻更远。",
5865
- ix + s(38),
5866
- y + s(126),
5867
- `${s(TYPE.bodySmall.size)}px ${FONT_SANS}`,
5868
- C.onSurfaceVariant
5869
- );
5870
- textLeft(
5871
- ctx,
5872
- "名次越小,离答案越近;#1 就是答案本身。",
5873
- ix + s(38),
5874
- y + s(148),
5875
- `${s(TYPE.bodySmall.size)}px ${FONT_SANS}`,
5876
- C.onSurfaceVariant
5877
- );
5878
- y += sampleH + s(19);
5879
- y += drawSectionTitle(ctx, ix + s(22), y, iw - s(44), "亲疏");
5880
- y += s(11);
5881
- const tierH = s(112);
5882
- drawPanel(ctx, ix + s(22), y, iw - s(44), tierH);
5883
- drawLegendFull(ctx, ix + s(38), y + s(18), iw - s(80));
5884
- y += tierH;
5885
- drawFooter(
5886
- ctx,
5887
- ix,
5888
- iy + ih - s(60),
5889
- iw,
5890
- [],
5891
- "词库与语义排序来自「词影」"
5892
- );
5893
- textLeft(
5894
- ctx,
5895
- "一日一词,猜中即止,次日零点换题",
5896
- ix + s(22),
5897
- iy + ih - s(30),
5898
- `${s(TYPE.labelSmall.size)}px ${FONT_SANS}`,
5899
- C.onSurfaceVariant
5900
- );
5707
+ y += usageH + s(L.gap);
5708
+ y += drawSectionTitle(ctx, x, y, "读板");
5709
+ drawPanel(ctx, x, y, w, readH);
5710
+ let ry = y + s(panelPad);
5711
+ ry += drawBoardTable(ctx, x + s(panelPad), ry, w - s(panelPad) * 2, sample, opts.total) + s(12);
5712
+ for (const note of notes) {
5713
+ textLeftFit(ctx, note, x + s(panelPad), ry + s(noteLine) / 2, w - s(panelPad) * 2, `${s(TYPE.bodyMedium.size)}px ${FONT_SANS}`, C.onSurfaceVariant);
5714
+ ry += s(noteLine);
5715
+ }
5716
+ y += readH + s(L.gap);
5717
+ y += drawSectionTitle(ctx, x, y, "亲疏");
5718
+ drawPanel(ctx, x, y, w, tierH);
5719
+ const colW = (w - s(panelPad) * 2) / 2;
5720
+ const fs = s(TYPE.bodyMedium.size);
5721
+ TIERS.forEach((tier, i) => {
5722
+ const cx = x + s(panelPad) + i % 2 * colW;
5723
+ const cy = y + s(panelPad) + s(legendRow) * (Math.floor(i / 2) + 0.5);
5724
+ fillRound(ctx, cx, cy - s(5), s(10), s(10), s(SHAPE.full), tier.color);
5725
+ textLeft(ctx, tier.name, cx + s(18), cy, `${EMPHASIZED_WEIGHT.label} ${fs}px ${FONT_SANS}`, tier.color);
5726
+ textLeft(ctx, tierRange(i), cx + s(60), cy, `${fs}px ${FONT_NUM}`, C.onSurfaceVariant);
5727
+ textLeftFit(ctx, tier.note, cx + s(124), cy, colW - s(132), `${fs}px ${FONT_SANS}`, C.onSurfaceVariant);
5728
+ });
5729
+ drawFooter(ctx, ix, iy, iw, ih, footer);
5901
5730
  });
5902
5731
  }
5903
5732
  __name(renderIntroCard, "renderIntroCard");
5904
5733
  function renderWinCard(service, opts) {
5905
- const innerW = s(720);
5906
- const innerH = s(420);
5734
+ const innerW = s(BOARD_W);
5735
+ const quip = opts.closest === null ? "一击即中,今日无需第二次落笔" : `从 #${opts.closest} 一步跨到 #1`;
5736
+ const footer = [
5737
+ { left: `${opts.username} 拿下今日一词 · ${quip}` },
5738
+ { left: opts.canStartToday ? "发送「ciyi.猜 两字词」开启今日新题" : "明日零点换新题 · 发送「ciyi.排行榜」看看榜单" }
5739
+ ];
5740
+ const panelPad = 20;
5741
+ const labelH = 20;
5742
+ const answerW = gridWidth(opts.answer, "lg");
5743
+ const chipH = 32;
5744
+ const chipGap = 8;
5745
+ const chipFs = s(TYPE.labelLarge.size);
5746
+ const w = innerW - s(L.pad) * 2;
5747
+ const rightX = s(panelPad) + answerW + s(32);
5748
+ const rightW = w - rightX - s(panelPad);
5749
+ const chips = [];
5750
+ let cx = 0;
5751
+ let row = 0;
5752
+ for (const word of opts.neighbors) {
5753
+ const fitted = ellipsize(word, s(120), chipFs);
5754
+ const cw = textWidth(fitted, chipFs) + s(28);
5755
+ if (cx > 0 && cx + cw > rightW) {
5756
+ cx = 0;
5757
+ row += 1;
5758
+ }
5759
+ chips.push({ word: fitted, x: cx, row, w: cw });
5760
+ cx += cw + s(chipGap);
5761
+ }
5762
+ const chipRows = chips.length ? row + 1 : 1;
5763
+ const leftH = s(labelH) + s(12) + s(GRID.lg.cell);
5764
+ const rightH = s(labelH) + s(12) + chipRows * s(chipH) + (chipRows - 1) * s(chipGap);
5765
+ const revealH = s(panelPad) * 2 + Math.max(leftH, rightH);
5766
+ const statH = 76;
5767
+ const innerH = s(HEADER_H) + s(L.gap) + revealH + s(12) + s(statH) + s(L.gap) + footerHeight(innerW, footer);
5907
5768
  return toPng(service, innerW, innerH, (ctx, ix, iy, iw, ih) => {
5908
- let y = iy + drawHeader(ctx, ix, iy, iw, "每日挑战 · 已封题");
5909
- y += s(20);
5910
- const revealH = s(130);
5911
- drawPanel(ctx, ix + s(22), y, iw - s(44), revealH);
5912
- drawSeal(ctx, ix + iw - s(80), y - s(13), s(42), ["中"]);
5913
- textLeft(ctx, "今日答案", ix + s(38), y + s(24), `${s(TYPE.labelSmall.size)}px ${FONT_SANS}`, C.onSurfaceVariant);
5914
- drawWordGrid(ctx, ix + s(38), y + s(40), opts.answer, "lg");
5915
- const ax = ix + iw - s(310);
5916
- if (opts.neighbors.length) {
5917
- textLeft(ctx, "意思最近的几个词", ax, y + s(24), `${s(TYPE.labelSmall.size)}px ${FONT_SANS}`, C.onSurfaceVariant);
5918
- let nx = ax;
5919
- let ny = y + s(48);
5920
- const chipFont = `${s(TYPE.labelLarge.size)}px ${FONT_SANS}`;
5921
- const chipRight = ix + iw - s(38);
5922
- for (const word of opts.neighbors) {
5923
- const fittedWord = ellipsize(word, s(116), s(TYPE.labelLarge.size));
5924
- const pw = textWidth(fittedWord, s(TYPE.labelLarge.size)) + s(20);
5925
- if (nx !== ax && nx + pw > chipRight) {
5926
- nx = ax;
5927
- ny += s(36);
5928
- }
5929
- if (ny + s(14) > y + revealH - s(12)) break;
5930
- fillRound(ctx, nx, ny - s(14), pw, s(28), s(SHAPE.full), C.secondaryContainer);
5931
- textCenter(
5932
- ctx,
5933
- fittedWord,
5934
- nx + pw / 2,
5935
- ny,
5936
- chipFont,
5937
- C.onSecondaryContainer,
5938
- cjkOffset(s(TYPE.labelLarge.size))
5939
- );
5940
- nx += pw + s(7);
5769
+ const x = ix + s(L.pad);
5770
+ let y = iy + drawHeader(ctx, ix, iy, iw, "每日挑战 · 已猜中");
5771
+ y += s(L.gap);
5772
+ drawPanel(ctx, x, y, w, revealH);
5773
+ const top = y + s(panelPad);
5774
+ const labelFont = `${s(TYPE.labelLarge.size)}px ${FONT_SANS}`;
5775
+ textLeft(ctx, "今日答案", x + s(panelPad), top + s(labelH) / 2, labelFont, C.onSurfaceVariant);
5776
+ drawWordGrid(ctx, x + s(panelPad), top + s(labelH) + s(12), opts.answer, "lg");
5777
+ const rx = x + rightX;
5778
+ if (chips.length) {
5779
+ textLeft(ctx, "意思最近的词", rx, top + s(labelH) / 2, labelFont, C.onSurfaceVariant);
5780
+ const chipTop = top + s(labelH) + s(12);
5781
+ for (const chip of chips) {
5782
+ const cy = chipTop + chip.row * (s(chipH) + s(chipGap));
5783
+ fillRound(ctx, rx + chip.x, cy, chip.w, s(chipH), s(SHAPE.full), C.secondaryContainer);
5784
+ textCenter(ctx, chip.word, rx + chip.x + chip.w / 2, cy + s(chipH) / 2, `${chipFs}px ${FONT_SANS}`, C.onSecondaryContainer, cjkOffset(chipFs));
5941
5785
  }
5942
5786
  } else {
5943
- textLeft(
5944
- ctx,
5945
- "今日一词,就此收笔。",
5946
- ax,
5947
- y + s(50),
5948
- `${s(TYPE.bodyMedium.size)}px ${FONT_SANS}`,
5949
- C.onSurfaceVariant
5950
- );
5787
+ textLeft(ctx, "今日一词,就此收笔。", rx, top + s(labelH) + s(12) + s(GRID.lg.cell) / 2, `${s(TYPE.bodyLarge.size)}px ${FONT_SANS}`, C.onSurfaceVariant);
5951
5788
  }
5952
- y += revealH + s(14);
5953
- const statW = (iw - s(44) - s(20)) / 3;
5789
+ y += revealH + s(12);
5954
5790
  const stats = [
5955
5791
  { n: String(opts.attempts), u: "次", c: "本局猜测" },
5956
5792
  { n: opts.closest === null ? "—" : `#${opts.closest}`, u: "", c: "此前最接近" },
5957
5793
  { n: String(opts.score), u: "次", c: "累计猜中" }
5958
5794
  ];
5959
- for (let i = 0; i < 3; i++) {
5960
- const sx = ix + s(22) + i * (statW + s(10));
5961
- drawPanel(ctx, sx, y, statW, s(56));
5962
- textLeft(
5963
- ctx,
5964
- stats[i].n,
5965
- sx + s(14),
5966
- y + s(22),
5967
- `${EMPHASIZED_WEIGHT.headline} ${s(TYPE.headlineSmall.size)}px ${FONT_NUM}`,
5968
- C.primary
5969
- );
5970
- if (stats[i].u) {
5971
- textLeft(
5972
- ctx,
5973
- stats[i].u,
5974
- sx + s(14) + textWidth(stats[i].n, s(TYPE.headlineSmall.size)) + s(3),
5975
- y + s(26),
5976
- `${s(TYPE.labelMedium.size)}px ${FONT_SANS}`,
5977
- C.onSurfaceVariant
5978
- );
5979
- }
5980
- textLeft(
5981
- ctx,
5982
- stats[i].c,
5983
- sx + s(14),
5984
- y + s(44),
5985
- `${s(TYPE.labelSmall.size)}px ${FONT_SANS}`,
5986
- C.onSurfaceVariant
5987
- );
5988
- }
5989
- const quip = opts.closest === null ? "一击即中,今日无需第二次落笔" : `从 #${opts.closest} 一步跨到 #1`;
5990
- const footerTip = opts.canStartToday ? "ciyi.猜 山水 · 开启今日新题" : "明日零点换新题 · ciyi.排行榜";
5991
- drawFooter(
5992
- ctx,
5993
- ix,
5994
- iy + ih - s(60),
5995
- iw,
5996
- [],
5997
- footerTip
5998
- );
5999
- const footerFont = `${s(TYPE.labelSmall.size)}px ${FONT_SANS}`;
6000
- const footerTipWidth = textWidth(footerTip, s(TYPE.labelSmall.size));
6001
- textLeftFit(
6002
- ctx,
6003
- `${opts.username} 拿下今日一词 · ${quip}`,
6004
- ix + s(22),
6005
- iy + ih - s(30),
6006
- iw - s(62) - footerTipWidth,
6007
- footerFont,
6008
- C.onSurfaceVariant
6009
- );
5795
+ const statW = (w - s(12) * 2) / 3;
5796
+ stats.forEach((stat, i) => {
5797
+ const sx = x + i * (statW + s(12));
5798
+ drawPanel(ctx, sx, y, statW, s(statH));
5799
+ const numFs = s(TYPE.headlineSmall.size);
5800
+ textLeft(ctx, stat.n, sx + s(16), y + s(28), `${EMPHASIZED_WEIGHT.headline} ${numFs}px ${FONT_NUM}`, C.primary);
5801
+ if (stat.u) textLeft(ctx, stat.u, sx + s(16) + textWidth(stat.n, numFs) + s(4), y + s(31), `${s(TYPE.labelLarge.size)}px ${FONT_SANS}`, C.onSurfaceVariant);
5802
+ textLeft(ctx, stat.c, sx + s(16), y + s(56), `${s(TYPE.labelMedium.size)}px ${FONT_SANS}`, C.onSurfaceVariant);
5803
+ });
5804
+ drawFooter(ctx, ix, iy, iw, ih, footer);
6010
5805
  });
6011
5806
  }
6012
5807
  __name(renderWinCard, "renderWinCard");
6013
5808
  function renderRankCard(service, opts) {
6014
- const innerW = s(640);
6015
- const rankPadX = s(32);
6016
- const rankInnerPad = s(20);
6017
- const headerH = s(87);
6018
- const contentGap = s(20);
6019
- const rowH = s(52);
6020
- const emptyH = s(92);
6021
- const hiddenH = opts.hidden > 0 ? s(36) : 0;
6022
- const footerGap = s(20);
6023
- const footerH = s(60);
6024
- const contentH = opts.entries.length ? opts.entries.length * rowH + hiddenH : emptyH;
6025
- const innerH = headerH + contentGap + contentH + footerGap + footerH;
5809
+ const innerW = s(600);
5810
+ const footer = [{ left: "每猜中一日之词,记一次", right: "发送「ciyi.猜 两字词」参与" }];
5811
+ const rowH = 56;
5812
+ const hiddenH = opts.hidden > 0 ? 40 : 0;
5813
+ const emptyH = 96;
5814
+ const contentH = opts.entries.length ? s(8) * 2 + opts.entries.length * s(rowH) + s(hiddenH) : s(emptyH);
5815
+ const innerH = s(HEADER_H) + s(L.gap) + contentH + s(L.gap) + footerHeight(innerW, footer);
6026
5816
  return toPng(service, innerW, innerH, (ctx, ix, iy, iw, ih) => {
6027
- const panelX = ix + rankPadX;
6028
- const panelW = iw - rankPadX * 2;
6029
- const contentLeft = panelX + rankInnerPad;
6030
- const contentRight = panelX + panelW - rankInnerPad;
6031
- let y = iy + drawHeader(ctx, ix, iy, iw, "每日挑战 · 累计猜中", {
6032
- big: String(opts.players),
6033
- cap: "上榜人数"
6034
- });
6035
- y += s(20);
6036
- drawPanel(ctx, panelX, y, panelW, contentH);
5817
+ const x = ix + s(L.pad);
5818
+ const w = iw - s(L.pad) * 2;
5819
+ let y = iy + drawHeader(ctx, ix, iy, iw, "每日挑战 · 累计猜中", { big: String(opts.players), cap: "上榜人数" });
5820
+ y += s(L.gap);
5821
+ drawPanel(ctx, x, y, w, contentH);
6037
5822
  if (!opts.entries.length) {
6038
- textCenter(
6039
- ctx,
6040
- "排行榜还空着",
6041
- panelX + panelW / 2,
6042
- y + s(31),
6043
- `${s(TYPE.bodyMedium.size)}px ${FONT_SANS}`,
6044
- C.onSurfaceVariant
6045
- );
6046
- textCenter(
6047
- ctx,
6048
- "今日第一个猜中的人,会写在这里。",
6049
- panelX + panelW / 2,
6050
- y + s(61),
6051
- `${s(TYPE.bodyMedium.size)}px ${FONT_SANS}`,
6052
- C.onSurfaceVariant
6053
- );
5823
+ textCenter(ctx, "排行榜还空着", x + w / 2, y + s(34), `${EMPHASIZED_WEIGHT.title} ${s(TYPE.titleMedium.size)}px ${FONT_SANS}`, C.onSurface);
5824
+ textCenter(ctx, "今日第一个猜中的人,会写在这里。", x + w / 2, y + s(62), `${s(TYPE.bodyMedium.size)}px ${FONT_SANS}`, C.onSurfaceVariant);
6054
5825
  } else {
6055
- let ry = y;
6056
- for (let i = 0; i < opts.entries.length; i++) {
6057
- const e = opts.entries[i];
6058
- const midY = ry + rowH / 2;
6059
- if (e.me) {
6060
- fillRound(ctx, panelX, ry, panelW, rowH, s(SHAPE.medium), FRESH);
6061
- }
6062
- if (i > 0) {
6063
- ctx.strokeStyle = C.outlineVariant;
6064
- ctx.beginPath();
6065
- ctx.moveTo(contentLeft, ry);
6066
- ctx.lineTo(contentRight, ry);
6067
- ctx.stroke();
6068
- }
6069
- const posSize = s(30);
6070
- const px = contentLeft;
6071
- const py = midY - posSize / 2;
5826
+ const left = x + s(16);
5827
+ const right = x + w - s(16);
5828
+ let ry = y + s(8);
5829
+ opts.entries.forEach((e, i) => {
5830
+ const midY = ry + s(rowH) / 2;
5831
+ if (e.me) fillRound(ctx, x + s(6), ry + s(3), w - s(12), s(rowH) - s(6), s(SHAPE.medium), FRESH);
5832
+ if (i > 0 && !e.me && !opts.entries[i - 1].me) hline(ctx, left, right, ry, C.outlineVariant);
6072
5833
  const podium = [MEDAL.gold, MEDAL.silver, MEDAL.bronze];
6073
5834
  const pc = i < 3 ? { bg: podium[i], fg: onColor(podium[i]) } : { bg: C.surfaceContainerHighest, fg: C.onSurfaceVariant };
6074
- fillRound(ctx, px, py, posSize, posSize, s(SHAPE.full), pc.bg);
6075
- textCenter(
6076
- ctx,
6077
- String(i + 1),
6078
- px + posSize / 2,
6079
- midY,
6080
- `${EMPHASIZED_WEIGHT.label} ${s(TYPE.labelLarge.size)}px ${FONT_NUM}`,
6081
- pc.fg
6082
- );
6083
- const name2 = e.username || "无名氏";
6084
- const nameX = px + posSize + s(14);
6085
- const nameFont = `${s(TYPE.titleMedium.size)}px ${FONT_SANS}`;
5835
+ const pos = s(32);
5836
+ fillRound(ctx, left, midY - pos / 2, pos, pos, s(SHAPE.full), pc.bg);
5837
+ textCenter(ctx, String(i + 1), left + pos / 2, midY, `${EMPHASIZED_WEIGHT.label} ${s(TYPE.labelLarge.size)}px ${FONT_NUM}`, pc.fg);
6086
5838
  const score = String(e.score);
6087
- const unitFont = s(TYPE.labelSmall.size);
6088
- const unitW = textWidth("次", unitFont);
6089
- const scoreFont = s(TYPE.titleLarge.size);
6090
- const scoreRight = contentRight - unitW - s(8);
6091
- const scoreLeft = scoreRight - textWidth(score, scoreFont);
6092
- const meWidth = e.me ? textWidth("我", s(TYPE.labelSmall.size)) + s(18) : 0;
6093
- const fittedName = ellipsize(
6094
- name2,
6095
- Math.max(0, scoreLeft - s(16) - nameX - meWidth),
6096
- s(TYPE.titleMedium.size)
6097
- );
6098
- textLeft(ctx, fittedName, nameX, midY, nameFont, C.onSurface);
5839
+ const scoreFs = s(TYPE.titleLarge.size);
5840
+ const unitFs = s(TYPE.labelLarge.size);
5841
+ const scoreRight = right - textWidth("次", unitFs) - s(6);
5842
+ const nameX = left + pos + s(14);
5843
+ const meW = e.me ? textWidth("我", s(TYPE.labelMedium.size)) + s(20) : 0;
5844
+ const nameFs = s(TYPE.titleMedium.size);
5845
+ const name2 = ellipsize(e.username || "无名氏", scoreRight - textWidth(score, scoreFs) - s(20) - nameX - meW, nameFs);
5846
+ textLeft(ctx, name2, nameX, midY, `${nameFs}px ${FONT_SANS}`, C.onSurface);
6099
5847
  if (e.me) {
6100
- const nw = textWidth(fittedName, s(TYPE.titleMedium.size));
6101
- textLeft(
6102
- ctx,
6103
- "我",
6104
- nameX + nw + s(8),
6105
- midY,
6106
- `${s(TYPE.labelSmall.size)}px ${FONT_SANS}`,
6107
- C.primary
6108
- );
5848
+ const bx = nameX + textWidth(name2, nameFs) + s(8);
5849
+ fillRound(ctx, bx, midY - s(10), meW - s(8), s(20), s(SHAPE.full), C.primary);
5850
+ textCenter(ctx, "我", bx + (meW - s(8)) / 2, midY, `${EMPHASIZED_WEIGHT.label} ${s(TYPE.labelMedium.size)}px ${FONT_SANS}`, C.onPrimary);
6109
5851
  }
6110
- textRight(
6111
- ctx,
6112
- score,
6113
- scoreRight,
6114
- midY,
6115
- `${EMPHASIZED_WEIGHT.title} ${scoreFont}px ${FONT_NUM}`,
6116
- C.onSurface
6117
- );
6118
- textRight(ctx, "次", contentRight, midY, `${unitFont}px ${FONT_SANS}`, C.onSurfaceVariant);
6119
- ry += rowH;
6120
- }
5852
+ textRight(ctx, score, scoreRight, midY, `${EMPHASIZED_WEIGHT.title} ${scoreFs}px ${FONT_NUM}`, C.onSurface);
5853
+ textRight(ctx, "次", right, midY, `${unitFs}px ${FONT_SANS}`, C.onSurfaceVariant);
5854
+ ry += s(rowH);
5855
+ });
6121
5856
  if (opts.hidden > 0) {
6122
- textLeft(
6123
- ctx,
6124
- `…… 另有 ${opts.hidden} 人在榜`,
6125
- contentLeft,
6126
- ry + s(16),
6127
- `${s(TYPE.labelSmall.size)}px ${FONT_SANS}`,
6128
- C.onSurfaceVariant
6129
- );
5857
+ textCenter(ctx, `另有 ${opts.hidden} 人在榜`, x + w / 2, ry + s(hiddenH) / 2, `${s(TYPE.labelMedium.size)}px ${FONT_SANS}`, C.onSurfaceVariant);
6130
5858
  }
6131
5859
  }
6132
- drawFooter(
6133
- ctx,
6134
- ix,
6135
- iy + ih - s(60),
6136
- iw,
6137
- [],
6138
- "ciyi.猜 山水"
6139
- );
6140
- textLeft(
6141
- ctx,
6142
- "每猜中一日之词,记一次",
6143
- panelX,
6144
- iy + ih - s(30),
6145
- `${s(TYPE.labelSmall.size)}px ${FONT_SANS}`,
6146
- C.onSurfaceVariant
6147
- );
5860
+ drawFooter(ctx, ix, iy, iw, ih, footer);
6148
5861
  });
6149
5862
  }
6150
5863
  __name(renderRankCard, "renderRankCard");
@@ -6217,7 +5930,6 @@ function resolveMiddlewareSwitch(config, override, action) {
6217
5930
  }
6218
5931
  __name(resolveMiddlewareSwitch, "resolveMiddlewareSwitch");
6219
5932
  function apply(ctx, cfg) {
6220
- const presentation = usePresentation(ctx, "ciyi");
6221
5933
  ctx.model.extend(
6222
5934
  "ciyi",
6223
5935
  {
@@ -6490,7 +6202,7 @@ function apply(ctx, cfg) {
6490
6202
  async function send(session, content) {
6491
6203
  const ids = await session.send(content);
6492
6204
  const messageId = ids?.[0];
6493
- if (presentation.textOnly(session) || !cfg.retractDelay || !messageId) return;
6205
+ if (!cfg.retractDelay || !messageId) return;
6494
6206
  const previous = lastMessage.get(session.channelId);
6495
6207
  if (previous) {
6496
6208
  const passed = Date.now() - previous.timestamp;
@@ -6504,12 +6216,12 @@ function apply(ctx, cfg) {
6504
6216
  }
6505
6217
  __name(send, "send");
6506
6218
  async function sendCard(session, render, fallback) {
6507
- const image = presentation.textOnly(session) ? null : await renderCard(render);
6219
+ const image = await renderCard(render);
6508
6220
  const prefix = [];
6509
6221
  if (cfg.quoteReply && session.messageId) prefix.push(import_koishi2.h.quote(session.messageId));
6510
6222
  if (cfg.atReply) prefix.push(import_koishi2.h.at(session.userId), (0, import_koishi2.h)("p"));
6511
6223
  if (image) {
6512
- await send(session, [...prefix, ...import_koishi2.h.normalize(presentation.present(session, image, import_koishi2.h.text(fallback)))]);
6224
+ await send(session, [...prefix, ...import_koishi2.h.normalize(present(image, import_koishi2.h.text(fallback)))]);
6513
6225
  return;
6514
6226
  }
6515
6227
  await send(session, [...prefix, import_koishi2.h.text(fallback)]);
package/lib/ux.d.ts CHANGED
@@ -3,29 +3,11 @@ type Owner = {
3
3
  name: string;
4
4
  matches: (session: Session) => boolean | Promise<boolean>;
5
5
  };
6
- /** 图文模式默认只发渲染图;完整文字只在用户切到「文字」模式时出现。偏好只影响本人。 */
7
- export declare function usePresentation(ctx: Context, command: string): {
8
- textOnly: (session: Session) => boolean;
9
- /** 图文模式只给图片;图片缺失或用户选了文字模式时才退回文字。 */
10
- present(session: Session, image: h.Fragment | null | undefined, text: h.Fragment): h.Fragment;
11
- };
6
+ /** 渲染图优先,只发图片;图片缺失时才退回等价文字。 */
7
+ export declare function present(image: h.Fragment | null | undefined, text: h.Fragment): h.Fragment;
12
8
  /** Plugins register their active input grammar; overlapping messages are never executed. */
13
9
  export declare function registerDirectInput(ctx: Context, name: string, matches: Owner['matches']): void;
14
10
  export declare function directInputConflict(ctx: Context, session: Session): Promise<boolean>;
15
- /** No hidden time limit: users can extend the non-game input step without losing context. */
16
- export declare function promptInput(session: Session, instruction: string): Promise<string | undefined>;
17
- export declare const IMAGE_FAILURE = "\u56FE\u7247\u6682\u65F6\u65E0\u6CD5\u751F\u6210\uFF0C\u5DF2\u4FDD\u7559\u6587\u5B57\u5185\u5BB9\u3002\u53EF\u4EE5\u7A0D\u540E\u91CD\u8BD5\uFF1B\u82E5\u6301\u7EED\u5931\u8D25\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458\u3002";
18
- /**
19
- * 渲染图的等价文字。图文模式随图片一起丢掉、文字模式展开成正文,
20
- * 因此它永远不会原样发到聊天平台。
21
- */
22
- export declare const IMAGE_TEXT_TYPE = "ux-image-text";
23
- export declare function imageText(text: h.Fragment): h;
24
- /** 图文模式:保留图片与普通文字,去掉图片的等价文字。 */
25
- export declare function imagesOnly(content: h.Fragment): h[];
26
- /** 文字模式:去掉图片,把等价文字展开成普通文本。 */
27
- export declare function withoutImages(content: h.Fragment): h[];
28
- /** 发送前的统一选择:文字模式去掉图片,图文模式去掉等价文字。 */
29
- export declare function choosePresentation(content: h.Fragment, textOnly: boolean): h[];
30
- export declare function isTextOnly(ctx: Context, session: Session): boolean;
11
+ /** 等待下一句回复。提问本身已经写明了可回复的内容与「取消」,这里不再追加说明。 */
12
+ export declare function promptInput(session: Session): Promise<string | undefined>;
31
13
  export {};
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "koishi-plugin-ciyi",
3
3
  "description": "Koishi 群聊猜词:每天一个两字词,支持续猜和排行榜。",
4
- "version": "2.2.1",
4
+ "version": "2.3.0",
5
5
  "main": "lib/index.js",
6
6
  "typings": "lib/index.d.ts",
7
7
  "files": [