koishi-plugin-dew-bot 1.0.242 → 1.0.243

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.js CHANGED
@@ -77,16 +77,16 @@ var AnalysisMsg = class {
77
77
 
78
78
  // src/plugin/appearanceCardTemplate.ts
79
79
  var escape = /* @__PURE__ */ __name((value) => String(value ?? "").replace(/[&<>"']/g, (c) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;" })[c]), "escape");
80
- function appearanceCardHtml(card, artwork, frame2) {
81
- const slice = /* @__PURE__ */ __name((y, height) => `<svg class="frame" viewBox="0 ${y} 760 ${height}" preserveAspectRatio="none" aria-hidden="true"><image href="${escape(frame2)}" width="760" height="1107" preserveAspectRatio="none"/></svg>`, "slice");
80
+ function appearanceCardHtml(card, artwork, paper) {
82
81
  return `<!doctype html><html lang="zh-CN"><head><meta charset="utf-8"><style>
83
- *{box-sizing:border-box}html,body{margin:0;width:760px;font-family:'Microsoft YaHei','Noto Sans CJK SC',sans-serif;color:#f5edd6;background:#141c26}
84
- #capture{width:760px}.section{position:relative}.frame{position:absolute;inset:0;width:100%;height:100%;pointer-events:none}.content{position:relative}
85
- .heading{height:205px}.title{position:absolute;left:58px;top:46px;width:644px;height:74px;margin:0;display:flex;align-items:center;font-size:30px;font-weight:800;line-height:1.15;text-shadow:0 2px 3px #152a24;overflow-wrap:anywhere;overflow:hidden}
86
- .meta{position:absolute;left:92px;right:91px;top:151px;display:flex;justify-content:space-between;font-size:21px}
87
- .portrait{height:957px}.art{position:relative;margin-left:93px;width:574px;height:957px;border:3px solid #bea874;background:#e9e5d9;overflow:hidden}.item-artwork{display:block;width:100%;height:100%;object-fit:contain}
88
- .footer{min-height:328px;padding:58px 62px 73px;color:#202c24}.description{margin:0;font-size:23px;line-height:1.6;white-space:pre-wrap;overflow-wrap:anywhere}.details{display:flex;justify-content:space-between;gap:24px;border-top:1px solid #52665366;margin-top:16px;padding-top:12px;font-size:18px;line-height:1.5;overflow-wrap:anywhere}.serial{position:absolute;top:9px;right:94px;font-size:14px;color:#23382e;letter-spacing:2px}
89
- </style></head><body><article id="capture"><header class="section heading">${slice(0, 205)}<h1 class="title">${escape(card.name)}</h1><div class="meta"><span>【形象卡】</span><span>使用后更换外观</span></div></header><div class="section portrait">${slice(205, 574)}<div class="art"><img class="item-artwork" src="${escape(artwork)}" alt="角色形象"></div></div><footer class="section footer">${slice(779, 328)}<div class="serial">DEW · APPEARANCE</div><div class="content"><p class="description">${escape(card.description)}</p><div class="details"><span>数量:${escape(card.count)}</span><span>参考价格:${escape(card.referencePrice)}</span></div></div></footer></article></body></html>`;
82
+ *{box-sizing:border-box}html,body{margin:0;width:760px;background:#ece8df;color:#625a49;font-family:'Microsoft YaHei','Noto Sans CJK SC',sans-serif}
83
+ #capture{width:760px;padding:25px 18px 29px;background:radial-gradient(ellipse at center,#f6f2e9,#e4ded2)}
84
+ .scroll{position:relative;padding:18px 0}.sheet{position:relative;margin:0 29px;padding:0 23px;background:#eee5cf;border-left:1px solid #b8a37b;border-right:1px solid #b8a37b;box-shadow:0 7px 16px #49381c24;overflow:hidden}
85
+ .paper{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;opacity:.22;pointer-events:none}.sheet:after{content:'';position:absolute;inset:0;pointer-events:none;box-shadow:inset 0 18px 20px -18px #49351cb0,inset 0 -18px 20px -18px #49351c90}
86
+ .title{position:relative;margin:0;padding:29px 0 23px;text-align:center;font-family:'KaiTi','STKaiti','SimSun',serif;font-size:34px;font-weight:400;line-height:1.4;letter-spacing:8px;color:#514630}.portrait{position:relative;padding:5px;border:1px solid #ae9360;background:#e8dec5}.item-artwork{display:block;width:100%;height:auto;aspect-ratio:3/5;object-fit:contain}
87
+ .details{position:relative;display:flex;justify-content:flex-end;gap:24px;padding:24px 8px 29px;font-size:17px;line-height:1.6;letter-spacing:1px}.details span{min-width:0;overflow-wrap:anywhere}.details span:last-child{text-align:right}.details b{font-weight:400;color:#403c31;margin-left:10px}
88
+ .roller{position:absolute;left:12px;right:12px;height:30px;z-index:2;border:1px solid #665033;border-radius:5px;background:linear-gradient(180deg,#695136 0%,#aa8b5f 16%,#dfc89a 38%,#b99a67 56%,#80613c 84%,#503a26 100%);box-shadow:0 4px 6px #44311933}.roller.top{top:0}.roller.bottom{bottom:0;height:34px}.roller:before,.roller:after{content:'';position:absolute;top:-4px;width:22px;height:calc(100% + 8px);border:1px solid #443321;border-radius:5px;background:linear-gradient(180deg,#493727,#987b50 28%,#bda071 42%,#745533 65%,#3d2d20);box-shadow:inset 3px 0 2px #d2b88166,inset -3px 0 2px #2c201c66}.roller:before{left:-14px}.roller:after{right:-14px}
89
+ </style></head><body><article id="capture"><div class="scroll"><div class="roller top" aria-hidden="true"></div><div class="sheet">${paper ? `<img class="paper" src="${escape(paper)}" alt="" aria-hidden="true">` : ""}<h1 class="title">形象卡</h1><div class="portrait"><img class="item-artwork" src="${escape(artwork)}" alt="${escape(card.name)}"></div><div class="details"><span>参考价格<b>${escape(card.referencePrice)}</b></span></div></div><div class="roller bottom" aria-hidden="true"></div></div></article></body></html>`;
90
90
  }
91
91
  __name(appearanceCardHtml, "appearanceCardHtml");
92
92
 
@@ -225,6 +225,7 @@ async function layoutCard(document = globalThis.document) {
225
225
  if (avatar.scrollWidth <= avatar.clientWidth) break;
226
226
  }
227
227
  }
228
+ if (document.getElementById("capture")?.classList.contains("skill-sheet")) return;
228
229
  const name2 = document.getElementById("name");
229
230
  if (!name2) return;
230
231
  for (let size = 36; size >= 20; size--) {
@@ -276,15 +277,21 @@ var import_node_path = __toESM(require("node:path"));
276
277
 
277
278
  // src/plugin/skillCardTemplate.ts
278
279
  var escape5 = /* @__PURE__ */ __name((value) => String(value ?? "").replace(/[&<>"']/g, (c) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;" })[c]), "escape");
279
- var colors = { F: "#b6c3c9", E: "#82cf88", D: "#5fbeed", C: "#9b8cfa", B: "#d97eee", A: "#f9b25f", S: "#f2dc88", SS: "#ff9e78", SSS: "#fff0b1" };
280
- function skillCardHtml(card, frame2, artwork) {
281
- const quality = card.quality.replace(/[+-]$/, ""), color = colors[quality] ?? colors.F;
282
- const desc = card.description || "暂无描述";
280
+ function skillCardHtml(card, _frame, artwork) {
281
+ const description = escape5((card.description || "暂无技能效果说明").replace(/\s*\n+\s*/g, ""));
282
+ const grain = "data:image/svg+xml," + encodeURIComponent('<svg xmlns="http://www.w3.org/2000/svg" width="180" height="180"><filter id="paper"><feTurbulence type="fractalNoise" baseFrequency=".72" numOctaves="3" stitchTiles="stitch"/></filter><rect width="100%" height="100%" opacity=".15" filter="url(#paper)"/></svg>');
283
283
  return `<!doctype html><html><head><meta charset="utf-8"><style>
284
- *{box-sizing:border-box}html,body{margin:0;width:760px;background:#141c26;color:#15252b;font-family:'Microsoft YaHei','Noto Sans CJK SC',sans-serif}body{padding:0}#capture{width:760px}#card{width:760px;height:1107px;position:relative;background-image:url('${frame2}');background-size:100% 100%}.name{position:absolute;left:58px;top:46px;width:644px;height:74px;display:flex;align-items:center;font-size:36px;font-weight:800;color:#f5f7e9;text-shadow:1px 2px 2px #163838;overflow:hidden}.badge{position:absolute;right:57px;top:54px;width:68px;height:62px;border:2px solid ${color};background:#163338;border-radius:50%;color:${color};display:grid;place-items:center;font:bold 25px Georgia}.meta{position:absolute;left:92px;right:91px;top:151px;display:flex;justify-content:space-between;color:#f9f1cf;font-size:21px;font-weight:700}.art{position:absolute;left:93px;top:205px;width:574px;height:574px;overflow:hidden;background:radial-gradient(ellipse at 50% 43%,#284951 0,#12222f 42%,#070d19 90%);border:3px solid #bea874}.skill-artwork{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;z-index:1}.art svg{position:absolute;width:100%;height:100%;color:${color}}.glyph{position:absolute;top:157px;width:100%;text-align:center;font-size:147px;filter:drop-shadow(0 0 30px ${color})}.art-title{position:absolute;bottom:113px;left:25px;right:25px;text-align:center;font-size:18px;letter-spacing:6px;color:${color}}.number{position:absolute;top:786px;right:94px;color:#e2eade;font:14px Georgia;letter-spacing:2px}.effect{position:absolute;top:837px;left:62px;width:636px;height:197px;overflow:hidden;font-size:23px;line-height:1.48}.effect b{font-size:23px}.effect p{margin:8px 0;white-space:pre-wrap;overflow-wrap:anywhere}
285
- </style></head><body><div id="capture"><div id="card"><div class="name" id="name">${escape5(card.quality)}级 ${escape5(card.name)}</div><div class="meta"><span>【${card.passive ? "被动" : "主动"}技能】</span><span>Lv.${card.level} / ${card.maxLevel}</span></div>
286
- <div class="art"><svg viewBox="0 0 574 574" xmlns="http://www.w3.org/2000/svg"><defs><radialGradient id="glow"><stop stop-color="${color}" stop-opacity=".25"/><stop offset="1" stop-color="${color}" stop-opacity="0"/></radialGradient></defs><circle cx="287" cy="244" r="220" fill="url(#glow)"/><g stroke="currentColor" fill="none"><circle cx="287" cy="244" r="191" opacity=".35"/><circle cx="287" cy="244" r="179" stroke-dasharray="2 13" opacity=".8"/><circle cx="287" cy="244" r="158" opacity=".6"/><path d="M287 65 442 334 132 334Z M287 423 132 155 442 155Z" opacity=".3"/><path d="M60 244H130M444 244H514M287 18V85M287 403V470" stroke-width="2" opacity=".6"/><path d="M80 440 491 72M102 457 505 95" opacity=".13" stroke-width="16"/></g></svg><div class="art-title">${escape5(card.passive ? "被 动 铭 刻" : "技 能 术 式")}</div>${artwork ? '<img class="skill-artwork" alt="" src="' + escape5(artwork) + '">' : ""}</div>
287
- <div class="number">DEW · SKILL / ${escape5(card.quality)}</div><div class="effect" id="effect"><p>${escape5(desc)}</p></div></div></div></body></html>`;
284
+ *{box-sizing:border-box}
285
+ html,body{margin:0;width:760px;background:#f1eee5;font-family:'Microsoft YaHei','Noto Sans CJK SC',sans-serif;color:#34372f}
286
+ #capture{position:relative;isolation:isolate;width:760px;min-height:1060px;padding:57px 58px 38px;background:radial-gradient(ellipse at 0 0,#ddd9c744,transparent 48%),radial-gradient(ellipse at 100% 90%,#c9cdb938,transparent 45%),#f4f1e8;border:1px solid #dedbcf}
287
+ #capture:before{content:'';position:absolute;inset:0;z-index:-1;background-image:url('${grain}');opacity:.42;pointer-events:none}
288
+ #capture:after{content:'';position:absolute;inset:17px;border:1px solid #8c8b6f22;pointer-events:none}
289
+ header{position:relative}.heading{display:flex;align-items:center;gap:24px}.heading-copy{flex:1;min-width:0}.overline{font-size:14px;letter-spacing:6px;color:#868a76;margin-bottom:13px}.name{font-family:'KaiTi','STKaiti','Noto Serif CJK SC','SimSun',serif;font-size:43px;font-weight:700;line-height:1.35;margin:0;color:#303a31;overflow-wrap:anywhere}
290
+ .seal{flex-shrink:0;width:66px;min-height:74px;transform:rotate(3deg);padding:8px 3px;color:#a04b40;border:2px solid #a04b40;outline:1px solid #a04b40;outline-offset:-6px;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:3px;border-radius:3px 1px 4px 2px}.seal strong{font:700 24px Georgia;letter-spacing:-1px}.seal span{font-family:'KaiTi','SimSun',serif;font-size:16px;letter-spacing:4px;padding-left:4px}
291
+ .meta{display:flex;flex-wrap:wrap;align-items:baseline;gap:8px 22px;margin:24px 0 0;padding:15px 0;border-top:1px solid #8a8a7144;border-bottom:1px solid #8a8a7128;font-size:19px;color:#535d50;line-height:1.5}.meta span{overflow-wrap:anywhere;max-width:100%}.meta small{font-size:16px;color:#92917f;margin-right:7px}.meta .kind{color:#915344;font-family:'KaiTi','SimSun',serif;font-size:21px}
292
+ .art{position:relative;width:calc(100% + 44px);height:570px;margin:14px -22px -8px;display:grid;place-items:center;isolation:isolate}.art:before{content:'';position:absolute;inset:15% 10%;border-radius:48% 52% 56% 44%;background:radial-gradient(ellipse,#aab4a443,transparent 69%);transform:rotate(-12deg)}.skill-artwork{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;object-position:center 43%;filter:saturate(.68) contrast(.94);mix-blend-mode:multiply;mask-image:linear-gradient(to bottom,transparent 0,#000 9%,#000 72%,transparent 100%),linear-gradient(to right,transparent 0,#000 8%,#000 92%,transparent 100%);mask-composite:intersect}.art-fallback{position:relative;z-index:-1;width:300px;height:300px;border:2px solid #7c887d33;border-radius:48% 52% 50% 46%;transform:rotate(-12deg);display:grid;place-items:center;color:#8c948177;font:70px 'KaiTi','SimSun',serif}.art-fallback span{transform:rotate(12deg)}.art:has(.skill-artwork) .art-fallback{visibility:hidden}
293
+ .effect{position:relative;margin-top:0}h2{display:flex;align-items:center;gap:15px;margin:0 0 17px;font:22px 'KaiTi','SimSun',serif;letter-spacing:5px;color:#626e5e}h2:before{content:'';width:5px;height:17px;background:#a04b40;transform:rotate(8deg);opacity:.75}h2:after{content:'';flex:1;height:1px;background:linear-gradient(to right,#7f897c55,transparent)}p{margin:0;font-size:25px;line-height:1.85;white-space:pre-wrap;overflow-wrap:anywhere;text-align:justify}.footer{margin-top:24px;padding-top:17px;border-top:1px solid #8a8a7128}.footer p{font-size:18px;color:#7d8070;line-height:1.75;text-align:left}.edition{display:flex;align-items:center;gap:12px;justify-content:center;margin-top:36px;color:#a6a38e;font:14px 'KaiTi','SimSun',serif;letter-spacing:5px}.edition:before,.edition:after{content:'';height:1px;width:32px;background:#9b9c833d}
294
+ </style></head><body><div id="capture" class="skill-sheet ink-skill-sheet"><header><div class="heading"><div class="heading-copy"><div class="overline">技 艺 图 录</div><h1 class="name">${escape5(card.name)}</h1></div><div class="seal"><strong>${escape5(card.quality)}</strong><span>品阶</span></div></div><div class="meta"><span class="kind">${card.passive ? "被动" : "主动"}</span><span><small>等级</small>${escape5(card.level)} / ${escape5(card.maxLevel)}</span><span><small>消耗</small>${escape5(card.cost || "无消耗")}</span><span><small>冷却</small>${escape5(card.cooldown || "无冷却")}</span></div></header><div class="art"><div class="art-fallback"><span>${card.passive ? "静" : "势"}</span></div>${artwork ? `<img class="skill-artwork" alt="" src="${escape5(artwork)}">` : ""}</div><section class="effect"><h2>技能效果</h2><p>${description}</p></section>${card.sources?.length || card.note ? `<div class="footer">${card.sources?.length ? `<p>获取来源:${escape5(card.sources.join("、"))}</p>` : ""}${card.note ? `<p>${escape5(card.note)}</p>` : ""}</div>` : ""}<div class="edition">露 · 技艺录</div></div></body></html>`;
288
295
  }
289
296
  __name(skillCardHtml, "skillCardHtml");
290
297
 
@@ -386,7 +393,7 @@ async function renderSkillCard(ctx, card, leaderboard, itemCard, characterCard)
386
393
  const work = /* @__PURE__ */ __name(async () => {
387
394
  const art = await loadSkillArtwork(characterCard?.artworkUrl ?? itemCard?.artworkUrl ?? leaderboard?.backgroundUrl ?? card?.artworkUrl);
388
395
  if (itemCard && !art) throw new Error("道具图片读取失败");
389
- const html = characterCard ? characterCardHtml(characterCard, art, await characterAsset("paper.webp"), await characterAsset("default-female.webp")) : itemCard ? itemCard.type === "形象卡" ? appearanceCardHtml(itemCard, art, await frame(false)) : itemCardHtml(itemCard, art, await itemPaper()) : leaderboard ? leaderboardHtml(leaderboard, await leaderboardHeader(), art) : skillCardHtml(card, await frame(card.passive), art);
396
+ const html = characterCard ? characterCardHtml(characterCard, art, await characterAsset("paper.webp"), await characterAsset("default-female.webp")) : itemCard ? itemCard.type === "形象卡" ? appearanceCardHtml(itemCard, art, await characterAsset("paper.webp")) : itemCardHtml(itemCard, art, await itemPaper()) : leaderboard ? leaderboardHtml(leaderboard, await leaderboardHeader(), art) : skillCardHtml(card, await frame(card.passive), art);
390
397
  page = await ctx.puppeteer.page();
391
398
  if (expired) {
392
399
  await page.close();
@@ -497,7 +504,6 @@ var ImageHandler = class extends handel_base {
497
504
  content = new AnalysisMsg().parse(data);
498
505
  }
499
506
  await this.send(data, content, rendered ? "image" : "text");
500
- if (rendered && data.itemCard) await this.send(data, new AnalysisMsg().parse(data));
501
507
  }
502
508
  };
503
509
 
@@ -1,3 +1,3 @@
1
1
  import type { ItemCardData } from '../shared/face';
2
- /** Reuse the skill frame, extending only its illustration and description sections. */
3
- export declare function appearanceCardHtml(card: ItemCardData, artwork: string, frame: string): string;
2
+ /** Hanging scroll shared by bot screenshots and editor previews. */
3
+ export declare function appearanceCardHtml(card: ItemCardData, artwork: string, paper?: string): string;
@@ -1,2 +1,3 @@
1
1
  import type { SkillCardData } from '../shared/face';
2
- export declare function skillCardHtml(card: SkillCardData, frame: string, artwork?: string): string;
2
+ /** One continuous rice-paper sheet; the old frame parameter remains API-compatible. */
3
+ export declare function skillCardHtml(card: SkillCardData, _frame: string, artwork?: string): string;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "koishi-plugin-dew-bot",
3
3
  "description": "连游戏服,把聊天消息交给服务器处理",
4
- "version": "1.0.242",
4
+ "version": "1.0.243",
5
5
  "main": "lib/index.js",
6
6
  "typings": "lib/index.d.ts",
7
7
  "files": [
@@ -2,15 +2,15 @@ import type { ItemCardData } from '../shared/face';
2
2
 
3
3
  const escape = (value: unknown) => String(value ?? '').replace(/[&<>"']/g, c => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' }[c]!));
4
4
 
5
- /** Reuse the skill frame, extending only its illustration and description sections. */
6
- export function appearanceCardHtml(card: ItemCardData, artwork: string, frame: string): string {
7
- const slice = (y: number, height: number) => `<svg class="frame" viewBox="0 ${y} 760 ${height}" preserveAspectRatio="none" aria-hidden="true"><image href="${escape(frame)}" width="760" height="1107" preserveAspectRatio="none"/></svg>`;
5
+ /** Hanging scroll shared by bot screenshots and editor previews. */
6
+ export function appearanceCardHtml(card: ItemCardData, artwork: string, paper?: string): string {
8
7
  return `<!doctype html><html lang="zh-CN"><head><meta charset="utf-8"><style>
9
- *{box-sizing:border-box}html,body{margin:0;width:760px;font-family:'Microsoft YaHei','Noto Sans CJK SC',sans-serif;color:#f5edd6;background:#141c26}
10
- #capture{width:760px}.section{position:relative}.frame{position:absolute;inset:0;width:100%;height:100%;pointer-events:none}.content{position:relative}
11
- .heading{height:205px}.title{position:absolute;left:58px;top:46px;width:644px;height:74px;margin:0;display:flex;align-items:center;font-size:30px;font-weight:800;line-height:1.15;text-shadow:0 2px 3px #152a24;overflow-wrap:anywhere;overflow:hidden}
12
- .meta{position:absolute;left:92px;right:91px;top:151px;display:flex;justify-content:space-between;font-size:21px}
13
- .portrait{height:957px}.art{position:relative;margin-left:93px;width:574px;height:957px;border:3px solid #bea874;background:#e9e5d9;overflow:hidden}.item-artwork{display:block;width:100%;height:100%;object-fit:contain}
14
- .footer{min-height:328px;padding:58px 62px 73px;color:#202c24}.description{margin:0;font-size:23px;line-height:1.6;white-space:pre-wrap;overflow-wrap:anywhere}.details{display:flex;justify-content:space-between;gap:24px;border-top:1px solid #52665366;margin-top:16px;padding-top:12px;font-size:18px;line-height:1.5;overflow-wrap:anywhere}.serial{position:absolute;top:9px;right:94px;font-size:14px;color:#23382e;letter-spacing:2px}
15
- </style></head><body><article id="capture"><header class="section heading">${slice(0, 205)}<h1 class="title">${escape(card.name)}</h1><div class="meta"><span>【形象卡】</span><span>使用后更换外观</span></div></header><div class="section portrait">${slice(205, 574)}<div class="art"><img class="item-artwork" src="${escape(artwork)}" alt="角色形象"></div></div><footer class="section footer">${slice(779, 328)}<div class="serial">DEW · APPEARANCE</div><div class="content"><p class="description">${escape(card.description)}</p><div class="details"><span>数量:${escape(card.count)}</span><span>参考价格:${escape(card.referencePrice)}</span></div></div></footer></article></body></html>`;
8
+ *{box-sizing:border-box}html,body{margin:0;width:760px;background:#ece8df;color:#625a49;font-family:'Microsoft YaHei','Noto Sans CJK SC',sans-serif}
9
+ #capture{width:760px;padding:25px 18px 29px;background:radial-gradient(ellipse at center,#f6f2e9,#e4ded2)}
10
+ .scroll{position:relative;padding:18px 0}.sheet{position:relative;margin:0 29px;padding:0 23px;background:#eee5cf;border-left:1px solid #b8a37b;border-right:1px solid #b8a37b;box-shadow:0 7px 16px #49381c24;overflow:hidden}
11
+ .paper{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;opacity:.22;pointer-events:none}.sheet:after{content:'';position:absolute;inset:0;pointer-events:none;box-shadow:inset 0 18px 20px -18px #49351cb0,inset 0 -18px 20px -18px #49351c90}
12
+ .title{position:relative;margin:0;padding:29px 0 23px;text-align:center;font-family:'KaiTi','STKaiti','SimSun',serif;font-size:34px;font-weight:400;line-height:1.4;letter-spacing:8px;color:#514630}.portrait{position:relative;padding:5px;border:1px solid #ae9360;background:#e8dec5}.item-artwork{display:block;width:100%;height:auto;aspect-ratio:3/5;object-fit:contain}
13
+ .details{position:relative;display:flex;justify-content:flex-end;gap:24px;padding:24px 8px 29px;font-size:17px;line-height:1.6;letter-spacing:1px}.details span{min-width:0;overflow-wrap:anywhere}.details span:last-child{text-align:right}.details b{font-weight:400;color:#403c31;margin-left:10px}
14
+ .roller{position:absolute;left:12px;right:12px;height:30px;z-index:2;border:1px solid #665033;border-radius:5px;background:linear-gradient(180deg,#695136 0%,#aa8b5f 16%,#dfc89a 38%,#b99a67 56%,#80613c 84%,#503a26 100%);box-shadow:0 4px 6px #44311933}.roller.top{top:0}.roller.bottom{bottom:0;height:34px}.roller:before,.roller:after{content:'';position:absolute;top:-4px;width:22px;height:calc(100% + 8px);border:1px solid #443321;border-radius:5px;background:linear-gradient(180deg,#493727,#987b50 28%,#bda071 42%,#745533 65%,#3d2d20);box-shadow:inset 3px 0 2px #d2b88166,inset -3px 0 2px #2c201c66}.roller:before{left:-14px}.roller:after{right:-14px}
15
+ </style></head><body><article id="capture"><div class="scroll"><div class="roller top" aria-hidden="true"></div><div class="sheet">${paper ? `<img class="paper" src="${escape(paper)}" alt="" aria-hidden="true">` : ''}<h1 class="title">形象卡</h1><div class="portrait"><img class="item-artwork" src="${escape(artwork)}" alt="${escape(card.name)}"></div><div class="details"><span>参考价格<b>${escape(card.referencePrice)}</b></span></div></div><div class="roller bottom" aria-hidden="true"></div></div></article></body></html>`;
16
16
  }
@@ -31,6 +31,8 @@ export async function layoutCard(document: Document = globalThis.document) {
31
31
  for (const avatar of document.querySelectorAll<HTMLElement>('.avatar')) {
32
32
  for (let size = 30; size >= 16; size--) { avatar.style.fontSize = size + 'px'; if (avatar.scrollWidth <= avatar.clientWidth) break }
33
33
  }
34
+ // Skill details use normal document flow and a fixed readable font size.
35
+ if (document.getElementById('capture')?.classList.contains('skill-sheet')) return;
34
36
  const name = document.getElementById('name');
35
37
  if (!name) return;
36
38
  for (let size=36; size>=20; size--) { name.style.fontSize=size+'px'; if(name.scrollWidth<=name.clientWidth && name.scrollHeight<=name.clientHeight) break }
@@ -16,6 +16,5 @@ export default class ImageHandler extends handel_base {
16
16
  content = new AnalysisMsg().parse(data)
17
17
  }
18
18
  await this.send(data, content, rendered ? 'image' : 'text')
19
- if (rendered && data.itemCard) await this.send(data, new AnalysisMsg().parse(data))
20
19
  }
21
20
  }
@@ -54,7 +54,7 @@ export async function renderSkillCard(ctx: Context, card?: SkillCardData, leader
54
54
  const work = async () => {
55
55
  const art = await loadSkillArtwork(characterCard?.artworkUrl ?? itemCard?.artworkUrl ?? leaderboard?.backgroundUrl ?? card?.artworkUrl)
56
56
  if (itemCard && !art) throw new Error('道具图片读取失败')
57
- const html = characterCard ? characterCardHtml(characterCard, art, await characterAsset('paper.webp'), await characterAsset('default-female.webp')) : itemCard ? (itemCard.type === '形象卡' ? appearanceCardHtml(itemCard, art!, await frame(false)) : itemCardHtml(itemCard, art!, await itemPaper())) : leaderboard ? leaderboardHtml(leaderboard, await leaderboardHeader(), art) : skillCardHtml(card!, await frame(card!.passive), art)
57
+ const html = characterCard ? characterCardHtml(characterCard, art, await characterAsset('paper.webp'), await characterAsset('default-female.webp')) : itemCard ? (itemCard.type === '形象卡' ? appearanceCardHtml(itemCard, art!, await characterAsset('paper.webp')) : itemCardHtml(itemCard, art!, await itemPaper())) : leaderboard ? leaderboardHtml(leaderboard, await leaderboardHeader(), art) : skillCardHtml(card!, await frame(card!.passive), art)
58
58
  page = await ctx.puppeteer.page()
59
59
  if (expired) { await page.close(); throw new Error('绘图超时') }
60
60
  await page.setViewport({ width:760,height:1107,deviceScaleFactor:1 })
@@ -1,12 +1,21 @@
1
- import type { SkillCardData } from '../shared/face'
2
- const escape = (value: unknown) => String(value ?? '').replace(/[&<>"']/g, c => ({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[c]!))
3
- const colors: Record<string,string> = {F:'#b6c3c9',E:'#82cf88',D:'#5fbeed',C:'#9b8cfa',B:'#d97eee',A:'#f9b25f',S:'#f2dc88',SS:'#ff9e78',SSS:'#fff0b1'}
4
- export function skillCardHtml(card: SkillCardData, frame: string, artwork?: string): string {
5
- const quality = card.quality.replace(/[+-]$/, ''), color = colors[quality] ?? colors.F
6
- const desc = card.description || '暂无描述'
7
- return `<!doctype html><html><head><meta charset="utf-8"><style>
8
- *{box-sizing:border-box}html,body{margin:0;width:760px;background:#141c26;color:#15252b;font-family:'Microsoft YaHei','Noto Sans CJK SC',sans-serif}body{padding:0}#capture{width:760px}#card{width:760px;height:1107px;position:relative;background-image:url('${frame}');background-size:100% 100%}.name{position:absolute;left:58px;top:46px;width:644px;height:74px;display:flex;align-items:center;font-size:36px;font-weight:800;color:#f5f7e9;text-shadow:1px 2px 2px #163838;overflow:hidden}.badge{position:absolute;right:57px;top:54px;width:68px;height:62px;border:2px solid ${color};background:#163338;border-radius:50%;color:${color};display:grid;place-items:center;font:bold 25px Georgia}.meta{position:absolute;left:92px;right:91px;top:151px;display:flex;justify-content:space-between;color:#f9f1cf;font-size:21px;font-weight:700}.art{position:absolute;left:93px;top:205px;width:574px;height:574px;overflow:hidden;background:radial-gradient(ellipse at 50% 43%,#284951 0,#12222f 42%,#070d19 90%);border:3px solid #bea874}.skill-artwork{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;z-index:1}.art svg{position:absolute;width:100%;height:100%;color:${color}}.glyph{position:absolute;top:157px;width:100%;text-align:center;font-size:147px;filter:drop-shadow(0 0 30px ${color})}.art-title{position:absolute;bottom:113px;left:25px;right:25px;text-align:center;font-size:18px;letter-spacing:6px;color:${color}}.number{position:absolute;top:786px;right:94px;color:#e2eade;font:14px Georgia;letter-spacing:2px}.effect{position:absolute;top:837px;left:62px;width:636px;height:197px;overflow:hidden;font-size:23px;line-height:1.48}.effect b{font-size:23px}.effect p{margin:8px 0;white-space:pre-wrap;overflow-wrap:anywhere}
9
- </style></head><body><div id="capture"><div id="card"><div class="name" id="name">${escape(card.quality)}级 ${escape(card.name)}</div><div class="meta"><span>【${card.passive ? '被动' : '主动'}技能】</span><span>Lv.${card.level} / ${card.maxLevel}</span></div>
10
- <div class="art"><svg viewBox="0 0 574 574" xmlns="http://www.w3.org/2000/svg"><defs><radialGradient id="glow"><stop stop-color="${color}" stop-opacity=".25"/><stop offset="1" stop-color="${color}" stop-opacity="0"/></radialGradient></defs><circle cx="287" cy="244" r="220" fill="url(#glow)"/><g stroke="currentColor" fill="none"><circle cx="287" cy="244" r="191" opacity=".35"/><circle cx="287" cy="244" r="179" stroke-dasharray="2 13" opacity=".8"/><circle cx="287" cy="244" r="158" opacity=".6"/><path d="M287 65 442 334 132 334Z M287 423 132 155 442 155Z" opacity=".3"/><path d="M60 244H130M444 244H514M287 18V85M287 403V470" stroke-width="2" opacity=".6"/><path d="M80 440 491 72M102 457 505 95" opacity=".13" stroke-width="16"/></g></svg><div class="art-title">${escape(card.passive ? '被 动 铭 刻' : '技 能 术 式')}</div>${artwork ? '<img class="skill-artwork" alt="" src="' + escape(artwork) + '">' : ''}</div>
11
- <div class="number">DEW · SKILL / ${escape(card.quality)}</div><div class="effect" id="effect"><p>${escape(desc)}</p></div></div></div></body></html>`
1
+ import type { SkillCardData } from '../shared/face'
2
+
3
+ const escape = (value: unknown) => String(value ?? '').replace(/[&<>"']/g, c => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' }[c]!))
4
+
5
+ /** One continuous rice-paper sheet; the old frame parameter remains API-compatible. */
6
+ export function skillCardHtml(card: SkillCardData, _frame: string, artwork?: string): string {
7
+ const description = escape((card.description || '暂无技能效果说明').replace(/\s*\n+\s*/g, ''))
8
+ const grain = 'data:image/svg+xml,' + encodeURIComponent('<svg xmlns="http://www.w3.org/2000/svg" width="180" height="180"><filter id="paper"><feTurbulence type="fractalNoise" baseFrequency=".72" numOctaves="3" stitchTiles="stitch"/></filter><rect width="100%" height="100%" opacity=".15" filter="url(#paper)"/></svg>')
9
+ return `<!doctype html><html><head><meta charset="utf-8"><style>
10
+ *{box-sizing:border-box}
11
+ html,body{margin:0;width:760px;background:#f1eee5;font-family:'Microsoft YaHei','Noto Sans CJK SC',sans-serif;color:#34372f}
12
+ #capture{position:relative;isolation:isolate;width:760px;min-height:1060px;padding:57px 58px 38px;background:radial-gradient(ellipse at 0 0,#ddd9c744,transparent 48%),radial-gradient(ellipse at 100% 90%,#c9cdb938,transparent 45%),#f4f1e8;border:1px solid #dedbcf}
13
+ #capture:before{content:'';position:absolute;inset:0;z-index:-1;background-image:url('${grain}');opacity:.42;pointer-events:none}
14
+ #capture:after{content:'';position:absolute;inset:17px;border:1px solid #8c8b6f22;pointer-events:none}
15
+ header{position:relative}.heading{display:flex;align-items:center;gap:24px}.heading-copy{flex:1;min-width:0}.overline{font-size:14px;letter-spacing:6px;color:#868a76;margin-bottom:13px}.name{font-family:'KaiTi','STKaiti','Noto Serif CJK SC','SimSun',serif;font-size:43px;font-weight:700;line-height:1.35;margin:0;color:#303a31;overflow-wrap:anywhere}
16
+ .seal{flex-shrink:0;width:66px;min-height:74px;transform:rotate(3deg);padding:8px 3px;color:#a04b40;border:2px solid #a04b40;outline:1px solid #a04b40;outline-offset:-6px;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:3px;border-radius:3px 1px 4px 2px}.seal strong{font:700 24px Georgia;letter-spacing:-1px}.seal span{font-family:'KaiTi','SimSun',serif;font-size:16px;letter-spacing:4px;padding-left:4px}
17
+ .meta{display:flex;flex-wrap:wrap;align-items:baseline;gap:8px 22px;margin:24px 0 0;padding:15px 0;border-top:1px solid #8a8a7144;border-bottom:1px solid #8a8a7128;font-size:19px;color:#535d50;line-height:1.5}.meta span{overflow-wrap:anywhere;max-width:100%}.meta small{font-size:16px;color:#92917f;margin-right:7px}.meta .kind{color:#915344;font-family:'KaiTi','SimSun',serif;font-size:21px}
18
+ .art{position:relative;width:calc(100% + 44px);height:570px;margin:14px -22px -8px;display:grid;place-items:center;isolation:isolate}.art:before{content:'';position:absolute;inset:15% 10%;border-radius:48% 52% 56% 44%;background:radial-gradient(ellipse,#aab4a443,transparent 69%);transform:rotate(-12deg)}.skill-artwork{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;object-position:center 43%;filter:saturate(.68) contrast(.94);mix-blend-mode:multiply;mask-image:linear-gradient(to bottom,transparent 0,#000 9%,#000 72%,transparent 100%),linear-gradient(to right,transparent 0,#000 8%,#000 92%,transparent 100%);mask-composite:intersect}.art-fallback{position:relative;z-index:-1;width:300px;height:300px;border:2px solid #7c887d33;border-radius:48% 52% 50% 46%;transform:rotate(-12deg);display:grid;place-items:center;color:#8c948177;font:70px 'KaiTi','SimSun',serif}.art-fallback span{transform:rotate(12deg)}.art:has(.skill-artwork) .art-fallback{visibility:hidden}
19
+ .effect{position:relative;margin-top:0}h2{display:flex;align-items:center;gap:15px;margin:0 0 17px;font:22px 'KaiTi','SimSun',serif;letter-spacing:5px;color:#626e5e}h2:before{content:'';width:5px;height:17px;background:#a04b40;transform:rotate(8deg);opacity:.75}h2:after{content:'';flex:1;height:1px;background:linear-gradient(to right,#7f897c55,transparent)}p{margin:0;font-size:25px;line-height:1.85;white-space:pre-wrap;overflow-wrap:anywhere;text-align:justify}.footer{margin-top:24px;padding-top:17px;border-top:1px solid #8a8a7128}.footer p{font-size:18px;color:#7d8070;line-height:1.75;text-align:left}.edition{display:flex;align-items:center;gap:12px;justify-content:center;margin-top:36px;color:#a6a38e;font:14px 'KaiTi','SimSun',serif;letter-spacing:5px}.edition:before,.edition:after{content:'';height:1px;width:32px;background:#9b9c833d}
20
+ </style></head><body><div id="capture" class="skill-sheet ink-skill-sheet"><header><div class="heading"><div class="heading-copy"><div class="overline">技 艺 图 录</div><h1 class="name">${escape(card.name)}</h1></div><div class="seal"><strong>${escape(card.quality)}</strong><span>品阶</span></div></div><div class="meta"><span class="kind">${card.passive ? '被动' : '主动'}</span><span><small>等级</small>${escape(card.level)} / ${escape(card.maxLevel)}</span><span><small>消耗</small>${escape(card.cost || '无消耗')}</span><span><small>冷却</small>${escape(card.cooldown || '无冷却')}</span></div></header><div class="art"><div class="art-fallback"><span>${card.passive ? '静' : '势'}</span></div>${artwork ? `<img class="skill-artwork" alt="" src="${escape(artwork)}">` : ''}</div><section class="effect"><h2>技能效果</h2><p>${description}</p></section>${card.sources?.length || card.note ? `<div class="footer">${card.sources?.length ? `<p>获取来源:${escape(card.sources.join('、'))}</p>` : ''}${card.note ? `<p>${escape(card.note)}</p>` : ''}</div>` : ''}<div class="edition">露 · 技艺录</div></div></body></html>`
12
21
  }