koishi-plugin-dew-bot 1.0.253 → 1.0.254

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 (40) hide show
  1. package/lib/index.js +727 -229
  2. package/lib/plugin/appearanceCardTemplate.d.ts +2 -2
  3. package/lib/plugin/detailCardTemplate.d.ts +22 -0
  4. package/lib/plugin/equipmentCardTemplate.d.ts +1 -1
  5. package/lib/plugin/formatCombatPower.d.ts +2 -0
  6. package/lib/plugin/itemCardTemplate.d.ts +2 -2
  7. package/lib/plugin/qualityCardDecorations.d.ts +7 -0
  8. package/lib/plugin/qualityCardStyles.d.ts +2 -0
  9. package/lib/plugin/serverHandel/qqCommandMessage.d.ts +2 -1
  10. package/lib/plugin/serverHandel/qqMessagePreview.d.ts +11 -0
  11. package/lib/plugin/serverHandel/qqPageMessage.d.ts +2 -2
  12. package/lib/plugin/serverHandel/qqRewardMessage.d.ts +5 -5
  13. package/lib/plugin/serverHandel/qqStoryMessage.d.ts +4 -3
  14. package/lib/plugin/serverHandel/qqTransactionMessage.d.ts +2 -1
  15. package/lib/plugin/skillCardTemplate.d.ts +2 -2
  16. package/lib/shared/face/index.d.ts +25 -1
  17. package/package.json +1 -1
  18. package/src/plugin/appearanceCardTemplate.ts +7 -16
  19. package/src/plugin/cardLayout.ts +5 -4
  20. package/src/plugin/characterCardTemplate.ts +2 -1
  21. package/src/plugin/detailCardTemplate.ts +69 -0
  22. package/src/plugin/equipmentCardTemplate.ts +29 -31
  23. package/src/plugin/formatCombatPower.ts +12 -0
  24. package/src/plugin/itemCardTemplate.ts +13 -19
  25. package/src/plugin/leaderboardTemplate.ts +2 -1
  26. package/src/plugin/qualityCardDecorations.ts +81 -0
  27. package/src/plugin/qualityCardStyles.ts +65 -0
  28. package/src/plugin/serverHandel/handel_base.ts +3 -3
  29. package/src/plugin/serverHandel/qqCommandMessage.ts +2 -2
  30. package/src/plugin/serverHandel/qqMessagePreview.ts +33 -0
  31. package/src/plugin/serverHandel/qqPageMessage.ts +141 -3
  32. package/src/plugin/serverHandel/qqRewardMessage.ts +10 -15
  33. package/src/plugin/serverHandel/qqStoryMessage.ts +10 -5
  34. package/src/plugin/serverHandel/qqTransactionMessage.ts +2 -2
  35. package/src/plugin/serverHandel/text.ts +7 -19
  36. package/src/plugin/skillCard.ts +2 -13
  37. package/src/plugin/skillCardTemplate.ts +56 -21
  38. package/src/shared/bot/analysisMsg.ts +7 -2
  39. package/src/shared/bot/serviceProto.ts +132 -2
  40. package/src/shared/face/index.ts +27 -2
package/lib/index.js CHANGED
@@ -42,45 +42,278 @@ var import_koishi2 = require("koishi");
42
42
  // src/plugin/serverHandel/image.ts
43
43
  var import_koishi = require("koishi");
44
44
 
45
+ // src/plugin/qualityCardDecorations.ts
46
+ var colors = {
47
+ F: ["#8d9690", "#55635b", "#e8ece6", "#77877d", "#aab4ad"],
48
+ E: ["#77a085", "#4f765c", "#e7efe5", "#5d886d", "#9db49a"],
49
+ D: ["#4ba59a", "#327d72", "#e1efea", "#337e79", "#83b6a5"],
50
+ C: ["#69a5dd", "#3975a1", "#e5eff6", "#447ea9", "#88b6db"],
51
+ B: ["#ab82d4", "#8058a4", "#f0e9f5", "#8254a9", "#b28cda"],
52
+ A: ["#dfa263", "#a26b30", "#f7ecde", "#be7734", "#e4ad70"],
53
+ S: ["#d5bc68", "#9b813a", "#f4f0de", "#ac903e", "#dcc67c"],
54
+ SS: ["#d5887e", "#ab5c53", "#f6e8e3", "#ac6259", "#e0a18d"],
55
+ SSS: ["#bd9fb7", "#906f8f", "#f0e8ef", "#7e68a0", "#d3bd8d"]
56
+ };
57
+ var magicSizes = { F: 100, E: 108, D: 118, C: 130, B: 144, A: 160, S: 176, SS: 192, SSS: 208 };
58
+ function normalizeCardQuality(value) {
59
+ const grade = String(value ?? "").trim().toUpperCase().replace(/\s*级$/, "").trim();
60
+ return Object.prototype.hasOwnProperty.call(colors, grade) ? grade : "F";
61
+ }
62
+ __name(normalizeCardQuality, "normalizeCardQuality");
63
+ function qualityStyle(value) {
64
+ const grade = normalizeCardQuality(value);
65
+ const color = colors[grade];
66
+ return ["accent", "ink", "light", "start", "end"].map((key, index) => "--quality-" + key + ":" + color[index]).join(";") + ";--magic-base:" + magicSizes[grade];
67
+ }
68
+ __name(qualityStyle, "qualityStyle");
69
+ var number = /* @__PURE__ */ __name((value) => String(Math.round(value * 1e4) / 1e4), "number");
70
+ function qualityMagicCircle(value) {
71
+ const q = normalizeCardQuality(value);
72
+ const rank = Object.keys(colors).indexOf(q);
73
+ const point = /* @__PURE__ */ __name((r, angle) => {
74
+ const a = angle * Math.PI / 180;
75
+ return [number(Math.cos(a) * r), number(Math.sin(a) * r)];
76
+ }, "point");
77
+ const ring = /* @__PURE__ */ __name((r, dash = "") => `<circle r="${r}"${dash ? ` stroke-dasharray="${dash}"` : ""}/>`, "ring");
78
+ const polygon = /* @__PURE__ */ __name((sides, r, turn = -90) => `<polygon points="${Array.from({ length: sides }, (_, i) => point(r, turn + i * 360 / sides).join(",")).join(" ")}"/>`, "polygon");
79
+ const star = /* @__PURE__ */ __name((tips, outer, inner2, turn = -90) => `<polygon points="${Array.from({ length: tips * 2 }, (_, i) => point(i % 2 ? inner2 : outer, turn + i * 180 / tips).join(",")).join(" ")}"/>`, "star");
80
+ const ticks = /* @__PURE__ */ __name((count, r, length = 5) => Array.from({ length: count }, (_, i) => {
81
+ const a = i * 360 / count - 90;
82
+ const [x1, y1] = point(r, a), [x2, y2] = point(r + length, a);
83
+ return `<path d="M${x1} ${y1} ${x2} ${y2}"/>`;
84
+ }).join(""), "ticks");
85
+ const gems = /* @__PURE__ */ __name((count, r, size = 3, turn = -90) => Array.from({ length: count }, (_, i) => {
86
+ const [x, y] = point(r, turn + i * 360 / count);
87
+ return `<path transform="translate(${x} ${y})" d="M0 ${-size} ${size} 0 0 ${size} ${-size} 0Z"/>`;
88
+ }).join(""), "gems");
89
+ const petals = /* @__PURE__ */ __name((count, r, depth, spread) => Array.from({ length: count }, (_, i) => `<path transform="rotate(${i * 360 / count})" d="M0 ${-r} C${spread} ${-r - depth / 2} ${spread} ${-r - depth} 0 ${-r - depth} C${-spread} ${-r - depth} ${-spread} ${-r - depth / 2} 0 ${-r}Z"/>`).join(""), "petals");
90
+ const inner = number(45 * 240 / magicSizes[q]);
91
+ let orbits = ring(inner), rays = "", marks = "", detail = "";
92
+ switch (q) {
93
+ case "F":
94
+ orbits = ring(106);
95
+ break;
96
+ case "E":
97
+ orbits += ring(110);
98
+ detail = ticks(4, 102, 7);
99
+ marks = gems(4, 112, 2);
100
+ break;
101
+ case "D":
102
+ orbits += ring(106) + polygon(4, 114);
103
+ detail = ticks(8, 98, 5);
104
+ marks = gems(4, 111, 3, 0);
105
+ break;
106
+ case "C":
107
+ orbits += ring(96) + ring(108, "21 5");
108
+ rays = star(4, 117, 82);
109
+ detail = ticks(8, 99, 7);
110
+ marks = gems(4, 109, 3, -45);
111
+ break;
112
+ case "B":
113
+ orbits += ring(91) + ring(109);
114
+ rays = polygon(3, 104) + polygon(3, 104, 90);
115
+ detail = ticks(12, 98, 5);
116
+ marks = gems(6, 111, 3);
117
+ break;
118
+ case "A":
119
+ orbits += ring(86) + ring(103);
120
+ rays = star(8, 115, 80) + polygon(8, 93, -67.5);
121
+ detail = ticks(16, 103, 5);
122
+ marks = gems(8, 111, 3.5);
123
+ break;
124
+ case "S":
125
+ orbits += ring(83) + ring(101) + ring(110, "2 4");
126
+ rays = star(12, 116, 87) + polygon(6, 92);
127
+ detail = ticks(24, 104, 4);
128
+ marks = gems(12, 113, 3);
129
+ break;
130
+ case "SS":
131
+ orbits += ring(80) + ring(98) + ring(108);
132
+ rays = star(8, 115, 77) + star(8, 103, 73, -67.5);
133
+ detail = `<ellipse rx="108" ry="58" transform="rotate(35)"/><ellipse rx="108" ry="58" transform="rotate(-35)"/>` + ticks(24, 106, 5);
134
+ marks = gems(8, 114, 4) + gems(8, 96, 2, -67.5);
135
+ break;
136
+ case "SSS":
137
+ orbits += ring(77) + ring(96) + ring(111, "3 4");
138
+ rays = petals(12, 76, 38, 13) + star(8, 116, 83) + star(8, 101, 77, -67.5);
139
+ detail = ticks(32, 101, 5) + polygon(4, 93, -45) + polygon(4, 93);
140
+ marks = gems(4, 114, 5) + gems(12, 108, 2.5, -75);
141
+ break;
142
+ }
143
+ return `<svg class="magic-circle magic-${q.toLowerCase()}" data-rank="${rank}" viewBox="-120 -120 240 240" fill="none" aria-hidden="true"><g class="magic-rays">${rays}</g><g class="magic-orbits">${orbits}</g><g class="magic-detail">${detail}</g><g class="magic-marks">${marks}</g></svg>`;
144
+ }
145
+ __name(qualityMagicCircle, "qualityMagicCircle");
146
+ var circle = /* @__PURE__ */ __name((radius, stroke, width, opacity = 1) => '<circle cx="0" cy="0" r="' + radius + '" fill="none" stroke="' + stroke + '" stroke-width="' + width + '" opacity="' + opacity + '"/>', "circle");
147
+ var metal = /* @__PURE__ */ __name((radius, width, opacity = 1) => circle(radius, "currentColor", width, opacity), "metal");
148
+ var light = /* @__PURE__ */ __name((radius, width, opacity = 0.65) => circle(radius, "#fff5df", width, opacity), "light");
149
+ var shadow = /* @__PURE__ */ __name((radius, width, opacity = 0.7) => circle(radius, "#15191f", width, opacity), "shadow");
150
+ var repeat = /* @__PURE__ */ __name((angles, shape) => angles.map((angle) => '<g transform="rotate(' + angle + ')">' + shape + "</g>").join(""), "repeat");
151
+ var base = /* @__PURE__ */ __name((width = 2) => shadow(46.5, width + 1.8, 0.78) + metal(46.5, width) + light(45.2, 0.5, 0.6), "base");
152
+ var designs = {
153
+ F: shadow(45.7, 1.5, 0.4) + metal(45.7, 0.9, 0.72) + light(46.45, 0.35, 0.5),
154
+ E: base(2.2) + metal(48.4, 0.6, 0.65) + repeat([0, 180], '<path d="M-5-48.5 H5" stroke="#fff5df" stroke-width=".8" opacity=".58"/>'),
155
+ D: base(1.8) + shadow(49.1, 1.8, 0.7) + metal(49.1, 0.95, 0.85) + light(48.5, 0.35, 0.42),
156
+ C: base(2.2) + metal(49.4, 0.85, 0.8) + repeat([90, 270], '<path d="M-3.5-46.3 H3.5 L4.2-51.3 H-4.2 Z" fill="currentColor" stroke="#192332" stroke-width=".7"/><path d="M-2.7-50.5 H2.7" stroke="#f0f5fc" stroke-width=".8" opacity=".8"/><path d="M0-47.2 V-50.6" stroke="#f0f5fc" stroke-width=".45" opacity=".55"/>'),
157
+ B: base(2.6) + metal(49.3, 0.65, 0.7) + repeat([0, 90, 180, 270], '<path d="M0-55 L3.8-50.8 0-46.7-3.8-50.8 Z" fill="currentColor" stroke="#211827" stroke-width=".8"/><path d="M0-54.1 2.8-50.8 0-49.5-2.8-50.8 Z" fill="#fff4f7" opacity=".52"/><path d="M0-49.5 V-47.4" stroke="#161320" stroke-width=".55" opacity=".65"/>'),
158
+ A: base(3.1) + light(48.2, 0.45, 0.65) + repeat([45, 135, 225, 315], '<path d="M-4-46.1 H4 L5.2-50.1 0-54.6-5.2-50.1 Z" fill="currentColor" stroke="#302316" stroke-width=".8"/><path d="M-3.5-49.8 0-53.6 3.5-49.8 0-48.6 Z" fill="#ffe9ba" opacity=".7"/><path d="M-3.5-49.8 0-48.6 0-46.6 H-3.2 Z" fill="#4e3017" opacity=".42"/>'),
159
+ S: base(3.3) + metal(49.4, 0.7, 0.9) + light(48.5, 0.6, 0.75) + repeat([0, 180], '<path d="M-7.6-47.1-5.6-51.1-2.5-50.1 0-55 2.5-50.1 5.6-51.1 7.6-47.1 3.6-48.4 0-50.4-3.6-48.4 Z" fill="currentColor" stroke="#3f3118" stroke-width=".8"/><path d="M-5.6-50.2-2.2-49.4 0-53.5 2.2-49.4 5.6-50.2" fill="none" stroke="#fff2c8" stroke-width=".8" opacity=".92"/><path d="M0-49.8 1.5-48.2 0-46.9-1.5-48.2 Z" fill="#fff3c8" opacity=".9"/>'),
160
+ SS: base(3.7) + metal(49.6, 0.75, 0.8) + light(48.55, 0.55, 0.6) + repeat([0, 90, 180, 270], '<path d="M-5-47.2-5.7-51.3 0-55 5.7-51.3 5-47.2 0-46.1 Z" fill="currentColor" stroke="#34191d" stroke-width=".85"/><path d="M-4.4-50.9 0-53.9 4.4-50.9 0-49.4 Z" fill="#ffddd0" opacity=".66"/><path d="M-4.4-50.9 0-49.4 V-47.1 L-3.9-47.8 Z" fill="#3c151c" opacity=".55"/><path d="M0-53.4 V-50.3" stroke="#fff2db" stroke-width=".65" opacity=".85"/>'),
161
+ SSS: shadow(46.7, 5.2, 0.86) + metal(46.7, 3.5, 1) + light(45.2, 0.6, 0.8) + shadow(50, 2.5, 0.75) + circle(50, "#d7b76f", 1.4, 0.95) + light(49.45, 0.45, 0.85) + repeat([0, 90, 180, 270], '<path d="M0-55 2.2-52.6 5.1-51 2.2-49.4 0-46.8-2.2-49.4-5.1-51-2.2-52.6 Z" fill="#d7b76f" stroke="#31213b" stroke-width=".8"/><path d="M0-54 1.5-52 3.4-51 1.5-50 0-48-1.5-50-3.4-51-1.5-52 Z" fill="currentColor"/><path d="M0-53.1 1.15-51 0-49.4-1.15-51 Z" fill="#fff4e7" opacity=".8"/>')
162
+ };
163
+ function qualityIconFrame(value) {
164
+ const grade = normalizeCardQuality(value);
165
+ return '<svg class="skill-icon-frame" aria-hidden="true" focusable="false" xmlns="http://www.w3.org/2000/svg" viewBox="-60 -60 120 120" fill="none" stroke-linejoin="round" stroke-linecap="round">' + designs[grade] + "</svg>";
166
+ }
167
+ __name(qualityIconFrame, "qualityIconFrame");
168
+
169
+ // src/plugin/qualityCardStyles.ts
170
+ var qualityCardStyles = `
171
+ *{box-sizing:border-box}
172
+ html,body{margin:0;width:760px;background:#e9e9e2;font-family:'Microsoft YaHei','Noto Sans CJK SC',sans-serif;color:#303735}
173
+ #capture{width:760px;padding:18px;background:radial-gradient(ellipse at 70% 10%,#f9f7efb8,transparent 55%),#e9e9e2}
174
+ h1,h2,h3,p{margin:0}
175
+ .detail-card{position:relative;width:100%;isolation:isolate;border:3px solid color-mix(in srgb,var(--quality-accent) 50%,#454a45);border-radius:18px;box-shadow:0 8px 18px #3f47321c;background:#f5f4ed;overflow:visible;--corner-size:24px;--corner-metal:#dce1d6;--corner-cut:polygon(0 0,100% 13%,75% 50%,100% 100%,50% 75%,13% 100%)}
176
+ .card-inner{overflow:hidden;border-radius:14px}
177
+ .card-corner{position:absolute;width:var(--corner-size);height:var(--corner-size);z-index:5;background:linear-gradient(135deg,#faf9eb,var(--corner-metal));border:1px solid #7b8075;clip-path:var(--corner-cut);filter:drop-shadow(0 1px 2px #0009)}
178
+ .card-corner:after{content:'';position:absolute;inset:6px;background:color-mix(in srgb,var(--quality-accent) 80%,#4e584b);clip-path:var(--corner-cut)}
179
+ .corner-tl{top:-7px;left:-7px}.corner-tr{top:-7px;right:-7px;transform:rotate(90deg)}.corner-br{bottom:-7px;right:-7px;transform:rotate(180deg)}.corner-bl{bottom:-7px;left:-7px;transform:rotate(270deg)}
180
+ .quality-f{--corner-size:13.5px;--corner-metal:#a6afa4;--corner-cut:polygon(0 0,100% 0,100% 30%,30% 30%,30% 100%,0 100%)}
181
+ .quality-f .card-corner:after{display:none}
182
+ .quality-e{--corner-size:18px;--corner-metal:#a6c1a7;--corner-cut:polygon(0 0,100% 0,85% 28%,37% 37%,28% 85%,0 100%)}
183
+ .quality-d{--corner-size:21px;--corner-metal:#87c3b1;--corner-cut:polygon(0 0,70% 10%,100% 45%,45% 100%,10% 70%)}
184
+ .quality-c{--corner-metal:#acd5ed}
185
+ .quality-b{--corner-size:25.5px;--corner-metal:#c2a5dc;--corner-cut:polygon(0 0,58% 14%,100% 4%,86% 47%,100% 100%,47% 86%,4% 100%,14% 58%)}
186
+ .quality-a{--corner-size:27px;--corner-metal:#e2ba7b}
187
+ .quality-s{--corner-size:30px;--corner-metal:#e2ca7c;--corner-cut:polygon(0 0,70% 9%,100% 0,86% 30%,67% 45%,100% 100%,45% 67%,30% 86%,0 100%,9% 70%)}
188
+ .quality-ss{--corner-size:30px;--corner-metal:#dea595;--corner-cut:polygon(0 0,78% 14%,100% 43%,70% 70%,43% 100%,14% 78%)}
189
+ .quality-sss{--corner-size:34.5px;--corner-metal:#d6b575;--corner-cut:polygon(0 0,55% 14%,100% 3%,82% 30%,68% 50%,100% 100%,50% 68%,30% 82%,3% 100%,14% 55%)}
190
+ .quality-sss .card-corner:after{background:#9d79ac}
191
+ .card-titlebar{position:relative;isolation:isolate;display:flex;align-items:center;gap:18px;min-height:98px;padding:27px 35px;background:linear-gradient(112deg,var(--quality-start),var(--quality-end));border-bottom:4px solid var(--quality-accent);color:#fffbee}
192
+ .card-titlebar:before{content:'';position:absolute;inset:0;z-index:-1;background-image:radial-gradient(circle,#fff7 1.5px,transparent 2.2px),linear-gradient(30deg,transparent 65%,#ffffff1a 65.2%,transparent 65.6%);background-size:118px 95px,100% 100%;opacity:.55}
193
+ .card-titlebar h1{min-width:0;flex:1;font-size:35px;line-height:1.4;letter-spacing:1px;font-weight:600;text-shadow:0 1px 2px #2c292348;overflow-wrap:anywhere}
194
+ .title-glyph{font-size:31px;line-height:1;color:#fff6cecc}.title-star{font:38px Georgia;opacity:.75}
195
+ .card-hero{position:relative;isolation:isolate;display:flex;align-items:center;justify-content:space-between;gap:24px;min-height:228px;padding:30px 36px;color:#eeeedd;background:radial-gradient(ellipse at 0 0,color-mix(in srgb,var(--quality-accent) 33%,transparent),transparent 65%),#272f30}
196
+ .card-hero:before{content:'';position:absolute;inset:0;z-index:-1;opacity:.16;background-image:radial-gradient(circle,#d3d4bd 1px,transparent 1.2px);background-size:7.5px 7.5px;mask-image:linear-gradient(to bottom,#000,transparent 74%)}
197
+ .hero-copy{flex:1;min-width:0;align-self:stretch;display:flex;flex-direction:column;justify-content:space-between}
198
+ .hero-meta{display:flex;flex-direction:column;align-items:flex-start;gap:15px;margin-bottom:25px}
199
+ .skill-type{font-size:24px;letter-spacing:3px;color:#cbcdbd}
200
+ .quality-badge{display:inline-flex;align-items:center;gap:9px;padding:6px 14px;color:#fff5de;font-size:18px;font-weight:600;border:1px solid color-mix(in srgb,var(--quality-accent) 70%,transparent);border-radius:6px;background:color-mix(in srgb,var(--quality-accent) 17%,transparent)}
201
+ .quality-badge b{color:var(--quality-end);font:700 24px Georgia,serif}
202
+ .tags{display:flex;gap:7px;flex-wrap:wrap}
203
+ .tag{font-size:20px;line-height:1.3;padding:6px 10px;border-radius:4px;overflow-wrap:anywhere;color:#bfcaed;background:#647bb434;border:1px solid #7d9cc62b}
204
+ .tag.damage{color:#f5ae94;background:#b961433d;border-color:#b4614340}.tag.shield{color:#a3d7b7;background:#5186683d;border-color:#73a48a40}.tag.heal{color:#b5deb0;background:#668e4a3b;border-color:#89a47140}.tag.passive{color:#cebbef;background:#8367a63d;border-color:#9682b540}.tag.growth{color:#ecd291;background:#b1944540;border-color:#ab944e40}
205
+ .hero-icon-wrap{--icon-size:120px;--magic-size:calc(var(--magic-base) * 1.5px);position:relative;flex-shrink:0;width:var(--magic-size);height:var(--magic-size)}
206
+ .magic-circle{position:absolute;width:100%;height:100%;top:50%;left:50%;transform:translate(-50%,-50%);color:var(--quality-end);pointer-events:none;overflow:visible}
207
+ .magic-rays{stroke:currentColor;stroke-width:.9;opacity:.28}.magic-orbits{stroke:currentColor;stroke-width:1.2;opacity:.42}.magic-detail{stroke:currentColor;stroke-width:.9;opacity:.45}.magic-marks{fill:currentColor;opacity:.68}
208
+ .magic-sss .magic-rays{stroke:#ddbc80;opacity:.49}.magic-sss .magic-detail{stroke:#bb9ccc;opacity:.64}.magic-sss .magic-marks{fill:#eed093;opacity:.88}
209
+ .quality-s .magic-circle,.quality-ss .magic-circle,.quality-sss .magic-circle{filter:drop-shadow(0 0 4px color-mix(in srgb,var(--quality-accent) 30%,transparent))}
210
+ .skill-artwork,.art-fallback{position:absolute;z-index:1;top:50%;left:50%;transform:translate(-50%,-50%);width:var(--icon-size);height:var(--icon-size);border:1.5px solid #bfc6b7;border-radius:50%;box-shadow:0 4px 13px #0007;object-fit:cover;object-position:center}
211
+ .art-fallback{display:grid;place-items:center;color:var(--quality-end);font:44px 'KaiTi','SimSun',serif;background:radial-gradient(circle,#53615b,#1f2928)}
212
+ .skill-icon-frame{position:absolute;top:50%;left:50%;width:calc(var(--icon-size) + 45px);height:calc(var(--icon-size) + 45px);transform:translate(-50%,-50%);z-index:2;color:var(--quality-accent);pointer-events:none;filter:drop-shadow(0 1px 1px #10181480)}
213
+ .quality-sss .hero-icon-wrap{background:radial-gradient(ellipse,#ad85d525,transparent 68%)}
214
+ .card-body{padding:32px 36px 34px;background:linear-gradient(145deg,#faf9f3,#eeeee7)}
215
+ .effect-stats{display:grid;gap:9px;margin-bottom:27px}
216
+ .stat-row{display:flex;align-items:center;justify-content:space-between;gap:18px;min-height:54px;padding:13px 18px;background:#e5e7de;border-radius:6px;font-size:24px}
217
+ .stat-row>span{color:#626d57;flex:0 0 auto;max-width:45%;overflow-wrap:anywhere}
218
+ .stat-row b{min-width:0;text-align:right;overflow-wrap:anywhere;font-size:30px;color:var(--quality-ink);font-weight:600}
219
+ .stat-row.detail-stat b{font-size:26px;font-weight:500;color:#4f594b;background:none;-webkit-text-fill-color:currentColor}
220
+ .stat-row .param-unit{font-size:24px;font-weight:400;color:#75816d;-webkit-text-fill-color:currentColor}
221
+ .stat-value{display:flex;flex-wrap:wrap;align-items:baseline;justify-content:flex-end;gap:9px}
222
+ .stat-value .stat-unit{font-size:24px;font-weight:400;color:#737c68;-webkit-text-fill-color:currentColor}
223
+ .section-heading{display:flex;align-items:center;gap:10px;margin:0 0 16px;font-size:24px;font-weight:500;letter-spacing:2px;color:#6f7d60}
224
+ .section-heading:before{content:'◆';font-size:10px;color:var(--quality-accent)}
225
+ .description{font-size:28px;line-height:1.85;color:#515b4c;overflow-wrap:anywhere;white-space:pre-wrap}
226
+ .description .number{color:var(--quality-ink);font-weight:600}.description .keyword{color:#907843;font-weight:500}
227
+ .growth-section,.note-section{border-top:1px solid #d6dacd;margin-top:27px;padding-top:24px}
228
+ .growth-section .description{color:#5c684f}
229
+ .note-section .description{font-size:26px;color:#626d57}
230
+ .quality-sss .card-titlebar{background:linear-gradient(110deg,#7c679e,#a77c95 50%,#cba770)}
231
+ .quality-sss .quality-badge{border-color:#d3c192}
232
+ .quality-sss .stat-number{background:linear-gradient(90deg,#936891,#ab884d);background-clip:text;-webkit-text-fill-color:transparent}
233
+ `;
234
+
235
+ // src/plugin/detailCardTemplate.ts
236
+ var cardEscape = /* @__PURE__ */ __name((value) => String(value ?? "").replace(/[&<>"']/g, (c) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;" })[c]), "cardEscape");
237
+ function detailCardQuality(value) {
238
+ const grade = String(value ?? "").trim().toUpperCase().replace(/\s*级$/, "").trim();
239
+ return ["F", "E", "D", "C", "B", "A", "S", "SS", "SSS"].includes(grade) ? normalizeCardQuality(grade) : void 0;
240
+ }
241
+ __name(detailCardQuality, "detailCardQuality");
242
+ function cardStatRow(label, value, primary = false) {
243
+ const match = primary ? value.match(/^([+-]?\d+(?:,\d{3})*(?:\.\d+)?%?)\s*(.*)$/) : void 0;
244
+ const display = match ? `<span class="stat-number">${cardEscape(match[1])}</span>${match[2] ? `<small class="stat-unit">${cardEscape(match[2])}</small>` : ""}` : cardEscape(value);
245
+ return `<div class="stat-row${primary ? "" : " detail-stat"}"><span>${cardEscape(label)}</span><b${primary ? ' class="stat-value"' : ""}>${display}</b></div>`;
246
+ }
247
+ __name(cardStatRow, "cardStatRow");
248
+ function cardSection(title, body) {
249
+ return `<section class="detail-section"><h2 class="section-heading">${cardEscape(title)}</h2>${body}</section>`;
250
+ }
251
+ __name(cardSection, "cardSection");
252
+ function cardBodyText(value) {
253
+ return value.split(/(\d+(?:\.\d+)?%?)/g).map((part) => /^\d+(?:\.\d+)?%?$/.test(part) ? `<span class="number">${cardEscape(part)}</span>` : cardEscape(part)).join("");
254
+ }
255
+ __name(cardBodyText, "cardBodyText");
256
+ var neutralStyle = "--quality-accent:#afb9c1;--quality-ink:#52616c;--quality-light:#ecf0f1;--quality-start:#56616d;--quality-end:#77828a;--magic-base:140";
257
+ var neutralMagic = '<svg class="magic-circle magic-neutral" aria-hidden="true" viewBox="-120 -120 240 240" fill="none"><g class="magic-orbits"><circle r="98"/><circle r="112"/></g><g class="magic-detail"><path d="M0-108V-116 M108 0H116 M0 108V116 M-108 0H-116"/></g></svg>';
258
+ var neutralFrame = '<svg class="skill-icon-frame" aria-hidden="true" viewBox="-60 -60 120 120" fill="none"><circle r="46.5" stroke="#15191f" stroke-width="4"/><circle r="46.5" stroke="#c1cbd0" stroke-width="1.6"/><circle r="49.5" stroke="#afb9c1" stroke-width=".8"/><circle r="45.4" stroke="#f2f4ef" stroke-width=".5"/></svg>';
259
+ function detailCardHtml(options) {
260
+ const quality = detailCardQuality(options.quality);
261
+ const artClass = options.artworkClass ?? "item-artwork";
262
+ const styles = `${qualityCardStyles}
263
+ .detail-section+.detail-section{border-top:1px solid #d6dacd;margin-top:27px;padding-top:24px}
264
+ .item-artwork{position:absolute;z-index:1;top:50%;left:50%;transform:translate(-50%,-50%);width:var(--icon-size);height:var(--icon-size);border:1.5px solid #bfc6b7;border-radius:50%;object-fit:cover;object-position:center;box-shadow:0 4px 13px #0007}
265
+ .quality-none{--corner-size:18px;--corner-metal:#bcc7cd;--corner-cut:polygon(0 0,100% 0,100% 22%,40% 40%,22% 100%,0 100%);border-color:#69777e}
266
+ .quality-none .card-corner:after{background:#596970}
267
+ .quality-none .card-titlebar{border-bottom:2px solid #afb9c1}
268
+ .quality-none .card-hero{background:radial-gradient(ellipse at 0 0,#8799a51c,transparent 65%),#272f30}
269
+ .quality-none .magic-circle{color:#aebcc4}
270
+ .quality-none .hero-meta{margin-bottom:0}
271
+ .quality-none .stat-number{color:#52616c}
272
+ .detail-portrait{display:block;width:100%;height:auto;max-height:900px;object-fit:contain;background:#ebece5;border:1px solid #d0d5c8;border-radius:8px}
273
+ .detail-note{font-size:26px;line-height:1.75;color:#626d57;overflow-wrap:anywhere;white-space:pre-wrap}
274
+ .detail-list{display:grid;gap:9px}
275
+ .empty-line{font-size:26px;line-height:1.75;color:#7a8472;overflow-wrap:anywhere}
276
+ .art-status{color:#c2cbbc;font-size:20px;line-height:1.5;overflow-wrap:anywhere}
277
+ `;
278
+ return `<!doctype html><html lang="zh-CN"><head><meta charset="utf-8"><style>${styles}</style></head><body><div id="capture" class="detail-sheet ${cardEscape(options.sheetClass || "item-sheet")}"><article class="detail-card ${quality ? "quality-" + quality.toLowerCase() : "quality-none"}" style="${quality ? qualityStyle(quality) : neutralStyle}">
279
+ <i class="card-corner corner-tl" aria-hidden="true"></i><i class="card-corner corner-tr" aria-hidden="true"></i><i class="card-corner corner-bl" aria-hidden="true"></i><i class="card-corner corner-br" aria-hidden="true"></i>
280
+ <div class="card-inner"><header class="card-titlebar"><span class="title-glyph" aria-hidden="true">◇</span><h1>${cardEscape(options.name)}</h1><span class="title-star" aria-hidden="true">✦</span></header>
281
+ <div class="card-hero"><div class="hero-copy"><div class="hero-meta"><span class="skill-type">${cardEscape(options.type)}</span>${quality ? `<span class="quality-badge"><b>${quality}</b> 级</span>` : ""}</div><div class="tags">${(options.tags || []).map((tag) => `<span class="tag">${cardEscape(tag)}</span>`).join("")}</div></div>
282
+ <div class="hero-icon-wrap">${quality ? qualityMagicCircle(quality) : neutralMagic}<div class="art-fallback" aria-hidden="true">${cardEscape(options.fallbackGlyph || "物")}</div>${options.artwork ? `<img class="${artClass}" alt="${cardEscape(options.name)}图标" src="${cardEscape(options.artwork)}">` : ""}${quality ? qualityIconFrame(quality) : neutralFrame}</div></div>
283
+ <div class="card-body">${options.body}</div></div></article></div></body></html>`;
284
+ }
285
+ __name(detailCardHtml, "detailCardHtml");
286
+
287
+ // src/plugin/itemCardTemplate.ts
288
+ function itemCardHtml(card, artwork, _paper, fullPortrait = false) {
289
+ const rows = cardStatRow("数量", card.count || "1") + cardStatRow("参考价格", card.referencePrice || "暂无") + (card.rows || []).map((row) => cardStatRow(row.label, row.value)).join("");
290
+ const portrait = fullPortrait && artwork ? cardSection("形象预览", `<img class="detail-portrait" src="${cardEscape(artwork)}" alt="${cardEscape(card.name)}完整形象">`) : "";
291
+ const body = `<div class="effect-stats">${rows}</div>` + cardSection("物品说明", `<p class="description item-description">${cardBodyText(card.description || "暂无描述")}</p>`) + (card.useDescription?.trim() ? cardSection("使用效果", `<p class="description item-use-description">${cardBodyText(card.useDescription.trim())}</p>`) : "") + portrait;
292
+ return detailCardHtml({ name: card.name, quality: card.quality, type: card.type || "道具", artwork, artworkClass: "item-artwork", body, sheetClass: fullPortrait ? "item-sheet appearance-sheet" : "item-sheet", fallbackGlyph: fullPortrait ? "像" : "物" });
293
+ }
294
+ __name(itemCardHtml, "itemCardHtml");
295
+
45
296
  // src/plugin/appearanceCardTemplate.ts
46
- var escape = /* @__PURE__ */ __name((value) => String(value ?? "").replace(/[&<>"']/g, (c) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;" })[c]), "escape");
47
297
  function appearanceCardHtml(card, artwork, paper) {
48
- return `<!doctype html><html lang="zh-CN"><head><meta charset="utf-8"><style>
49
- *{box-sizing:border-box}html,body{margin:0;width:760px;background:#ece8df;color:#625a49;font-family:'Microsoft YaHei','Noto Sans CJK SC',sans-serif}
50
- #capture{width:760px;padding:25px 18px 29px;background:radial-gradient(ellipse at center,#f6f2e9,#e4ded2)}
51
- .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}
52
- .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}
53
- .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}
54
- .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}
55
- .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}
56
- </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>`;
298
+ return itemCardHtml(card, artwork, paper, true);
57
299
  }
58
300
  __name(appearanceCardHtml, "appearanceCardHtml");
59
301
 
60
- // src/plugin/itemCardTemplate.ts
61
- var escape2 = /* @__PURE__ */ __name((value) => String(value ?? "").replace(/[&<>"']/g, (c) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;" })[c]), "escape");
62
- function itemCardHtml(card, artwork, paper) {
63
- return `<!doctype html><html lang="zh-CN"><head><meta charset="utf-8"><style>
64
- *{box-sizing:border-box}html,body{margin:0;width:760px;color:#29271f;font-family:SimSun,'Noto Serif CJK SC','Microsoft YaHei',serif}
65
- #capture{width:760px;min-height:1140px;padding:30px 48px 38px;background-color:#f3eddf;${paper ? `background-image:url('${escape2(paper)}');background-size:100% 100%;` : ""}box-shadow:inset 0 0 55px #85796228;position:relative}
66
- .catalog-label{display:flex;align-items:center;justify-content:center;gap:22px;font-size:25px;letter-spacing:8px;margin-bottom:32px}.catalog-label:before,.catalog-label:after{content:'';width:106px;height:1px;background:#69625288}
67
- .item-heading{display:grid;grid-template-columns:140px minmax(0,1fr) 140px;align-items:center;margin-bottom:10px;min-height:76px}.item-title{font-family:KaiTi,STKaiti,'Noto Serif CJK SC',serif;font-weight:700;font-size:51px;line-height:1.3;overflow-wrap:anywhere;grid-column:2;text-align:center;margin:0}
68
- .quality{grid-column:3;grid-row:1;justify-self:end;display:flex;align-items:center;gap:8px;flex-shrink:0}.quality-label{writing-mode:vertical-rl;font-size:18px;letter-spacing:4px}.quality-seal{border:3px double #a33f31;padding:8px 5px;min-width:68px;max-width:116px;text-align:center;color:#a33f31;font-family:KaiTi,STKaiti,serif;font-size:33px;font-weight:bold;box-shadow:inset 0 0 0 2px #a33f3126;overflow-wrap:anywhere}
69
- .item-type{text-align:center;font-size:24px;margin:16px 0 22px;overflow-wrap:anywhere}
70
- .item-art-frame{width:548px;height:548px;padding:8px;margin:0 auto;border:2px solid #4b473d;outline:1px solid #4b473d88;outline-offset:4px;background:#eee6d5}
71
- .item-artwork{display:block;width:100%;height:100%;object-fit:cover;background:#ede6d8}
72
- .item-count{display:flex;gap:20px;justify-content:center;align-items:center;font-size:26px;margin:25px 0 30px;overflow-wrap:anywhere}.item-count:before,.item-count:after{content:'';height:1px;width:85px;background:#77705f88}
73
- .description-title{display:flex;align-items:center;gap:22px;font-family:KaiTi,STKaiti,serif;font-size:34px;font-weight:bold;margin-bottom:16px}.description-title:after{content:'';height:1px;background:#655e5088;flex:1}
74
- .item-description{font-size:25px;line-height:1.75;white-space:pre-wrap;overflow-wrap:anywhere;margin:0 0 42px;min-height:88px}
75
- .item-price{border-top:1px solid #736b5b88;padding-top:22px;font-size:24px;line-height:1.6;white-space:pre-wrap;overflow-wrap:anywhere}
76
- </style></head><body><article id="capture"><div class="catalog-label">道具图鉴</div><header class="item-heading"><h1 class="item-title">${escape2(card.name)}</h1><div class="quality"><span class="quality-label">品质</span><span class="quality-seal">${escape2(card.quality)}</span></div></header><div class="item-type">类型:${escape2(card.type)}</div><div class="item-art-frame"><img class="item-artwork" alt="道具插画" src="${escape2(artwork)}"></div><div class="item-count">数量:${escape2(card.count)}</div><div class="description-title">描述</div><p class="item-description">${escape2(card.description)}</p><footer class="item-price">参考价格:${escape2(card.referencePrice)}</footer></article></body></html>`;
302
+ // src/plugin/formatCombatPower.ts
303
+ var compactPower = new Intl.NumberFormat("zh-CN", {
304
+ useGrouping: false,
305
+ minimumFractionDigits: 1,
306
+ maximumFractionDigits: 1
307
+ });
308
+ function formatCombatPower(value) {
309
+ return Number.isFinite(value) && Math.abs(value) >= 1e4 ? compactPower.format(value / 1e4) + "万" : value.toLocaleString("zh-CN", { maximumFractionDigits: 2 });
77
310
  }
78
- __name(itemCardHtml, "itemCardHtml");
311
+ __name(formatCombatPower, "formatCombatPower");
79
312
 
80
313
  // src/plugin/characterCardTemplate.ts
81
- var escape3 = /* @__PURE__ */ __name((value) => String(value ?? "").replace(/[&<>"']/g, (c) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;" })[c]), "escape");
314
+ var escape = /* @__PURE__ */ __name((value) => String(value ?? "").replace(/[&<>"']/g, (c) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;" })[c]), "escape");
82
315
  var finite = /* @__PURE__ */ __name((value) => Number.isFinite(value) ? value : 0, "finite");
83
- var number = /* @__PURE__ */ __name((value) => finite(value).toLocaleString("zh-CN", { maximumFractionDigits: 2 }), "number");
316
+ var number2 = /* @__PURE__ */ __name((value) => finite(value).toLocaleString("zh-CN", { maximumFractionDigits: 2 }), "number");
84
317
  var ratio = /* @__PURE__ */ __name((value, max) => max > 0 ? Math.max(0, Math.min(1, finite(value) / max)) : 0, "ratio");
85
318
  var qualityColors = { F: "#99958b", E: "#bac1ab", D: "#75b889", C: "#75a8cb", B: "#ab88cc", A: "#e5bd64", S: "#d8a2f0", SS: "#edb76e", SSS: "#f09783" };
86
319
  function radar(attributes) {
@@ -92,7 +325,7 @@ function radar(attributes) {
92
325
  }, "rank");
93
326
  return rank(a.name) - rank(b.name);
94
327
  });
95
- if (attrs.length < 3) return `<div class="empty">${attrs.map((a) => `${escape3(a.name)} ${number(a.value)}`).join("<br>") || "暂无一级属性"}</div>`;
328
+ if (attrs.length < 3) return `<div class="empty">${attrs.map((a) => `${escape(a.name)} ${number2(a.value)}`).join("<br>") || "暂无一级属性"}</div>`;
96
329
  const max = Math.max(1, ...attrs.map((a) => Math.max(0, finite(a.value))));
97
330
  const ceiling = Math.ceil(max / Math.pow(10, Math.floor(Math.log10(max)))) * Math.pow(10, Math.floor(Math.log10(max)));
98
331
  const point = /* @__PURE__ */ __name((index, radius) => {
@@ -108,9 +341,9 @@ function radar(attributes) {
108
341
  ${data.map((p) => `<circle cx="${p[0]}" cy="${p[1]}" r="4.5" fill="#174e45"/>`).join("")}
109
342
  ${attrs.map((a, i) => {
110
343
  const [x, y] = point(i, 172);
111
- return `<g transform="translate(${x} ${y})"><path d="M-44 -18 H44 L49 -6 L44 6 H-44 L-49 -6 Z" fill="#222827" stroke="#ab8b4f"/><text y="0" text-anchor="middle" fill="#f3e7c9" font-size="17">${escape3(a.name)}</text><text y="32" text-anchor="middle" fill="#1c5048" font-size="${number(a.value).length > 12 ? 17 : 24}" font-weight="bold">${number(a.value)}</text></g>`;
344
+ return `<g transform="translate(${x} ${y})"><path d="M-44 -18 H44 L49 -6 L44 6 H-44 L-49 -6 Z" fill="#222827" stroke="#ab8b4f"/><text y="0" text-anchor="middle" fill="#f3e7c9" font-size="17">${escape(a.name)}</text><text y="32" text-anchor="middle" fill="#1c5048" font-size="${number2(a.value).length > 12 ? 17 : 24}" font-weight="bold">${number2(a.value)}</text></g>`;
112
345
  }).join("")}
113
- </svg><div class="scale">各轴同尺度 · 满刻度 ${number(ceiling)}</div>`;
346
+ </svg><div class="scale">各轴同尺度 · 满刻度 ${number2(ceiling)}</div>`;
114
347
  }
115
348
  __name(radar, "radar");
116
349
  function characterCardHtml(card, artwork, paper, defaultArtwork) {
@@ -131,13 +364,13 @@ function characterCardHtml(card, artwork, paper, defaultArtwork) {
131
364
  .power-label{font-size:19px;letter-spacing:2px}.power-value{font:bold 43px Georgia,serif;margin-top:10px;color:#edd69a;overflow-wrap:anywhere}.power-value.long{font-size:29px}.health{padding:15px 13px}.health-label{font-size:18px;margin-bottom:9px}.bar{position:relative;height:30px;border:1px solid #bea365;background:#101817;box-shadow:inset 0 2px 4px #000}.fill{height:100%;background:linear-gradient(#df4d39,#9a241d);border-right:1px solid #f19b67}.bar b{position:absolute;inset:0;display:grid;place-items:center;color:#fff7e6;font:16px Georgia,serif;text-shadow:0 1px 3px #000}
132
365
  .panel{margin-top:22px;border:3px solid #252a27;box-shadow:0 0 0 1px #b19559,inset 0 0 0 1px #b19559;background:#f9f1df65;padding:10px 12px 14px}.heading{display:inline-block;margin:-10px 0 0 -12px;padding:9px 25px 10px 20px;background:#222927;border-right:3px solid #b3975a;border-bottom:1px solid #b3975a;color:#eee1bb;font:25px KaiTi,STKaiti,serif;letter-spacing:4px;clip-path:polygon(0 0,94% 0,100% 50%,94% 100%,0 100%)}
133
366
  .radar{display:block;width:100%;height:390px;margin-top:-12px;overflow:visible}.scale{text-align:center;font-size:12px;color:#767565}.bloodlines{display:grid;gap:11px;margin-top:15px}.bloodline{display:flex;align-items:center;gap:14px;padding:12px;background:linear-gradient(100deg,#1e2624,#363b34);border:1px solid #b59961;box-shadow:0 0 0 2px #292f29;color:#f2e5c5;min-height:68px}.blood-icon{width:40px;height:40px;border:1px solid #b79a59;display:grid;place-items:center;color:#ddc180;font:26px KaiTi,serif;flex-shrink:0}.blood-name{font-size:21px;flex:1;overflow-wrap:anywhere}.quality{min-width:54px;padding:6px 9px;text-align:center;border:1px solid currentColor;font:bold 27px Georgia,serif;background:#111916;box-shadow:inset 0 0 10px #ffffff12}.empty{text-align:center;padding:25px;color:#6d7266;font-size:18px}.foot{font-size:13px;text-align:center;margin-top:18px;color:#626454;letter-spacing:1px}
134
- </style><body><main id="capture">${paper ? `<img class="paper" src="${escape3(paper)}" alt="">` : ""}<div class="content"><div class="top">
135
- <div class="portrait">${portrait ? `<img class="character-artwork" src="${escape3(portrait)}" data-fallback="${escape3(defaultArtwork ?? "")}" alt="角色形象">` : '<div class="placeholder"><div class="seal">轮回</div><small>形象尚未生成</small></div>'}</div>
136
- <div class="info"><div class="dark title">✦ 角色档案</div><div class="identity"><div class="eyebrow">轮回者</div><div class="identity-row"><h1 class="character-name">${escape3(card.name)}</h1><span class="level">Lv. ${number(card.level)}</span></div><div class="experience-label">升级进度 ${number(exp)}%</div><div class="experience"><span style="width:${exp}%"></span></div></div>
137
- <div class="dark"><div class="power-label">⚔ 综合战力</div><div class="power-value ${number(card.power).length > 11 ? "long" : ""}">${number(card.power)}</div></div>
138
- <div class="dark health"><div class="health-label">✚ 生命</div><div class="bar"><div class="fill" style="width:${hpPercent}%"></div><b>${number(card.hp)} / ${number(card.maxHp)}</b></div></div></div></div>
367
+ </style><body><main id="capture">${paper ? `<img class="paper" src="${escape(paper)}" alt="">` : ""}<div class="content"><div class="top">
368
+ <div class="portrait">${portrait ? `<img class="character-artwork" src="${escape(portrait)}" data-fallback="${escape(defaultArtwork ?? "")}" alt="角色形象">` : '<div class="placeholder"><div class="seal">轮回</div><small>形象尚未生成</small></div>'}</div>
369
+ <div class="info"><div class="dark title">✦ 角色档案</div><div class="identity"><div class="eyebrow">轮回者</div><div class="identity-row"><h1 class="character-name">${escape(card.name)}</h1><span class="level">Lv. ${number2(card.level)}</span></div><div class="experience-label">升级进度 ${number2(exp)}%</div><div class="experience"><span style="width:${exp}%"></span></div></div>
370
+ <div class="dark"><div class="power-label">⚔ 综合战力</div><div class="power-value ${formatCombatPower(finite(card.power)).length > 11 ? "long" : ""}">${formatCombatPower(finite(card.power))}</div></div>
371
+ <div class="dark health"><div class="health-label">✚ 生命</div><div class="bar"><div class="fill" style="width:${hpPercent}%"></div><b>${number2(card.hp)} / ${number2(card.maxHp)}</b></div></div></div></div>
139
372
  <section class="panel"><h2 class="heading">一级属性</h2>${radar(card.attributes)}</section>
140
- <section class="panel"><h2 class="heading">血统</h2><div class="bloodlines">${card.bloodlines.length ? card.bloodlines.map((b) => `<div class="bloodline"><span class="blood-icon">血</span><span class="blood-name">${escape3(b.name)}</span><span class="quality" style="color:${qualityColors[b.quality] ?? "#bdbbae"}">${escape3(b.quality)}</span></div>`).join("") : '<div class="empty">尚未融合血统</div>'}</div></section>
373
+ <section class="panel"><h2 class="heading">血统</h2><div class="bloodlines">${card.bloodlines.length ? card.bloodlines.map((b) => `<div class="bloodline"><span class="blood-icon">血</span><span class="blood-name">${escape(b.name)}</span><span class="quality" style="color:${qualityColors[b.quality] ?? "#bdbbae"}">${escape(b.quality)}</span></div>`).join("") : '<div class="empty">尚未融合血统</div>'}</div></section>
141
374
  <div class="foot">[详细属性] 查看二级属性 · [指令] 查看操作</div></div></main></body></html>`;
142
375
  }
143
376
  __name(characterCardHtml, "characterCardHtml");
@@ -170,8 +403,13 @@ async function layoutCard(document = globalThis.document) {
170
403
  }
171
404
  }
172
405
  }
173
- const itemArtwork = document.querySelector(".item-artwork");
174
- if (itemArtwork) await itemArtwork.decode();
406
+ for (const itemArtwork of document.querySelectorAll(".item-artwork,.detail-portrait")) {
407
+ try {
408
+ await itemArtwork.decode();
409
+ } catch {
410
+ itemArtwork.remove();
411
+ }
412
+ }
175
413
  const artwork = document.querySelector(".skill-artwork");
176
414
  if (artwork) {
177
415
  try {
@@ -195,7 +433,7 @@ async function layoutCard(document = globalThis.document) {
195
433
  if (avatar.scrollWidth <= avatar.clientWidth) break;
196
434
  }
197
435
  }
198
- if (document.getElementById("capture")?.classList.contains("skill-sheet")) return;
436
+ if (document.getElementById("capture")?.classList.contains("skill-sheet") || document.getElementById("capture")?.classList.contains("detail-sheet")) return;
199
437
  const name2 = document.getElementById("name");
200
438
  if (!name2) return;
201
439
  for (let size = 36; size >= 20; size--) {
@@ -216,10 +454,10 @@ async function layoutCard(document = globalThis.document) {
216
454
  __name(layoutCard, "layoutCard");
217
455
 
218
456
  // src/plugin/leaderboardTemplate.ts
219
- var escape4 = /* @__PURE__ */ __name((s) => String(s ?? "").replace(/[&<>"']/g, (c) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;" })[c]), "escape");
457
+ var escape2 = /* @__PURE__ */ __name((s) => String(s ?? "").replace(/[&<>"']/g, (c) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;" })[c]), "escape");
220
458
  function leaderboardHtml(data, _headerImage, backgroundImage) {
221
459
  const rows = data.rows.slice(0, 20);
222
- const row = /* @__PURE__ */ __name((r, mine = false) => `<div class="row rank-${r.rank} ${mine ? "personal" : ""}"><div class="rank">${mine ? "<small>我的排名</small>" : ""}<b>${escape4(r.rank)}</b></div><div class="name">${escape4(r.name)}</div><div class="value">${escape4(r.value.toLocaleString("zh-CN", { maximumFractionDigits: 2 }))}</div></div>`, "row");
460
+ const row = /* @__PURE__ */ __name((r, mine = false) => `<div class="row rank-${r.rank} ${mine ? "personal" : ""}"><div class="rank">${mine ? "<small>我的排名</small>" : ""}<b>${escape2(r.rank)}</b></div><div class="name">${escape2(r.name)}</div><div class="value">${escape2(data.metric === "战力" ? formatCombatPower(r.value) : r.value.toLocaleString("zh-CN", { maximumFractionDigits: 2 }))}</div></div>`, "row");
223
461
  const personal = data.mine ? row(data.mine, true) : "";
224
462
  return `<!doctype html><html lang="zh-CN"><meta charset="utf-8"><style>
225
463
  *{box-sizing:border-box}body{margin:0;width:760px;color:#242725;font-family:"Microsoft YaHei","Noto Sans CJK SC",sans-serif}
@@ -237,7 +475,7 @@ function leaderboardHtml(data, _headerImage, backgroundImage) {
237
475
  .value{font-size:23px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
238
476
  #capture.has-background{padding:90px 80px}.has-background .sheet{background:rgba(250,247,238,.20);border:0}.has-background .row{grid-template-columns:48px minmax(0,1fr) 142px;gap:6px;padding:10px 6px}.has-background .name{font-size:20px}
239
477
  .has-background .row{background-color:rgba(255,253,246,.38)}
240
- </style><body><div id="capture" class="${backgroundImage ? "has-background" : ""}">${backgroundImage ? '<img class="board-background" alt="" src="' + escape4(backgroundImage) + '">' : ""}<h1 class="board-title">${escape4(data.title)}</h1><main class="sheet">${rows.length ? rows.map((r) => row(r)).join("") : '<div class="empty">暂无玩家上榜</div>'}${personal}</main></div></body></html>`;
478
+ </style><body><div id="capture" class="${backgroundImage ? "has-background" : ""}">${backgroundImage ? '<img class="board-background" alt="" src="' + escape2(backgroundImage) + '">' : ""}<h1 class="board-title">${escape2(data.title)}</h1><main class="sheet">${rows.length ? rows.map((r) => row(r)).join("") : '<div class="empty">暂无玩家上榜</div>'}${personal}</main></div></body></html>`;
241
479
  }
242
480
  __name(leaderboardHtml, "leaderboardHtml");
243
481
 
@@ -246,53 +484,82 @@ var import_promises = __toESM(require("node:fs/promises"));
246
484
  var import_node_path = __toESM(require("node:path"));
247
485
 
248
486
  // src/plugin/skillCardTemplate.ts
249
- var escape5 = /* @__PURE__ */ __name((value) => String(value ?? "").replace(/[&<>"']/g, (c) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;" })[c]), "escape");
250
- function skillCardHtml(card, _frame, artwork) {
251
- const description = escape5((card.description || "暂无技能效果说明").replace(/\s*\n+\s*/g, ""));
252
- 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>');
253
- return `<!doctype html><html><head><meta charset="utf-8"><style>
254
- *{box-sizing:border-box}
255
- html,body{margin:0;width:760px;background:#f1eee5;font-family:'Microsoft YaHei','Noto Sans CJK SC',sans-serif;color:#34372f}
256
- #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}
257
- #capture:before{content:'';position:absolute;inset:0;z-index:-1;background-image:url('${grain}');opacity:.42;pointer-events:none}
258
- #capture:after{content:'';position:absolute;inset:17px;border:1px solid #8c8b6f22;pointer-events:none}
259
- 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}
260
- .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}
261
- .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}
262
- .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}
263
- .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}
264
- </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>`;
487
+ var escape3 = /* @__PURE__ */ __name((value) => String(value ?? "").replace(/[&<>"']/g, (c) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;" })[c]), "escape");
488
+ function effectText(value) {
489
+ return value.split(/(\d+(?:\.\d+)?%?|真实伤害|魔法伤害|物理伤害|全能护盾|魔法护盾|不可闪避|不暴击)/g).map((part) => /^\d+(?:\.\d+)?%?$/.test(part) ? `<span class="number">${escape3(part)}</span>` : /^(真实伤害|魔法伤害|物理伤害|全能护盾|魔法护盾|不可闪避|不暴击)$/.test(part) ? `<span class="keyword">${escape3(part)}</span>` : escape3(part)).join("");
490
+ }
491
+ __name(effectText, "effectText");
492
+ function statValue(value) {
493
+ const match = value.match(/^([+-]?\d+(?:\.\d+)?%?)\s*(.*)$/);
494
+ return match ? `<span class="stat-number">${escape3(match[1])}</span>${match[2] ? `<small class="stat-unit">${escape3(match[2])}</small>` : ""}` : escape3(value);
495
+ }
496
+ __name(statValue, "statValue");
497
+ function tagClass(tag) {
498
+ if (/永久成长/.test(tag)) return "growth";
499
+ if (/护盾|防御/.test(tag)) return "shield";
500
+ if (/治疗|恢复/.test(tag)) return "heal";
501
+ if (/伤害|反击/.test(tag)) return "damage";
502
+ if (/被动/.test(tag)) return "passive";
503
+ return "";
504
+ }
505
+ __name(tagClass, "tagClass");
506
+ function skillCardHtml(card, _frame = "", artwork) {
507
+ const quality = normalizeCardQuality(card.quality);
508
+ const description = card.description?.trim() || "暂无技能效果说明";
509
+ const growthMarker = /永久成长[((]仅玩家[))]\s*[::]/.exec(description);
510
+ const effect = growthMarker ? description.slice(0, growthMarker.index).trim() : description;
511
+ const growth = growthMarker ? description.slice(growthMarker.index + growthMarker[0].length).trim() : "";
512
+ const notes = (card.note || "").split(/\n+/).filter(Boolean);
513
+ const power = notes.find((line) => /^(?:🔥\s*)?战力\s*[::]/.test(line));
514
+ const otherNotes = notes.filter((line) => line !== power).join("\n");
515
+ const tags = [...new Set(card.tags || [])];
516
+ const row = /* @__PURE__ */ __name((label, value, detail = false) => `<div class="stat-row${detail ? " detail-stat" : ""}"><span>${escape3(label)}</span><b${detail ? "" : ' class="stat-value"'}>${value}</b></div>`, "row");
517
+ const cooldown = /^([\d.]+)\s*回合$/.exec(card.cooldown || "");
518
+ const cooldownValue = cooldown ? Number(cooldown[1]) === 0 ? "无冷却" : `${escape3(cooldown[1])} <small class="param-unit">回合</small>` : escape3(card.cooldown || "无冷却");
519
+ return `<!doctype html><html lang="zh-CN"><head><meta charset="utf-8"><style>${qualityCardStyles}</style></head><body>
520
+ <div id="capture" class="skill-sheet"><article class="detail-card skill-card quality-${quality.toLowerCase()}" style="${qualityStyle(quality)}">
521
+ <i class="card-corner corner-tl" aria-hidden="true"></i><i class="card-corner corner-tr" aria-hidden="true"></i><i class="card-corner corner-bl" aria-hidden="true"></i><i class="card-corner corner-br" aria-hidden="true"></i>
522
+ <div class="card-inner"><header class="card-titlebar"><span class="title-glyph" aria-hidden="true">◇</span><h1>${escape3(card.name)}</h1><span class="title-star" aria-hidden="true">✦</span></header>
523
+ <div class="card-hero"><div class="hero-copy"><div class="hero-meta"><span class="skill-type">${card.passive ? "被动技能" : "主动技能"}</span><span class="quality-badge"><b>${quality}</b> 级</span></div><div class="tags">${tags.map((tag) => `<span class="tag ${tagClass(tag)}">${escape3(tag)}</span>`).join("")}</div></div><div class="hero-icon-wrap">${qualityMagicCircle(quality)}<div class="art-fallback" aria-hidden="true">${card.passive ? "静" : "势"}</div>${artwork ? `<img class="skill-artwork" alt="${escape3(card.name)}图标" src="${escape3(artwork)}">` : ""}${qualityIconFrame(quality)}</div></div>
524
+ <div class="card-body"><div class="effect-stats">
525
+ ${row("技能等级", `${escape3(card.level)} <small class="param-unit">/ ${escape3(card.maxLevel)}</small>`, true)}
526
+ ${row("消耗", escape3(card.cost || "无消耗"), true)}
527
+ ${row("冷却", cooldownValue, true)}
528
+ ${power ? row("战力", escape3(power.replace(/^(?:🔥\s*)?战力\s*[::]\s*/, "")), true) : ""}
529
+ ${(card.stats || []).slice(0, 3).map((stat) => row(stat.label, statValue(stat.value))).join("")}
530
+ </div><section class="effect"><h2 class="section-heading">技能效果</h2><p class="description">${effectText(effect)}</p></section>
531
+ ${growth ? `<section class="growth-section"><h2 class="section-heading">永久成长 · 仅玩家</h2><p class="description">${effectText(growth)}</p></section>` : ""}
532
+ ${otherNotes ? `<section class="note-section"><h2 class="section-heading">附加信息</h2><p class="description">${escape3(otherNotes)}</p></section>` : ""}
533
+ </div></div></article></div></body></html>`;
265
534
  }
266
535
  __name(skillCardHtml, "skillCardHtml");
267
536
 
268
537
  // src/plugin/equipmentCardTemplate.ts
269
- var escape6 = /* @__PURE__ */ __name((value) => String(value ?? "").replace(/[&<>"']/g, (c) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;" })[c]), "escape");
270
538
  function equipmentCardHtml(card, artwork, unknownFallback = false) {
271
- 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>');
272
- const power = Number.isFinite(Number(card.power)) ? Number(card.power).toLocaleString("en-US") : String(card.power ?? "—");
273
- const affixes = card.affixes?.length ? card.affixes.map((affix) => `<div class="affix"><span>${escape6(affix.name)}</span><strong>${escape6(affix.value)}</strong></div>`).join("") : '<p class="empty-line">暂无附加属性</p>';
274
- const skills = card.skills?.length ? `<section class="section skills"><h2>附带技能</h2>${card.skills.map((skill) => `<div class="skill"><span class="skill-name"><i aria-hidden="true"></i>${escape6(skill.name)}</span><span class="rank">${escape6(skill.rank)}</span></div>`).join("")}</section>` : "";
275
- const description = card.description?.trim() ? `<section class="section description"><h2>装备描述</h2><p>${escape6(card.description.trim())}</p></section>` : "";
276
- const notes = card.notes?.filter(Boolean).length ? `<div class="notes">${card.notes.filter(Boolean).map((note) => `<p>${escape6(note)}</p>`).join("")}</div>` : "";
277
- const enhancement = Number(card.enhancement) > 0 ? `<span class="enhance"><small>强化</small>+${escape6(card.enhancement)}</span>` : "";
278
- const price = card.referencePrice ? `<div class="price"><span>参考价格</span><strong>${escape6(card.referencePrice)}</strong></div>` : "";
279
- const author = `<span class="author"><small>打造者</small>${escape6(card.authorName?.trim() || "未知")}</span>`;
280
- return `<!doctype html><html><head><meta charset="utf-8"><style>
281
- *{box-sizing:border-box}
282
- html,body{margin:0;width:760px;background:#f1eee5;font-family:'Microsoft YaHei','Noto Sans CJK SC',sans-serif;color:#343b35}
283
- #capture{position:relative;isolation:isolate;width:760px;min-height:1100px;padding:57px 58px 34px;background:radial-gradient(ellipse at 0 0,#ddd9c744,transparent 48%),radial-gradient(ellipse at 100% 90%,#c9cdb938,transparent 45%),#f4f1e8;border:1px solid #dedbcf}
284
- #capture:before{content:'';position:absolute;inset:0;z-index:-1;background-image:url('${grain}');opacity:.42;pointer-events:none}
285
- #capture:after{content:'';position:absolute;inset:17px;border:1px solid #8c8b6f22;pointer-events:none}
286
- .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:14px}.name{font:700 43px/1.35 'KaiTi','STKaiti','Noto Serif CJK SC','SimSun',serif;margin:0;color:#303a31;overflow-wrap:anywhere}
287
- .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,'KaiTi',serif;letter-spacing:-1px}.seal span{font:16px 'KaiTi','SimSun',serif;letter-spacing:4px;padding-left:4px}
288
- .meta{display:flex;flex-wrap:wrap;align-items:baseline;gap:8px 26px;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 .slot{color:#a04b40;font:21px 'KaiTi','SimSun',serif;letter-spacing:2px}.meta .author{min-width:0;overflow-wrap:anywhere}.meta small{font-size:16px;color:#92917f;margin-right:9px}
289
- .art{position:relative;width:610px;height:610px;max-width:100%;margin:18px auto 11px;display:grid;place-items:center;isolation:isolate;overflow:hidden}.art:before{content:'';position:absolute;inset:4%;border-radius:48% 52% 56% 44%;background:radial-gradient(ellipse,#aab4a443,transparent 69%);transform:rotate(-12deg)}.equipment-artwork{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;object-position:center;filter:saturate(.72) contrast(.97);mix-blend-mode:multiply;mask-image:linear-gradient(to bottom,transparent 0,#000 8%,#000 84%,transparent 100%),linear-gradient(to right,transparent 0,#000 7%,#000 93%,transparent 100%);mask-composite:intersect}.art-fallback{position:relative;width:360px;height:360px;max-width:90%;display:flex;flex-direction:column;align-items:center;justify-content:center;color:#7d8b7d;border:1px solid #7c887d3b;border-radius:50%;background:radial-gradient(circle,#b1b9a435,transparent 70%);font-family:'KaiTi','SimSun',serif}.art-fallback:before,.art-fallback:after{content:'';position:absolute;inset:21px;border:1px solid #7c887d22;border-radius:50%}.art-fallback:after{inset:48px;border-color:#9a9e8533}.art-fallback .glyph{font-size:126px;line-height:1.2;opacity:.46}.art-fallback .kind{font-size:20px;letter-spacing:4px;opacity:.55}.art:has(.equipment-artwork) .art-fallback{visibility:hidden}.placeholder-note{position:absolute;right:18px;bottom:16px;z-index:2;padding:5px 9px;background:#f4f1e8ce;border:1px solid #9d9b8755;color:#536055;font:18px 'KaiTi','SimSun',serif;letter-spacing:2px}
290
- .power{display:flex;align-items:center;justify-content:space-between;gap:20px;padding:18px 0 22px;border-top:1px solid #8a8a7128;border-bottom:1px solid #8a8a7144;color:#31463e}.power span{font:24px 'KaiTi','SimSun',serif;letter-spacing:12px}.power strong{font:700 45px/1.1 Georgia,'Times New Roman',serif;letter-spacing:2px;font-variant-numeric:lining-nums}
291
- .section{margin-top:29px}.section h2{display:flex;align-items:center;gap:16px;margin:0 0 17px;color:#626e5e;font:21px 'KaiTi','SimSun',serif;letter-spacing:5px}.section h2:before{content:'';width:5px;height:17px;background:#a04b40;transform:rotate(8deg);opacity:.75}.section h2:after{content:'';height:1px;flex:1;background:linear-gradient(to right,#7f897c55,transparent)}
292
- .affixes{display:grid;grid-template-columns:1fr 1fr;column-gap:26px}.affix{display:flex;align-items:baseline;justify-content:space-between;gap:10px;min-width:0;padding:9px 0 12px;border-bottom:1px solid #8a8a7124;font-size:18px;color:#697064}.affix span{overflow-wrap:anywhere}.affix strong{flex-shrink:0;font:700 22px Georgia,'KaiTi',serif;color:#344b42}.empty-line{font-size:18px;color:#92917f;margin:4px 0 12px}
293
- .skill{display:flex;align-items:baseline;justify-content:space-between;gap:18px;padding:5px 0 10px;font-size:20px;color:#304b40}.skill-name{font-weight:700;overflow-wrap:anywhere}.skill-name i{display:inline-block;width:13px;height:13px;margin:0 17px 1px 4px;background:#a04b40;transform:rotate(45deg)}.rank{font:18px Georgia,'KaiTi',serif;color:#7f8d7b;white-space:nowrap}
294
- .description p{margin:0;font:22px/1.75 'KaiTi','STKaiti','Noto Serif CJK SC','SimSun',serif;white-space:pre-wrap;overflow-wrap:anywhere;color:#313f38}.price{display:flex;align-items:baseline;gap:16px;margin-top:24px;padding-top:17px;border-top:1px solid #8a8a7128;font-size:18px;color:#8b907d}.price strong{font-size:18px;color:#354c42}.notes{margin-top:17px;padding:10px 15px;border-left:3px solid #a04b4080;background:#e8e8db70;color:#3e5549}.notes p{margin:5px 0;font-size:17px;line-height:1.6;overflow-wrap:anywhere}.edition{display:flex;align-items:center;gap:12px;justify-content:center;margin-top:34px;color:#a6a38e;font:14px 'KaiTi','SimSun',serif;letter-spacing:5px}.edition:before,.edition:after{content:'';height:1px;width:32px;background:#9b9c833d}
295
- </style></head><body><div id="capture" class="equipment-sheet ink-equipment-sheet"><header><div class="heading"><div class="heading-copy"><div class="overline">装 备 图 录</div><h1 class="name">${escape6(card.name)}</h1></div><div class="seal"><strong>${escape6(card.quality)}</strong><span>品阶</span></div></div><div class="meta"><span class="slot">${escape6(card.slot)}</span>${enhancement}${author}</div></header><div class="art"><div class="art-fallback"><span class="glyph">?</span><span class="kind">未知外观</span></div>${artwork ? `<img class="skill-artwork equipment-artwork" alt="" src="${escape6(artwork)}">` : ""}${unknownFallback || !artwork ? '<span class="placeholder-note">外观暂未显现</span>' : ""}</div><div class="power"><span>装 备 战 力</span><strong>${escape6(power)}</strong></div><section class="section attributes"><h2>附加属性</h2><div class="affixes">${affixes}</div></section>${skills}${description}${price}${notes}<div class="edition">露 · 装备录</div></div></body></html>`;
539
+ const power = Number.isFinite(Number(card.power)) ? formatCombatPower(Number(card.power)) : String(card.power ?? "—");
540
+ const enhancement = Number(card.enhancement) > 0 ? "+" + String(card.enhancement) : "未强化";
541
+ const details = [
542
+ cardStatRow("装备战力", power, true),
543
+ cardStatRow("装备部位", card.slot || "未知"),
544
+ cardStatRow("强化等级", enhancement),
545
+ cardStatRow("打造者", card.authorName?.trim() || "未知"),
546
+ ...card.referencePrice ? [cardStatRow("参考价格", card.referencePrice)] : []
547
+ ].join("");
548
+ const affixes = card.affixes?.length ? '<div class="detail-list">' + card.affixes.map((affix) => cardStatRow(affix.name, affix.value)).join("") + "</div>" : '<p class="description">暂无附加属性</p>';
549
+ const skills = card.skills?.length ? cardSection("附带技能", '<div class="detail-list">' + card.skills.map((skill) => cardStatRow(skill.name, skill.rank)).join("") + "</div>") : "";
550
+ const description = card.description?.trim() ? cardSection("装备描述", '<p class="description">' + cardEscape(card.description.trim()) + "</p>") : "";
551
+ const notes = card.notes?.filter((note) => note?.trim()).length ? cardSection("附加信息", '<p class="description">' + card.notes.filter((note) => note?.trim()).map((note) => cardEscape(note)).join("\n") + "</p>") : "";
552
+ return detailCardHtml({
553
+ name: card.name,
554
+ quality: card.quality,
555
+ type: "装备",
556
+ artwork,
557
+ artworkClass: "skill-artwork equipment-artwork",
558
+ fallbackGlyph: "?",
559
+ sheetClass: "equipment-sheet",
560
+ tags: unknownFallback || !artwork ? ["外观暂未显现"] : [],
561
+ body: '<div class="effect-stats">' + details + "</div>" + cardSection("附加属性", affixes) + skills + description + notes
562
+ });
296
563
  }
297
564
  __name(equipmentCardHtml, "equipmentCardHtml");
298
565
 
@@ -401,9 +668,9 @@ var staticAssets = /* @__PURE__ */ new Map();
401
668
  async function staticAsset(folder, file) {
402
669
  const key = `${folder}/${file}`;
403
670
  if (!staticAssets.has(key)) staticAssets.set(key, (async () => {
404
- for (const base of [`../assets/${folder}`, `../../assets/${folder}`]) {
671
+ for (const base2 of [`../assets/${folder}`, `../../assets/${folder}`]) {
405
672
  try {
406
- return "data:image/webp;base64," + (await import_promises.default.readFile(import_node_path.default.resolve(__dirname, base, file))).toString("base64");
673
+ return "data:image/webp;base64," + (await import_promises.default.readFile(import_node_path.default.resolve(__dirname, base2, file))).toString("base64");
407
674
  } catch {
408
675
  }
409
676
  }
@@ -416,23 +683,12 @@ async function staticAsset(folder, file) {
416
683
  }
417
684
  __name(staticAsset, "staticAsset");
418
685
  var characterAsset = /* @__PURE__ */ __name((file) => staticAsset("character-card", file), "characterAsset");
419
- var itemPaper = /* @__PURE__ */ __name(() => staticAsset("item-card", "paper.webp"), "itemPaper");
420
686
  var equipmentFallback = /* @__PURE__ */ __name(() => staticAsset("equipment-card", "unknown.webp"), "equipmentFallback");
421
- var frames = /* @__PURE__ */ new Map();
422
- async function frame(passive) {
423
- const file = passive ? "card-trap.png" : "card-spell.png";
424
- if (!frames.has(file)) frames.set(file, import_promises.default.readFile(import_node_path.default.resolve(__dirname, "../assets/skill-card", file)).catch(() => import_promises.default.readFile(import_node_path.default.resolve(__dirname, "../../assets/skill-card", file))).then((b) => "data:image/png;base64," + b.toString("base64")).catch((e) => {
425
- frames.delete(file);
426
- throw e;
427
- }));
428
- return frames.get(file);
429
- }
430
- __name(frame, "frame");
431
687
  async function leaderboardHeader() {
432
- for (const base of ["../assets/leaderboard", "../../assets/leaderboard"]) {
688
+ for (const base2 of ["../assets/leaderboard", "../../assets/leaderboard"]) {
433
689
  for (const [file, mime] of [["header.png", "image/png"], ["header.jpg", "image/jpeg"], ["header.webp", "image/webp"], ["header.svg", "image/svg+xml"]]) {
434
690
  try {
435
- return "data:" + mime + ";base64," + (await import_promises.default.readFile(import_node_path.default.resolve(__dirname, base, file))).toString("base64");
691
+ return "data:" + mime + ";base64," + (await import_promises.default.readFile(import_node_path.default.resolve(__dirname, base2, file))).toString("base64");
436
692
  } catch (error) {
437
693
  if (error.code !== "ENOENT") throw error;
438
694
  }
@@ -522,7 +778,6 @@ async function renderUncached(ctx, card, leaderboard, itemCard, characterCard, e
522
778
  const dedicatedArt = equipmentCard ? await loadSkillArtwork(equipmentCard.artworkUrl) : void 0;
523
779
  const unknownArt = equipmentCard && !dedicatedArt ? await equipmentFallback() : void 0;
524
780
  const art = equipmentCard ? dedicatedArt ?? unknownArt : await loadSkillArtwork(characterCard?.artworkUrl ?? itemCard?.artworkUrl ?? leaderboard?.backgroundUrl ?? card?.artworkUrl);
525
- if (itemCard && !equipmentCard && !art) throw new Error("道具图片读取失败");
526
781
  let html;
527
782
  let localAssetsReady = true;
528
783
  if (equipmentCard) {
@@ -533,13 +788,11 @@ async function renderUncached(ctx, card, leaderboard, itemCard, characterCard, e
533
788
  localAssetsReady = Boolean(paper && (art || defaultArtwork));
534
789
  html = characterCardHtml(characterCard, art, paper, defaultArtwork);
535
790
  } else if (itemCard) {
536
- const paper = itemCard.type === "形象卡" ? await characterAsset("paper.webp") : await itemPaper();
537
- localAssetsReady = Boolean(paper);
538
- html = itemCard.type === "形象卡" ? appearanceCardHtml(itemCard, art, paper) : itemCardHtml(itemCard, art, paper);
791
+ html = itemCard.type === "形象卡" ? appearanceCardHtml(itemCard, art) : itemCardHtml(itemCard, art);
539
792
  } else if (leaderboard) {
540
793
  html = leaderboardHtml(leaderboard, await leaderboardHeader(), art);
541
794
  } else {
542
- html = skillCardHtml(card, await frame(card.passive), art);
795
+ html = skillCardHtml(card, "", art);
543
796
  }
544
797
  if (expired) throw new Error("绘图超时");
545
798
  page = await ctx.puppeteer.page();
@@ -639,8 +892,8 @@ var handel_base = class {
639
892
  this.ctx.logger.info("session not found", data.userId, data.channelId);
640
893
  return;
641
894
  }
642
- let finalContent = content;
643
- if (messageType === "text" && session.event?.platform === "qq" && session.event?.channel?.type === 0) {
895
+ let finalContent = data.notification ? content.trimEnd() : content;
896
+ if (messageType === "text" && !data.notification && session.event?.platform === "qq" && session.event?.channel?.type === 0) {
644
897
  finalContent = "Ciallo~(∠・ω< )\n" + content;
645
898
  }
646
899
  const sendStartedAt = this.timing ? performance.now() : 0;
@@ -655,40 +908,6 @@ var handel_base = class {
655
908
  }
656
909
  };
657
910
 
658
- // src/shared/bot/analysisMsg.ts
659
- var AnalysisMsg = class {
660
- static {
661
- __name(this, "AnalysisMsg");
662
- }
663
- parse(data) {
664
- let msg = "";
665
- let haveTitle = false;
666
- for (const item of data.line) {
667
- switch (item.type) {
668
- case 0 /* title */:
669
- haveTitle = true;
670
- msg += `┄━═${item.content}═━┄
671
- `;
672
- break;
673
- case 1 /* line */:
674
- msg += `${item.content}
675
- `;
676
- break;
677
- case 2 /* error */:
678
- msg += `❌出错了!错误原因:${item.content},请反馈至联系开发者
679
- `;
680
- break;
681
- default:
682
- break;
683
- }
684
- }
685
- if (haveTitle) {
686
- msg += "┄━════════━┄";
687
- }
688
- return msg;
689
- }
690
- };
691
-
692
911
  // src/plugin/serverHandel/qqMarkdownHelpers.ts
693
912
  function escapeMarkdown(value) {
694
913
  return value.replace(/[\\`*_{}\[\]()#+\-.!|>~<&]/g, "\\$&");
@@ -728,19 +947,16 @@ function renderRewardLine(content) {
728
947
  }
729
948
  __name(renderRewardLine, "renderRewardLine");
730
949
  function renderQQRewardMarkdown(data, showGreeting) {
950
+ const rewards = data.line.filter((item) => item.type === 1 /* line */ && item.reward).map((item) => renderRewardLine(item.content)).filter(Boolean);
951
+ if (rewards.length) return "🎁获得奖励\n\n" + rewards.join(" \n");
731
952
  const sections = [];
732
953
  const bodyLines = [];
733
- const rewardLines = [];
734
954
  const flushBody = /* @__PURE__ */ __name(() => {
735
955
  if (bodyLines.length) sections.push(bodyLines.splice(0).join(" \n"));
736
956
  }, "flushBody");
737
957
  if (showGreeting && !data.reward) sections.push(escapeMarkdown("Ciallo~(∠・ω< )"));
738
958
  for (const item of data.line) {
739
- if (item.type === 1 /* line */ && item.reward) {
740
- const reward = renderRewardLine(item.content);
741
- if (reward) rewardLines.push(reward);
742
- continue;
743
- }
959
+ if (item.type === 1 /* line */ && item.reward) continue;
744
960
  if (item.type === 0 /* title */) {
745
961
  flushBody();
746
962
  bodyLines.push(renderRewardTitle(item.content));
@@ -754,7 +970,6 @@ function renderQQRewardMarkdown(data, showGreeting) {
754
970
  }
755
971
  }
756
972
  flushBody();
757
- if (rewardLines.length) sections.push(rewardLines.join(" \n"));
758
973
  return sections.join("\n\n") || " ";
759
974
  }
760
975
  __name(renderQQRewardMarkdown, "renderQQRewardMarkdown");
@@ -813,14 +1028,126 @@ function pageCommandLines(nav) {
813
1028
  ];
814
1029
  }
815
1030
  __name(pageCommandLines, "pageCommandLines");
1031
+ function renderQQTaskMarkdown(data) {
1032
+ const nav = data.pageNav;
1033
+ const entryCommands = new Map(nav?.entries?.map((entry) => [entry.lineIndex, entry.viewCommand]) ?? []);
1034
+ const sections = [];
1035
+ const rewards = [];
1036
+ let quote;
1037
+ const renderQuote = /* @__PURE__ */ __name((content) => content.replace(/\r\n?/g, "\n").split("\n").map((line) => "> " + renderInlineCommands(line)).join(" \n"), "renderQuote");
1038
+ const flushRewards = /* @__PURE__ */ __name(() => {
1039
+ if (rewards.length) sections.push(rewards.splice(0).join(" \n"));
1040
+ }, "flushRewards");
1041
+ for (const [lineIndex, item] of data.line.entries()) {
1042
+ if (item.type === 0 /* title */) {
1043
+ flushRewards();
1044
+ sections.push(renderRewardTitle(item.content));
1045
+ } else if (item.type === 2 /* error */) {
1046
+ flushRewards();
1047
+ sections.push("❌ " + renderInlineCommands(item.content));
1048
+ } else if (quote === "description") {
1049
+ sections.push(renderQuote(item.content));
1050
+ quote = void 0;
1051
+ } else if (item.reward) {
1052
+ const reward = renderRewardLine(item.content);
1053
+ if (reward) rewards.push(reward);
1054
+ } else if (item.content === "ℹ️任务说明" || item.content === "🎁任务奖励") {
1055
+ flushRewards();
1056
+ sections.push(renderRewardTitle(item.content));
1057
+ quote = item.content === "ℹ️任务说明" ? "description" : "rewards";
1058
+ } else if (/^(?:⌛任务进度[■□]{10}|✅任务完成)$/.test(item.content)) {
1059
+ flushRewards();
1060
+ sections.push(item.content.startsWith("✅任务完成") ? "**✅** " + escapeMarkdown(item.content.slice("✅".length)) : renderRewardTitle(item.content));
1061
+ quote = void 0;
1062
+ } else if (quote === "rewards") {
1063
+ rewards.push(renderQuote(item.content));
1064
+ } else {
1065
+ flushRewards();
1066
+ const command = entryCommands.get(lineIndex);
1067
+ sections.push(command ? "[" + escapeMarkdown(item.content) + "](" + inlineCommandUrl(command) + ")" : renderInlineCommands(item.content));
1068
+ }
1069
+ }
1070
+ flushRewards();
1071
+ if (nav && (nav.total > 1 || nav.previousCommand || nav.nextCommand)) {
1072
+ const previous = nav.previousCommand ? "[上一个](" + inlineCommandUrl(nav.previousCommand) + ")" : "";
1073
+ const next = nav.nextCommand ? "[下一个](" + inlineCommandUrl(nav.nextCommand) + ")" : "";
1074
+ sections.push(previous + "**(" + nav.current + "/" + nav.total + ")**" + next);
1075
+ }
1076
+ return sections.join("\n\n") || " ";
1077
+ }
1078
+ __name(renderQQTaskMarkdown, "renderQQTaskMarkdown");
1079
+ var inventoryFilterCategories = [
1080
+ { name: "装备", emoji: "⚔️" },
1081
+ { name: "装备图纸", emoji: "📐" },
1082
+ { name: "技能书", emoji: "📖" },
1083
+ { name: "消耗品", emoji: "🧪" },
1084
+ { name: "材料", emoji: "🧱" },
1085
+ { name: "血统道具", emoji: "🧬" },
1086
+ { name: "盲盒", emoji: "🎁" },
1087
+ { name: "宠物蛋", emoji: "🥚" },
1088
+ { name: "随从契约", emoji: "📜" },
1089
+ { name: "合成配方", emoji: "📋" },
1090
+ { name: "形象卡", emoji: "🎨" }
1091
+ ];
1092
+ function renderInventoryFilters(nav) {
1093
+ const filters = nav.filters ?? inventoryFilterCategories.map((category) => ({
1094
+ ...category,
1095
+ command: "搜索" + category.name
1096
+ }));
1097
+ const lines = [];
1098
+ for (let index = 0; index < filters.length; ) {
1099
+ const count = index === 0 ? 2 : 3;
1100
+ lines.push((index === 0 ? "🔍筛选" : "") + filters.slice(index, index + count).map(({ name: name2, emoji, command }) => `[${escapeMarkdown((emoji ?? "") + name2)}](${inlineCommandUrl(command)})`).join(""));
1101
+ index += count;
1102
+ }
1103
+ return lines.join(" \n");
1104
+ }
1105
+ __name(renderInventoryFilters, "renderInventoryFilters");
1106
+ function renderQQInventoryMarkdown(data, showGreeting) {
1107
+ const nav = data.pageNav;
1108
+ const entryCommands = new Map(nav.entries?.map((entry) => [entry.lineIndex, entry.viewCommand]) ?? []);
1109
+ const sections = [];
1110
+ const items = [];
1111
+ const flushItems = /* @__PURE__ */ __name(() => {
1112
+ if (items.length) sections.push(items.splice(0).map((line) => "> " + line).join(" \n"));
1113
+ }, "flushItems");
1114
+ if (showGreeting) sections.push(escapeMarkdown("Ciallo~(∠・ω< )"));
1115
+ for (const [lineIndex, item] of data.line.entries()) {
1116
+ const command = entryCommands.get(lineIndex);
1117
+ if (item.type === 1 /* line */ && command) {
1118
+ const label = compactListLabel(item.content.replace(/^\[\d+\]\s*/, ""));
1119
+ items.push(`[${escapeMarkdown(label)}](${inlineCommandUrl(command)})`);
1120
+ continue;
1121
+ }
1122
+ flushItems();
1123
+ if (item.type === 0 /* title */) {
1124
+ sections.push(renderQQTitle(item.content));
1125
+ const filters = renderInventoryFilters(nav);
1126
+ if (filters) sections.push(filters);
1127
+ } else if (item.type === 2 /* error */) {
1128
+ sections.push(`❌ 出错了!错误原因:${renderInlineCommands(item.content)},请反馈至联系开发者`);
1129
+ } else if (!/^第\s*\d+\s*\/\s*\d+\s*页(?:\s*[||]\s*共\s*\d+\s*[项条])?$/.test(item.content) && !/^\[指令\]相关指令$/.test(item.content) && item.content.trim() !== "[上一页] [下一页]") {
1130
+ sections.push(renderInlineCommands(item.content));
1131
+ }
1132
+ }
1133
+ flushItems();
1134
+ sections.push(`[上一页](${inlineCommandUrl(nav.previousCommand ?? "上一页")})**(${nav.current}/${nav.total})**[下一页](${inlineCommandUrl(nav.nextCommand ?? "下一页")})`);
1135
+ return sections.join("\n\n");
1136
+ }
1137
+ __name(renderQQInventoryMarkdown, "renderQQInventoryMarkdown");
816
1138
  function renderQQPageMarkdown(data, showGreeting) {
1139
+ const hasEntryLabels = data.pageNav?.entries?.some((entry) => entry.label !== void 0) ?? false;
1140
+ if (data.task && !hasEntryLabels) return renderQQTaskMarkdown(data);
1141
+ if (!hasEntryLabels && data.pageNav && data.line.some((item) => item.type === 0 /* title */ && /^(?:💼)?我的背包$/u.test(compactDisplayText(item.content))))
1142
+ return renderQQInventoryMarkdown(data, showGreeting);
817
1143
  const rewardMessage = isRewardMessage(data);
818
1144
  const nav = data.pageNav;
819
1145
  const entryCommands = new Map(nav?.entries?.map((entry) => [entry.lineIndex, entry.viewCommand]) ?? []);
1146
+ const labeledEntries = new Map(nav?.entries?.filter((entry) => entry.label !== void 0).map((entry) => [entry.lineIndex, entry]) ?? []);
820
1147
  const listPattern = /^\[(\d+)\]\s*([^\r\n]+)$/;
821
1148
  const pagePattern = /^第\s*\d+\s*\/\s*\d+\s*页(?:\s*[||]\s*共\s*(\d+)\s*([项条]))?$/;
822
1149
  const title = data.line.find((item) => item.type === 0 /* title */);
823
- const isLeaderboardDirectory = !!nav?.entries?.length && /^(?:🏆)?排行榜$/u.test(compactDisplayText(title?.content ?? ""));
1150
+ const isLeaderboardDirectory = !hasEntryLabels && !!nav?.entries?.length && /^(?:🏆)?排行榜$/u.test(compactDisplayText(title?.content ?? ""));
824
1151
  const sections = [];
825
1152
  const bodyLines = [];
826
1153
  const listLines = [];
@@ -852,6 +1179,13 @@ function renderQQPageMarkdown(data, showGreeting) {
852
1179
  else sections.push(renderQQTitle(titleText));
853
1180
  break;
854
1181
  case 1 /* line */: {
1182
+ const labeledEntry = labeledEntries.get(lineIndex);
1183
+ if (labeledEntry) {
1184
+ flushList();
1185
+ flushRewards();
1186
+ bodyLines.push(`[${escapeMarkdown(labeledEntry.label)}](${inlineCommandUrl(labeledEntry.viewCommand)})` + (labeledEntry.description ? " " + escapeMarkdown(labeledEntry.description) : ""));
1187
+ break;
1188
+ }
855
1189
  if (item.reward) {
856
1190
  flushList();
857
1191
  flushBody();
@@ -915,11 +1249,6 @@ function canSendQQPage(session, data) {
915
1249
  return !!internal?.sendMessage && !!internal.sendPrivateMessage;
916
1250
  }
917
1251
  __name(canSendQQPage, "canSendQQPage");
918
- async function sendQQPage(session, data) {
919
- const markdown = renderQQPageMarkdown(data, session.event?.channel?.type === 0);
920
- await sendQQMarkdown(session, markdown);
921
- }
922
- __name(sendQQPage, "sendQQPage");
923
1252
  async function sendQQMarkdown(session, markdown) {
924
1253
  const internal = session.bot.internal;
925
1254
  const request = {
@@ -941,6 +1270,91 @@ async function sendQQMarkdown(session, markdown) {
941
1270
  }
942
1271
  __name(sendQQMarkdown, "sendQQMarkdown");
943
1272
 
1273
+ // src/shared/bot/analysisMsg.ts
1274
+ var AnalysisMsg = class {
1275
+ static {
1276
+ __name(this, "AnalysisMsg");
1277
+ }
1278
+ parse(data) {
1279
+ let msg = "";
1280
+ let haveTitle = false;
1281
+ const commands = new Map(data.pageNav?.entries?.map((entry) => [entry.lineIndex, entry.viewCommand]) ?? []);
1282
+ for (const [lineIndex, item] of data.line.entries()) {
1283
+ switch (item.type) {
1284
+ case 0 /* title */:
1285
+ haveTitle = true;
1286
+ msg += `┄━═${item.content}═━┄
1287
+ `;
1288
+ break;
1289
+ case 1 /* line */:
1290
+ const command = commands.get(lineIndex);
1291
+ const hint = command && !item.content.includes(`[${command}]`) && item.content.trim() !== command ? ` [${command}]` : "";
1292
+ msg += `${item.content}${hint}
1293
+ `;
1294
+ break;
1295
+ case 2 /* error */:
1296
+ msg += `❌出错了!错误原因:${item.content},请反馈至联系开发者
1297
+ `;
1298
+ break;
1299
+ default:
1300
+ break;
1301
+ }
1302
+ }
1303
+ if (haveTitle) {
1304
+ msg += "┄━════════━┄";
1305
+ }
1306
+ return msg;
1307
+ }
1308
+ };
1309
+
1310
+ // src/plugin/serverHandel/qqCommandMessage.ts
1311
+ function renderQQCommandMarkdown(data, showGreeting) {
1312
+ const rewardMessage = isRewardMessage(data);
1313
+ if (!data.commandHelp && !data.pageNav && data.line.some((item) => item.type === 1 /* line */ && !item.reward && /^\[\d+\]\s*[^\r\n]+$/.test(item.content))) {
1314
+ return renderQQPageMarkdown(data, showGreeting);
1315
+ }
1316
+ const sections = [];
1317
+ const bodyLines = [];
1318
+ const rewardLines = [];
1319
+ const flushBody = /* @__PURE__ */ __name(() => {
1320
+ if (bodyLines.length) sections.push(bodyLines.splice(0).join(" \n"));
1321
+ }, "flushBody");
1322
+ const flushRewards = /* @__PURE__ */ __name(() => {
1323
+ if (rewardLines.length) sections.push(rewardLines.splice(0).join(" \n"));
1324
+ }, "flushRewards");
1325
+ if (showGreeting && !data.reward) sections.push(escapeMarkdown("Ciallo~(∠・ω< )"));
1326
+ for (const item of data.line) {
1327
+ switch (item.type) {
1328
+ case 0 /* title */:
1329
+ flushBody();
1330
+ flushRewards();
1331
+ if (rewardMessage) bodyLines.push(renderRewardTitle(item.content));
1332
+ else sections.push(renderQQTitle(item.content));
1333
+ break;
1334
+ case 1 /* line */:
1335
+ if (item.reward) {
1336
+ flushBody();
1337
+ const reward = renderRewardLine(item.content);
1338
+ if (reward) rewardLines.push(reward);
1339
+ break;
1340
+ }
1341
+ flushRewards();
1342
+ if (!item.content.trim()) flushBody();
1343
+ else bodyLines.push(renderInlineCommands(item.content));
1344
+ break;
1345
+ case 2 /* error */:
1346
+ flushBody();
1347
+ flushRewards();
1348
+ sections.push(`❌ 出错了!错误原因:${renderInlineCommands(item.content)},请反馈至联系开发者`);
1349
+ break;
1350
+ }
1351
+ }
1352
+ flushBody();
1353
+ flushRewards();
1354
+ return sections.join("\n\n") || " ";
1355
+ }
1356
+ __name(renderQQCommandMarkdown, "renderQQCommandMarkdown");
1357
+
944
1358
  // src/plugin/serverHandel/qqTransactionMessage.ts
945
1359
  function renderQQTransactionMarkdown(data, showGreeting) {
946
1360
  const rewardMessage = isRewardMessage(data);
@@ -1007,62 +1421,6 @@ function renderQQTransactionMarkdown(data, showGreeting) {
1007
1421
  return sections.join("\n\n") || " ";
1008
1422
  }
1009
1423
  __name(renderQQTransactionMarkdown, "renderQQTransactionMarkdown");
1010
- async function sendQQTransaction(session, data) {
1011
- await sendQQMarkdown(session, renderQQTransactionMarkdown(data, session.event?.channel?.type === 0));
1012
- }
1013
- __name(sendQQTransaction, "sendQQTransaction");
1014
-
1015
- // src/plugin/serverHandel/qqCommandMessage.ts
1016
- function renderQQCommandMarkdown(data, showGreeting) {
1017
- const rewardMessage = isRewardMessage(data);
1018
- if (!data.commandHelp && !data.pageNav && data.line.some((item) => item.type === 1 /* line */ && !item.reward && /^\[\d+\]\s*[^\r\n]+$/.test(item.content))) {
1019
- return renderQQPageMarkdown(data, showGreeting);
1020
- }
1021
- const sections = [];
1022
- const bodyLines = [];
1023
- const rewardLines = [];
1024
- const flushBody = /* @__PURE__ */ __name(() => {
1025
- if (bodyLines.length) sections.push(bodyLines.splice(0).join(" \n"));
1026
- }, "flushBody");
1027
- const flushRewards = /* @__PURE__ */ __name(() => {
1028
- if (rewardLines.length) sections.push(rewardLines.splice(0).join(" \n"));
1029
- }, "flushRewards");
1030
- if (showGreeting && !data.reward) sections.push(escapeMarkdown("Ciallo~(∠・ω< )"));
1031
- for (const item of data.line) {
1032
- switch (item.type) {
1033
- case 0 /* title */:
1034
- flushBody();
1035
- flushRewards();
1036
- if (rewardMessage) bodyLines.push(renderRewardTitle(item.content));
1037
- else sections.push(renderQQTitle(item.content));
1038
- break;
1039
- case 1 /* line */:
1040
- if (item.reward) {
1041
- flushBody();
1042
- const reward = renderRewardLine(item.content);
1043
- if (reward) rewardLines.push(reward);
1044
- break;
1045
- }
1046
- flushRewards();
1047
- if (!item.content.trim()) flushBody();
1048
- else bodyLines.push(renderInlineCommands(item.content));
1049
- break;
1050
- case 2 /* error */:
1051
- flushBody();
1052
- flushRewards();
1053
- sections.push(`❌ 出错了!错误原因:${renderInlineCommands(item.content)},请反馈至联系开发者`);
1054
- break;
1055
- }
1056
- }
1057
- flushBody();
1058
- flushRewards();
1059
- return sections.join("\n\n") || " ";
1060
- }
1061
- __name(renderQQCommandMarkdown, "renderQQCommandMarkdown");
1062
- async function sendQQCommand(session, data) {
1063
- await sendQQMarkdown(session, renderQQCommandMarkdown(data, session.event?.channel?.type === 0));
1064
- }
1065
- __name(sendQQCommand, "sendQQCommand");
1066
1424
 
1067
1425
  // src/plugin/serverHandel/qqStoryMessage.ts
1068
1426
  function hasStoryCodeBlock(data) {
@@ -1077,6 +1435,10 @@ function fencedStory(content) {
1077
1435
  }
1078
1436
  __name(fencedStory, "fencedStory");
1079
1437
  function renderQQStoryMarkdown(data, showGreeting) {
1438
+ if (data.reward === true && data.line.some((item) => item.type === 1 /* line */ && item.reward === true && item.content.trim())) {
1439
+ const stories = data.line.filter((item) => item.type === 1 /* line */ && item.codeBlock === true).map((item) => fencedStory(item.content));
1440
+ return [...stories, renderQQRewardMarkdown(data, false)].join("\n\n");
1441
+ }
1080
1442
  const rewardMessage = isRewardMessage(data);
1081
1443
  const sections = [];
1082
1444
  const bodyLines = [];
@@ -1126,10 +1488,23 @@ function renderQQStoryMarkdown(data, showGreeting) {
1126
1488
  return sections.join("\n\n") || " ";
1127
1489
  }
1128
1490
  __name(renderQQStoryMarkdown, "renderQQStoryMarkdown");
1129
- async function sendQQStory(session, data) {
1130
- await sendQQMarkdown(session, renderQQStoryMarkdown(data, session.event?.channel?.type === 0));
1491
+
1492
+ // src/plugin/serverHandel/qqMessagePreview.ts
1493
+ function renderQQMessagePreview(data, showGreeting = false) {
1494
+ const plain = new AnalysisMsg().parse(data);
1495
+ const text = data.notification ? plain.trimEnd() : plain;
1496
+ if (data.notification) return { kind: "command", markdown: renderQQCommandMarkdown(data, false), text };
1497
+ if (hasStoryCodeBlock(data)) return { kind: "story", markdown: renderQQStoryMarkdown(data, showGreeting), text };
1498
+ if (data.commandHelp) return { kind: "command", markdown: renderQQCommandMarkdown(data, showGreeting), text };
1499
+ if (data.transaction) return { kind: "transaction", markdown: renderQQTransactionMarkdown(data, showGreeting), text };
1500
+ if (data.pageNav) return { kind: "page", markdown: renderQQPageMarkdown(data, showGreeting), text };
1501
+ if (isRewardMessage(data)) return { kind: "reward", markdown: renderQQRewardMarkdown(data, showGreeting), text };
1502
+ if (data.line.some((item) => (item.type === 1 /* line */ || item.type === 2 /* error */) && hasInlineCommands(item.content))) {
1503
+ return { kind: "command", markdown: renderQQCommandMarkdown(data, showGreeting), text };
1504
+ }
1505
+ return { kind: "text", text };
1131
1506
  }
1132
- __name(sendQQStory, "sendQQStory");
1507
+ __name(renderQQMessagePreview, "renderQQMessagePreview");
1133
1508
 
1134
1509
  // src/plugin/serverHandel/text.ts
1135
1510
  var handelr_text = class extends handel_base {
@@ -1137,20 +1512,13 @@ var handelr_text = class extends handel_base {
1137
1512
  __name(this, "handelr_text");
1138
1513
  }
1139
1514
  async start(data) {
1140
- const content = new AnalysisMsg().parse(data);
1141
1515
  const session = sessions_default.get(sessionKey(data.botId, data.userId, data.channelId));
1142
- const inlineCommands = data.line.some((item) => (item.type === 1 /* line */ || item.type === 2 /* error */) && hasInlineCommands(item.content));
1143
- const story = hasStoryCodeBlock(data);
1144
- const reward = isRewardMessage(data);
1145
- if ((story || data.commandHelp || data.transaction || data.pageNav || inlineCommands || reward) && canSendQQPage(session, data)) {
1516
+ const preview = renderQQMessagePreview(data, session?.event?.channel?.type === 0);
1517
+ const content = preview.text;
1518
+ if (preview.markdown !== void 0 && canSendQQPage(session, data)) {
1146
1519
  const sendStartedAt = this.timing ? performance.now() : 0;
1147
1520
  try {
1148
- if (story) await sendQQStory(session, data);
1149
- else if (data.commandHelp) await sendQQCommand(session, data);
1150
- else if (data.transaction) await sendQQTransaction(session, data);
1151
- else if (data.pageNav) await sendQQPage(session, data);
1152
- else if (reward) await sendQQMarkdown(session, renderQQRewardMarkdown(data, session.event?.channel?.type === 0));
1153
- else await sendQQCommand(session, data);
1521
+ await sendQQMarkdown(session, preview.markdown);
1154
1522
  if (this.timing) {
1155
1523
  this.timing.delivery = "qq-markdown";
1156
1524
  this.timing.sendMs = performance.now() - sendStartedAt;
@@ -1159,7 +1527,7 @@ var handelr_text = class extends handel_base {
1159
1527
  } catch (error) {
1160
1528
  this.ctx.logger.warn("QQ Markdown 发送失败,改用普通文字", error instanceof Error ? error.message : error);
1161
1529
  }
1162
- const commands = !story && !data.commandHelp && !data.transaction && data.pageNav ? pageCommandLines(data.pageNav) : [];
1530
+ const commands = preview.kind === "page" && data.pageNav ? pageCommandLines(data.pageNav) : [];
1163
1531
  await this.send(data, commands.length ? `${content.trimEnd()}
1164
1532
  ${commands.join("\n")}` : content);
1165
1533
  return;
@@ -1264,7 +1632,7 @@ var import_tsrpc = require("tsrpc");
1264
1632
 
1265
1633
  // src/shared/bot/serviceProto.ts
1266
1634
  var serviceProto = {
1267
- "version": 15,
1635
+ "version": 21,
1268
1636
  "services": [
1269
1637
  {
1270
1638
  "id": 0,
@@ -1399,6 +1767,14 @@ var serviceProto = {
1399
1767
  },
1400
1768
  "optional": true
1401
1769
  },
1770
+ {
1771
+ "id": 11,
1772
+ "name": "task",
1773
+ "type": {
1774
+ "type": "Boolean"
1775
+ },
1776
+ "optional": true
1777
+ },
1402
1778
  {
1403
1779
  "id": 8,
1404
1780
  "name": "transaction",
@@ -1423,6 +1799,14 @@ var serviceProto = {
1423
1799
  },
1424
1800
  "optional": true
1425
1801
  },
1802
+ {
1803
+ "id": 12,
1804
+ "name": "notification",
1805
+ "type": {
1806
+ "type": "Boolean"
1807
+ },
1808
+ "optional": true
1809
+ },
1426
1810
  {
1427
1811
  "id": 1,
1428
1812
  "name": "line",
@@ -1637,6 +2021,33 @@ var serviceProto = {
1637
2021
  "type": "String"
1638
2022
  }
1639
2023
  },
2024
+ {
2025
+ "id": 14,
2026
+ "name": "stats",
2027
+ "type": {
2028
+ "type": "Array",
2029
+ "elementType": {
2030
+ "type": "Interface",
2031
+ "properties": [
2032
+ {
2033
+ "id": 0,
2034
+ "name": "label",
2035
+ "type": {
2036
+ "type": "String"
2037
+ }
2038
+ },
2039
+ {
2040
+ "id": 1,
2041
+ "name": "value",
2042
+ "type": {
2043
+ "type": "String"
2044
+ }
2045
+ }
2046
+ ]
2047
+ }
2048
+ },
2049
+ "optional": true
2050
+ },
1640
2051
  {
1641
2052
  "id": 10,
1642
2053
  "name": "tags",
@@ -1674,7 +2085,8 @@ var serviceProto = {
1674
2085
  "name": "artworkUrl",
1675
2086
  "type": {
1676
2087
  "type": "String"
1677
- }
2088
+ },
2089
+ "optional": true
1678
2090
  },
1679
2091
  {
1680
2092
  "id": 1,
@@ -1717,6 +2129,41 @@ var serviceProto = {
1717
2129
  "type": {
1718
2130
  "type": "String"
1719
2131
  }
2132
+ },
2133
+ {
2134
+ "id": 7,
2135
+ "name": "rows",
2136
+ "type": {
2137
+ "type": "Array",
2138
+ "elementType": {
2139
+ "type": "Interface",
2140
+ "properties": [
2141
+ {
2142
+ "id": 0,
2143
+ "name": "label",
2144
+ "type": {
2145
+ "type": "String"
2146
+ }
2147
+ },
2148
+ {
2149
+ "id": 1,
2150
+ "name": "value",
2151
+ "type": {
2152
+ "type": "String"
2153
+ }
2154
+ }
2155
+ ]
2156
+ }
2157
+ },
2158
+ "optional": true
2159
+ },
2160
+ {
2161
+ "id": 8,
2162
+ "name": "useDescription",
2163
+ "type": {
2164
+ "type": "String"
2165
+ },
2166
+ "optional": true
1720
2167
  }
1721
2168
  ]
1722
2169
  },
@@ -2071,6 +2518,57 @@ var serviceProto = {
2071
2518
  "type": {
2072
2519
  "type": "String"
2073
2520
  }
2521
+ },
2522
+ {
2523
+ "id": 2,
2524
+ "name": "label",
2525
+ "type": {
2526
+ "type": "String"
2527
+ },
2528
+ "optional": true
2529
+ },
2530
+ {
2531
+ "id": 3,
2532
+ "name": "description",
2533
+ "type": {
2534
+ "type": "String"
2535
+ },
2536
+ "optional": true
2537
+ }
2538
+ ]
2539
+ }
2540
+ },
2541
+ "optional": true
2542
+ },
2543
+ {
2544
+ "id": 5,
2545
+ "name": "filters",
2546
+ "type": {
2547
+ "type": "Array",
2548
+ "elementType": {
2549
+ "type": "Interface",
2550
+ "properties": [
2551
+ {
2552
+ "id": 0,
2553
+ "name": "name",
2554
+ "type": {
2555
+ "type": "String"
2556
+ }
2557
+ },
2558
+ {
2559
+ "id": 1,
2560
+ "name": "emoji",
2561
+ "type": {
2562
+ "type": "String"
2563
+ },
2564
+ "optional": true
2565
+ },
2566
+ {
2567
+ "id": 2,
2568
+ "name": "command",
2569
+ "type": {
2570
+ "type": "String"
2571
+ }
2074
2572
  }
2075
2573
  ]
2076
2574
  }