koishi-plugin-dew-bot 1.0.261 → 1.0.262

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/index.js CHANGED
@@ -67,7 +67,7 @@ var serviceProto;
67
67
  var init_serviceProto = __esm({
68
68
  "src/shared/bot/serviceProto.ts"() {
69
69
  serviceProto = {
70
- "version": 37,
70
+ "version": 38,
71
71
  "services": [
72
72
  {
73
73
  "id": 0,
@@ -751,6 +751,14 @@ var init_serviceProto = __esm({
751
751
  }
752
752
  },
753
753
  "optional": true
754
+ },
755
+ {
756
+ "id": 14,
757
+ "name": "explorationRank",
758
+ "type": {
759
+ "type": "String"
760
+ },
761
+ "optional": true
754
762
  }
755
763
  ]
756
764
  },
@@ -2660,15 +2668,17 @@ var characterCardStyles = `
2660
2668
  .character-sheet .companion-portrait{padding:10px;border-radius:18px;flex:0 0 auto;background:linear-gradient(135deg,#fffefa 2%,var(--edge) 18%,#ffffff 30%,var(--accent) 41%,#fffdf2 51%,var(--edge) 71%,#ffffff 82%,var(--metal-shadow) 100%);box-shadow:inset 0 0 0 1px #ffffffd9,0 3px 9px #51628038}
2661
2669
  .character-sheet .companion-portrait:before{inset:5px;border-radius:12px;border-color:#fffdf1cf;box-shadow:0 0 0 1px color-mix(in srgb,var(--accent) 43%,transparent)}.character-sheet .companion-portrait:after{inset:2px;border-radius:15px;border-color:#fffefa9c}.character-sheet .companion-portrait .panel-corner{width:var(--portrait-ornament-size);height:var(--portrait-ornament-size)}
2662
2670
  .character-sheet .portrait-window{height:auto;border:2px solid var(--edge);border-radius:6px;overflow:hidden;background:var(--paper)}.character-artwork{display:block;width:100%;height:auto;object-fit:contain}.character-sheet .portrait-placeholder,.character-sheet .placeholder{height:100%;width:100%;display:flex;align-items:center;justify-content:center;flex-direction:column;gap:12px;color:var(--subtle);font:21px/1.5 "Microsoft YaHei",sans-serif}.character-sheet .placeholder small{font-size:16px}.character-sheet .portrait-window>.placeholder{height:448px}
2663
- .character-stats{padding:17px 18px 15px;display:flex;flex-direction:column}.character-stats>.character-identity,.character-stats>.combat-row,.character-stats>.resource,.character-stats>.attribute-panel{position:relative;z-index:4}
2671
+ .character-stats{padding:17px 18px 15px;display:flex;flex-direction:column}.character-stats>.character-identity,.character-stats>.resource,.character-stats>.attribute-panel{position:relative;z-index:4}
2664
2672
  .character-identity{text-align:center;min-width:0}.title-image-slot{position:absolute;inset:12px;display:flex;align-items:center;justify-content:center;min-width:0;z-index:4;pointer-events:none}.title-artwork{display:block;width:auto;height:auto;max-width:100%;max-height:min(110px,100%);object-fit:contain;filter:drop-shadow(0 2px 3px #17213a70)}.title-artwork[alt="称号图片占位"]{max-width:min(244px,100%);max-height:min(44px,100%)}
2665
2673
  .character-name{font-size:43px;line-height:1.17;font-weight:700;letter-spacing:3px;margin:0 0 3px;overflow-wrap:anywhere;color:var(--ink);text-shadow:0 1px 0 #ffffff}.character-level{font:27px/1.25 Georgia,"Microsoft YaHei",serif;margin:0;color:var(--subtle);display:flex;align-items:baseline;justify-content:center;gap:7px;min-width:0}.character-level strong{font-size:28px;font-weight:500;overflow-wrap:anywhere;min-width:0}
2666
2674
  .gene-panel{position:relative;margin:8px auto 0;padding:4px 12px 4px 7px;display:flex;align-items:center;justify-content:center;gap:6px;width:fit-content;max-width:100%;border:1px solid color-mix(in srgb,var(--edge) 80%,#ffffff);border-radius:7px;background:linear-gradient(#ffffffd6,color-mix(in srgb,var(--paper) 75%,#ffffff));box-shadow:inset 0 0 0 2px #ffffffaa,0 1px 2px #54628210}
2667
2675
  .gene-panel:before,.gene-panel:after{content:"";position:absolute;top:50%;width:6px;height:6px;background:var(--light-edge);border:1px solid var(--edge);transform:translateY(-50%) rotate(45deg)}.gene-panel:before{left:-4px}.gene-panel:after{right:-4px}.gene-crest{flex:0 0 26px;height:29px;color:var(--accent)}.gene-crest svg{display:block;width:100%;height:100%}.gene-rank{font-size:18px;line-height:1.35;letter-spacing:1px;overflow-wrap:anywhere;font-weight:700;text-align:center;min-width:0;color:var(--ink)}
2668
- .combat-row{display:flex;align-items:center;gap:8px;margin:14px 0 12px;min-width:0}.power-icon{flex:0 0 42px;height:42px;display:grid;place-items:center;border-radius:50%;border:1px solid var(--accent);box-shadow:inset 0 0 0 3px #fffef3b5;color:var(--accent);background:radial-gradient(ellipse at 38% 25%,#fffdf0,color-mix(in srgb,var(--accent) 14%,var(--panel)))}.power-icon svg{width:27px;height:27px;stroke:currentColor;fill:currentColor;stroke-width:2;stroke-linejoin:round}.power-label{font-size:21px;white-space:nowrap}.power-value{min-width:0;flex:1;text-align:right;overflow-wrap:anywhere;font:35px/1.2 Georgia,"Microsoft YaHei",serif;color:var(--accent);letter-spacing:-1px}
2676
+ .exploration-rank-row{display:flex;align-items:center;justify-content:center;gap:7px;margin:10px 0 0;font:16px/1.4 "KaiTi","STKaiti","Microsoft YaHei",sans-serif;letter-spacing:1px;color:var(--subtle)}.exploration-rank-row>svg{width:17px;height:17px;flex-shrink:0;fill:none;stroke:var(--accent);stroke-width:1.5;stroke-linecap:round;stroke-linejoin:round}.exploration-rank-row>span{white-space:nowrap}.exploration-rank-row strong{font-size:18px;font-weight:700;color:var(--ink);overflow-wrap:anywhere;min-width:0}
2677
+ .combat-row{display:flex;align-items:center;justify-content:center;gap:8px;margin:12px 0 0;padding:0 6px;min-width:0;font:28px/1.2 "KaiTi","STKaiti","Microsoft YaHei",sans-serif;position:relative;z-index:4}.power-icon{flex:0 0 34px;height:34px;display:grid;place-items:center;color:var(--accent)}.power-icon svg{display:block;width:34px;height:34px;overflow:visible}.power-label{font:inherit;white-space:nowrap}.power-value{min-width:0;flex:0 1 auto;text-align:center;overflow-wrap:anywhere;font:inherit;color:var(--accent);letter-spacing:0}
2669
2678
  .companion-resource{font-family:"Microsoft YaHei",sans-serif;margin-bottom:12px}.resource-heading{display:flex;justify-content:space-between;align-items:baseline;gap:8px;font-size:17px;color:var(--subtle);line-height:1.35}.resource-label{display:flex;align-items:center;gap:6px;font-weight:600;flex-shrink:0}.resource-label svg{width:18px;height:18px;fill:var(--health)}.resource-number{text-align:right;overflow-wrap:anywhere;min-width:0;font-size:15px}
2670
- .resource-track{height:18px;position:relative;margin-top:5px;border:1px solid var(--track-edge);border-radius:10px;overflow:hidden;background:linear-gradient(var(--track),#f2f5ed);box-shadow:inset 0 1px 2px #6e805825}.resource-fill{display:block;position:absolute;top:0;bottom:0;left:0;border-radius:9px;background:linear-gradient(var(--health-light),var(--health) 55%,color-mix(in srgb,var(--health) 80%,var(--health-light)));border-right:1px solid #fff9}.resource-percent{position:absolute;inset:0;display:grid;place-items:center;font:11px/1 "Microsoft YaHei",sans-serif;color:#fffef7;text-shadow:0 1px 2px #435443}.exp-resource .resource-heading{color:var(--subtle)}.exp-resource .resource-label svg{fill:var(--radar)}.exp-resource .resource-track{height:16px;border-color:color-mix(in srgb,var(--radar) 60%,#fff);background:var(--paper)}.exp-resource .resource-fill{background:linear-gradient(var(--leaf-light),var(--radar) 56%,var(--leaf))}
2671
- .companion-attributes{margin-top:5px;min-width:0;flex:1;display:flex;flex-direction:column;justify-content:center}.ornament-heading{display:flex;align-items:center;justify-content:center;gap:7px;margin:0 0 3px;font-size:22px;font-weight:700;letter-spacing:2px;line-height:1.3;color:var(--ink)}.ornament-heading:before,.ornament-heading:after{content:"";height:1px;flex:1;background:linear-gradient(90deg,transparent,var(--edge))}.ornament-heading:after{transform:rotate(180deg)}.heading-star{font-size:12px;color:var(--accent);letter-spacing:0}.companion-radar{width:100%;height:260px;display:block;overflow:visible}
2679
+ .resource-track{height:18px;position:relative;margin-top:5px;border:1px solid var(--track-edge);border-radius:10px;overflow:hidden;background:linear-gradient(var(--track),#f2f5ed);box-shadow:inset 0 1px 2px #6e805825}.resource-fill{display:block;position:absolute;top:0;bottom:0;left:0;border-radius:9px;background:linear-gradient(var(--health-light),var(--health) 55%,color-mix(in srgb,var(--health) 80%,var(--health-light)));border-right:1px solid #fff9}.resource-percent{position:absolute;inset:0;display:grid;place-items:center;font:11px/1 "Microsoft YaHei",sans-serif;color:#fffef7;text-shadow:0 1px 2px #435443}.exp-resource{width:calc(100% - 20px);align-self:center;margin:0 0 14px}.exp-resource .resource-track{height:6px;margin-top:0;border-color:#d5c48b;border-radius:4px;background:#eee9d7;box-shadow:inset 0 1px 1px #9d82351a}.exp-resource .resource-fill{border-radius:3px;border-right:0;background:linear-gradient(#fff0a0,#e8bd48 60%,#d6a934)}
2680
+ .hp-resource{display:flex;align-items:center;margin-top:16px;margin-bottom:0}.hp-resource .resource-track{flex:1;min-width:0;height:20px;margin-top:0}.hp-bar-value{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;padding:0 8px;white-space:nowrap;font:12px/1 "Microsoft YaHei",sans-serif;color:#fffef7;text-shadow:0 1px 2px #435443,0 0 2px #435443}
2681
+ .companion-attributes{margin-top:5px;min-width:0;flex:1;display:flex;flex-direction:column;justify-content:center}.attribute-divider{height:28px;margin:0 0 3px;display:flex;align-items:center;justify-content:center}.attribute-divider svg{display:block;width:100%;height:36px}.companion-radar{width:100%;height:260px;display:block;overflow:visible}
2672
2682
  .radar-grid{fill:none;stroke:var(--radar-grid);stroke-width:1.1}.radar-axis{stroke:var(--radar-grid);stroke-width:1;opacity:.8}.radar-area{fill:var(--radar-fill);stroke:var(--radar);stroke-width:2.3;stroke-linejoin:round}.radar-dot{fill:#fffefa;stroke:var(--radar);stroke-width:2}.radar-label{font:19px "KaiTi","STKaiti","Microsoft YaHei",sans-serif;fill:var(--ink)}.radar-value{font:20px Georgia,"Microsoft YaHei",serif;fill:var(--ink)}.radar-scale{font:11px Georgia,serif;fill:var(--subtle)}.empty-attributes{padding:45px 10px;color:var(--subtle);font:18px/1.6 "Microsoft YaHei",sans-serif;text-align:center}.custom-attributes{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:5px 10px;margin:6px 0 0;font:15px/1.5 "Microsoft YaHei",sans-serif;color:var(--subtle)}.custom-attributes span{overflow-wrap:anywhere}.custom-attributes b{font-weight:500;color:var(--ink)}
2673
2683
  `;
2674
2684
 
@@ -2721,6 +2731,12 @@ function characterGeneCrest(tier) {
2721
2731
  return `<svg viewBox="0 0 60 60" xmlns="http://www.w3.org/2000/svg" aria-hidden="true"><path d="m30 4 20 10v20L30 55 10 34V14Z" fill="var(--paper)" stroke="currentColor" stroke-width="1.6"/><path d="m30 9 15 8v16L30 49 15 33V17Z" fill="none" stroke="var(--light-edge)" stroke-width="1"/><path d="M23 27v-6a7 7 0 0 1 14 0v6" fill="none" stroke="currentColor" stroke-width="2.5"/><rect x="20" y="26" width="20" height="15" rx="3" fill="currentColor"/><circle cx="30" cy="32" r="2" fill="var(--light-edge)"/><path d="M30 33v4" stroke="var(--light-edge)" stroke-width="1.8"/>${tier >= 3 ? '<path d="m30 2 3 4-3 4-3-4Z" fill="var(--gem)" stroke="var(--light-edge)"/>' : ""}${tier >= 5 ? '<path d="m18 12 1-7 6 4 5-7 5 7 6-4 1 7Z" fill="var(--accent)" stroke="var(--light-edge)" stroke-width=".7"/>' : ""}</svg>`;
2722
2732
  }
2723
2733
  __name(characterGeneCrest, "characterGeneCrest");
2734
+ var characterAttributeDivider = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 310 36">
2735
+ ${ornamentPaint}
2736
+ <g fill="none" stroke="var(--edge)" stroke-width=".8"><path d="M5 18h91m118 0h91"/><path d="M75 18c18 0 29-8 47-8m66 0c18 0 29 8 47 8"/><path d="M102 18c12 0 21 6 31 6m44 0c10 0 19-6 31-6"/></g>
2737
+ <g fill="url(#character-leaf)" stroke="var(--light-edge)" stroke-width=".6"><path d="M120 11c-10-1-14-6-16-10 10 0 17 3 16 10Zm14 12c-10 2-18-1-22-6 10-2 18-1 22 6Zm56-12c10-1 14-6 16-10-10 0-17 3-16 10Zm-14 12c10 2 18-1 22-6-10-2-18-1-22 6Z"/></g>
2738
+ ${flower(155, 18, 0.5)}<g fill="var(--accent)"><path d="m94 18 3-3 3 3-3 3Zm116 0 3-3 3 3-3 3Z"/></g>
2739
+ </svg>`;
2724
2740
 
2725
2741
  // src/plugin/characterCardTemplate.ts
2726
2742
  var finite = /* @__PURE__ */ __name((value) => Number.isFinite(value) ? value : 0, "finite");
@@ -2735,7 +2751,6 @@ var primaryAttributes = [
2735
2751
  ["resistance", "抗性"]
2736
2752
  ];
2737
2753
  var primaryIds = new Set(primaryAttributes.map(([id]) => id));
2738
- var heading = /* @__PURE__ */ __name((label) => `<h2 class="ornament-heading"><span class="heading-star" aria-hidden="true">✦</span>${cardEscape(label)}<span class="heading-star" aria-hidden="true">✦</span></h2>`, "heading");
2739
2754
  var corners = /* @__PURE__ */ __name((tier, positions = ["tl", "tr", "bl", "br"]) => positions.map((position) => `<span class="panel-corner corner-${position}" data-decoration-level="${tier}">${characterTierOrnament(tier)}</span>`).join(""), "corners");
2740
2755
  function radar(attributes) {
2741
2756
  if (!attributes.length) return '<p class="empty-attributes">暂无一级属性</p>';
@@ -2781,14 +2796,16 @@ function characterCardHtml(card, artwork, _frame, defaultArtwork, titleArtwork,
2781
2796
  const hpPercent = ratio(card.hp, card.maxHp) * 100;
2782
2797
  const experience = Math.max(0, Math.min(100, finite(card.experiencePercent)));
2783
2798
  const geneRank = card.geneLockRankName || "基因锁未开启";
2799
+ const explorationRank = card.explorationRank?.trim();
2784
2800
  return `<!doctype html><html lang="zh-CN"><head><meta charset="utf-8"><style>${characterCardStyles}</style></head><body><main id="capture" class="companion-sheet character-adventure character-sheet gene-tier-${tier}" data-gene-lock-tier="${tier}" data-decoration-level="${tier}"><span class="character-ambient" aria-hidden="true">${characterFloralWatermark}</span><div class="character-card companion-main">
2785
2801
  <div class="portrait-column"><div class="companion-panel companion-portrait character-portrait">${corners(tier)}<div class="portrait-window portrait">${portrait ? `<img id="character-image" class="character-artwork" src="${cardEscape(portrait)}" data-fallback="${cardEscape(defaultArtwork ?? "")}" alt="${cardEscape(card.name)}的形象图">` : '<div class="placeholder"><span>形象尚未生成</span><small>角色形象</small></div>'}</div>
2786
- <div class="title-image-slot character-title-centered"><img id="title-image" class="title-artwork" src="${cardEscape(titleArtwork || characterTitlePlaceholder)}" data-fallback="${cardEscape(characterTitlePlaceholder)}" alt="${titleArtwork ? "称号图片" : "称号图片占位"}"></div></div></div>
2787
- <section class="companion-panel companion-stats character-stats" aria-label="角色属性"><span class="character-paper-pattern" aria-hidden="true">${characterFloralWatermark}</span>${corners(tier, ["tr", "br"])}<div class="identity character-identity"><h1 id="character-name" class="character-name" title="${cardEscape(card.name)}">${cardEscape(card.name)}</h1><p class="level character-level"><span>Lv.</span><strong id="character-level">${number2(card.level)}</strong></p><div class="gene-panel"><span class="gene-crest" aria-hidden="true">${characterGeneCrest(tier)}</span><div id="gene-rank" class="gene-rank" title="${cardEscape(geneRank)}">${cardEscape(geneRank)}</div></div></div>
2788
- <div class="combat-row companion-power-row"><span class="power-emblem power-icon" aria-hidden="true"><svg viewBox="0 0 42 42"><path d="m9 5 5 1 16 21-4 4L9 11Zm24 0-5 1L12 27l4 4L33 11ZM14 27l14 10m0-10L14 37M9 35l4-4m20 4-4-4"/></svg></span><span class="power-label companion-power-label">战力</span><strong id="combat-power" class="power-value companion-power">${number2(card.power)}</strong></div>
2789
- <div class="resource companion-resource hp-resource"><div class="resource-heading"><span class="resource-label"><svg viewBox="0 0 32 32" aria-hidden="true"><path d="M16 28C6 21 2 17 2 10a7 7 0 0 1 14-2 7 7 0 0 1 14 2c0 7-4 11-14 18Z"/></svg>生命值</span><span id="hp-numbers" class="resource-number">${number2(card.hp)} / ${number2(card.maxHp)}</span></div><div class="resource-track" role="progressbar" aria-label="生命值" aria-valuemin="0" aria-valuemax="100" aria-valuenow="${hpPercent}"><span class="resource-fill fill" style="width:${hpPercent}%"></span><span class="bar-percent resource-percent">${number2(hpPercent)}%</span></div></div>
2790
- <div class="resource companion-resource exp-resource"><div class="resource-heading"><span class="resource-label"><svg viewBox="0 0 32 32" aria-hidden="true"><path d="m16 2 4.5 9.1 10 1.4-7.3 7.1 1.7 10-8.9-4.7-8.9 4.7 1.7-10-7.3-7.1 10-1.4Z"/></svg>经验进度</span><span id="exp-number" class="resource-number">${number2(experience)}%</span></div><div class="resource-track" role="progressbar" aria-label="经验进度" aria-valuemin="0" aria-valuemax="100" aria-valuenow="${experience}"><span class="resource-fill" style="width:${experience}%"></span><span class="resource-percent">${number2(experience)}%</span></div></div>
2791
- <section class="attribute-panel companion-attributes" aria-label="一级属性">${heading("一级属性")}${radar(card.attributes)}</section></section>
2802
+ ${titleArtwork || card.titleImageUrl ? `<div class="title-image-slot character-title-centered"><img id="title-image" class="title-artwork" src="${cardEscape(titleArtwork || characterTitlePlaceholder)}" data-fallback="${cardEscape(characterTitlePlaceholder)}" alt="${titleArtwork ? "称号图片" : "称号图片占位"}"></div>` : ""}</div>
2803
+ <div class="combat-row companion-power-row"><span class="power-emblem power-icon" aria-hidden="true"><svg class="crossed-weapons" viewBox="0 0 42 42"><g fill="currentColor" stroke="var(--panel)" stroke-width=".9" stroke-linejoin="round"><g transform="rotate(-45 21 21)"><path d="M21 2 26 9v14l-5 4-5-4V9Z"/><path d="M12 26h18l-1 4H13Zm7 4h4v8h-4Zm-1 8h6v3h-6Z"/><path d="M21 7v15" fill="none" stroke="var(--light-edge)" stroke-width="1.1"/></g><g transform="rotate(45 21 21)"><path d="M21 2 26 9v14l-5 4-5-4V9Z"/><path d="M12 26h18l-1 4H13Zm7 4h4v8h-4Zm-1 8h6v3h-6Z"/><path d="M21 7v15" fill="none" stroke="var(--light-edge)" stroke-width="1.1"/></g></g></svg></span><span class="power-label companion-power-label">战力</span><strong id="combat-power" class="power-value companion-power">${number2(card.power)}</strong></div>
2804
+ </div>
2805
+ <section class="companion-panel companion-stats character-stats" aria-label="角色属性"><span class="character-paper-pattern" aria-hidden="true">${characterFloralWatermark}</span>${corners(tier, ["tr", "br"])}<div class="identity character-identity"><h1 id="character-name" class="character-name" title="${cardEscape(card.name)}">${cardEscape(card.name)}</h1><p class="level character-level"><span>Lv.</span><strong id="character-level">${number2(card.level)}</strong></p><div class="gene-panel"><span class="gene-crest" aria-hidden="true">${characterGeneCrest(tier)}</span><div id="gene-rank" class="gene-rank" title="${cardEscape(geneRank)}">${cardEscape(geneRank)}</div></div><p class="exploration-rank-row"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M19.8 7.7A8 8 0 1 0 20 16M20 3v5h-5M15.8 12a3.8 3.8 0 1 1-3.8-3.8"/></svg><span>轮回等级</span> <strong id="exploration-rank">${cardEscape(explorationRank ? explorationRank + "级" : "—")}</strong></p></div>
2806
+ <div class="resource companion-resource hp-resource"><div class="resource-track" role="progressbar" aria-label="生命值" aria-valuemin="0" aria-valuemax="100" aria-valuenow="${hpPercent}" aria-valuetext="${number2(card.hp)} / ${number2(card.maxHp)}"><span class="resource-fill fill" style="width:${hpPercent}%"></span><span id="hp-numbers" class="hp-bar-value">${number2(card.hp)} / ${number2(card.maxHp)}</span></div></div>
2807
+ <div class="resource companion-resource exp-resource"><div id="experience-track" class="resource-track" role="progressbar" aria-label="经验进度" aria-valuemin="0" aria-valuemax="100" aria-valuenow="${experience}"><span class="resource-fill" style="width:${experience}%"></span></div></div>
2808
+ <section class="attribute-panel companion-attributes" aria-label="一级属性"><div class="attribute-divider" aria-hidden="true">${characterAttributeDivider}</div>${radar(card.attributes)}</section></section>
2792
2809
  </div></main></body></html>`;
2793
2810
  }
2794
2811
  __name(characterCardHtml, "characterCardHtml");
@@ -2854,7 +2871,7 @@ var primaryAttributes2 = [
2854
2871
  ];
2855
2872
  var primaryIds2 = new Set(primaryAttributes2.map(([id]) => id));
2856
2873
  var corners2 = /* @__PURE__ */ __name((positions = ["tl", "tr", "bl", "br"]) => positions.map((position) => `<span class="panel-corner corner-${position}">${companionCornerDecoration}</span>`).join(""), "corners");
2857
- var heading2 = /* @__PURE__ */ __name((title) => `<h2 class="ornament-heading"><span class="heading-star" aria-hidden="true">✦</span>${cardEscape(title)}<span class="heading-star" aria-hidden="true">✦</span></h2>`, "heading");
2874
+ var heading = /* @__PURE__ */ __name((title) => `<h2 class="ornament-heading"><span class="heading-star" aria-hidden="true">✦</span>${cardEscape(title)}<span class="heading-star" aria-hidden="true">✦</span></h2>`, "heading");
2858
2875
  var paw = '<svg viewBox="0 0 28 28" aria-hidden="true"><ellipse cx="5" cy="10" rx="3" ry="4"/><ellipse cx="11" cy="5" rx="3" ry="4"/><ellipse cx="18" cy="5" rx="3" ry="4"/><ellipse cx="24" cy="10" rx="3" ry="4"/><path d="M5 21c0-5 4-4 6-9 1-3 5-3 6 0 2 5 6 4 6 9 0 5-6 4-9 2-3 2-9 3-9-2Z"/></svg>';
2859
2876
  var crest = '<svg viewBox="0 0 32 32" aria-hidden="true" fill="currentColor"><path d="M15 28C5 24 3 16 4 7c8 0 11 9 11 21ZM17 28c10-4 12-12 11-21-8 0-11 9-11 21ZM16 18C9 13 10 7 16 2c6 5 7 11 0 16Z"/></svg>';
2860
2877
  var sword = '<svg viewBox="0 0 42 42" aria-hidden="true"><path d="m9 5 5 1 16 21-4 4L9 11Zm24 0-5 1L12 27l4 4L33 11ZM14 27l14 10m0-10L14 37M9 35l4-4m20 4-4-4"/></svg>';
@@ -2898,13 +2915,13 @@ function radar2(card) {
2898
2915
  }
2899
2916
  __name(radar2, "radar");
2900
2917
  function bloodlines(card, icons) {
2901
- return `<section class="companion-panel bloodline-panel" aria-label="血统">${heading2("血统")}${card.bloodlines.length ? `<div class="bloodline-list">${card.bloodlines.map((bloodline, index) => `<div class="bloodline-row"><span class="bloodline-crest${icons?.bloodlines?.[index] ? " has-entry-artwork" : ""}" aria-hidden="true">${bloodline.emoji ? cardEscape(bloodline.emoji) : crest}${icons?.bloodlines?.[index] ? `<img class="bloodline-entry-artwork" data-entry-index="${index}" src="${cardEscape(icons.bloodlines[index])}" alt="">` : ""}</span><span class="bloodline-name">${cardEscape(bloodline.name)}</span><span class="bloodline-quality">${cardEscape(bloodline.quality)}</span></div>`).join("")}</div>` : '<p class="empty-list">暂无血统</p>'}</section>`;
2918
+ return `<section class="companion-panel bloodline-panel" aria-label="血统">${heading("血统")}${card.bloodlines.length ? `<div class="bloodline-list">${card.bloodlines.map((bloodline, index) => `<div class="bloodline-row"><span class="bloodline-crest${icons?.bloodlines?.[index] ? " has-entry-artwork" : ""}" aria-hidden="true">${bloodline.emoji ? cardEscape(bloodline.emoji) : crest}${icons?.bloodlines?.[index] ? `<img class="bloodline-entry-artwork" data-entry-index="${index}" src="${cardEscape(icons.bloodlines[index])}" alt="">` : ""}</span><span class="bloodline-name">${cardEscape(bloodline.name)}</span><span class="bloodline-quality">${cardEscape(bloodline.quality)}</span></div>`).join("")}</div>` : '<p class="empty-list">暂无血统</p>'}</section>`;
2902
2919
  }
2903
2920
  __name(bloodlines, "bloodlines");
2904
2921
  function skills(card, icons) {
2905
2922
  const active = '<path d="m8 24 16-16 2-3 4 4-3 2-16 16M7 21l9 9M7 31l5-5M5 33l2-2"/>';
2906
2923
  const passive = '<path d="M17 30V14M17 24C7 22 4 14 5 8c9 1 13 7 12 16ZM17 19c1-9 5-14 13-15 0 8-4 14-13 15Z"/>';
2907
- return `<section class="companion-panel companion-skills" aria-label="技能">${corners2()}${heading2("技能")}${card.skills.length ? `<div class="skill-grid">${card.skills.map((skill, index) => `<div class="companion-skill" data-skill-id="${cardEscape(skill.id)}"><span class="skill-icon" aria-hidden="true"><svg viewBox="0 0 36 36">${skill.type === "passive" ? passive : active}</svg>${icons?.skills?.[index] ? `<img class="skill-entry-artwork" data-entry-index="${index}" src="${cardEscape(icons.skills[index])}" alt="">` : ""}</span><span class="skill-name">${cardEscape(skill.name)}</span><span class="skill-level">Lv.${number3(skill.rank)}</span></div>`).join("")}</div>` : '<p class="empty-list">暂无技能</p>'}</section>`;
2924
+ return `<section class="companion-panel companion-skills" aria-label="技能">${corners2()}${heading("技能")}${card.skills.length ? `<div class="skill-grid">${card.skills.map((skill, index) => `<div class="companion-skill" data-skill-id="${cardEscape(skill.id)}"><span class="skill-icon" aria-hidden="true"><svg viewBox="0 0 36 36">${skill.type === "passive" ? passive : active}</svg>${icons?.skills?.[index] ? `<img class="skill-entry-artwork" data-entry-index="${index}" src="${cardEscape(icons.skills[index])}" alt="">` : ""}</span><span class="skill-name">${cardEscape(skill.name)}</span><span class="skill-level">Lv.${number3(skill.rank)}</span></div>`).join("")}</div>` : '<p class="empty-list">暂无技能</p>'}</section>`;
2908
2925
  }
2909
2926
  __name(skills, "skills");
2910
2927
  function companionCardHtml(card, artwork, icons) {
@@ -2920,7 +2937,7 @@ function companionCardHtml(card, artwork, icons) {
2920
2937
  <div class="companion-power-row"><span class="power-icon" aria-hidden="true">${sword}</span><span class="companion-power-label">战力</span><strong id="companion-power" class="companion-power">${number3(card.power)}</strong></div>
2921
2938
  <div class="companion-resource hp-resource"><div class="resource-heading"><span class="resource-label"><svg viewBox="0 0 32 32" aria-hidden="true"><path d="M16 28C6 21 2 17 2 10a7 7 0 0 1 14-2 7 7 0 0 1 14 2c0 7-4 11-14 18Z"/></svg>生命值</span><span id="companion-hp" class="resource-number">${number3(card.hp)} / ${number3(card.maxHp)}</span></div><div class="resource-track" role="progressbar" aria-label="生命值" aria-valuemin="0" aria-valuemax="100" aria-valuenow="${healthPercent}"><span class="resource-fill" style="width:${healthPercent}%"></span><span class="resource-percent">${number3(healthPercent)}%</span></div></div>
2922
2939
  ${pet ? `<div class="companion-resource exp-resource"><div class="resource-heading"><span class="resource-label"><svg viewBox="0 0 32 32" aria-hidden="true"><path d="m16 2 4.5 9.1 10 1.4-7.3 7.1 1.7 10-8.9-4.7-8.9 4.7 1.7-10-7.3-7.1 10-1.4Z"/></svg>经验进度</span><span class="resource-number">${number3(experience)}%</span></div><div class="resource-track" role="progressbar" aria-label="经验进度" aria-valuemin="0" aria-valuemax="100" aria-valuenow="${experience}"><span class="resource-fill" style="width:${experience}%"></span><span class="resource-percent">${number3(experience)}%</span></div></div>` : ""}
2923
- <section class="companion-attributes" aria-label="一级属性">${heading2("一级属性")}${radar2(card)}</section></section>
2940
+ <section class="companion-attributes" aria-label="一级属性">${heading("一级属性")}${radar2(card)}</section></section>
2924
2941
  </div>${skills(card, icons)}</main></body></html>`;
2925
2942
  }
2926
2943
  __name(companionCardHtml, "companionCardHtml");
@@ -3419,15 +3436,13 @@ function radar4(card) {
3419
3436
  const attribute = card.attributes.find((value) => value.id === id);
3420
3437
  return { name: attribute?.name?.trim() || name2, value: finite4(attribute?.value ?? 0) };
3421
3438
  });
3422
- const max = Math.max(1, ...attributes.map((attribute) => Math.max(0, attribute.value)));
3423
- const magnitude = 10 ** Math.floor(Math.log10(max));
3424
- const ceiling = Math.ceil(max / magnitude) * magnitude;
3439
+ const ceiling = Math.max(0, ...attributes.map((attribute) => attribute.value));
3425
3440
  const point = /* @__PURE__ */ __name((index, radius) => {
3426
3441
  const angle = -Math.PI / 2 + index * Math.PI / 3;
3427
3442
  return [320 + Math.cos(angle) * radius, 190 + Math.sin(angle) * radius];
3428
3443
  }, "point");
3429
3444
  const polygon = /* @__PURE__ */ __name((radius) => attributes.map((_, index) => point(index, radius).join(",")).join(" "), "polygon");
3430
- const data = attributes.map((attribute, index) => point(index, 122 * Math.max(0, Math.min(1, attribute.value / ceiling))));
3445
+ const data = attributes.map((attribute, index) => point(index, 122 * (ceiling > 0 ? Math.max(0, attribute.value / ceiling) : 0)));
3431
3446
  return `<div class="radar-panel"><svg class="bloodline-radar" viewBox="0 0 640 396" role="img" aria-label="血统六维属性加成雷达图" xmlns="http://www.w3.org/2000/svg">
3432
3447
  <defs><linearGradient id="bloodline-radar-fill" x1="0" y1="0" x2="1" y2="1"><stop stop-color="var(--quality-accent)" stop-opacity=".35"/><stop offset="1" stop-color="var(--quality-accent)" stop-opacity=".12"/></linearGradient></defs>
3433
3448
  ${[0.2, 0.4, 0.6, 0.8, 1].map((scale) => `<polygon points="${polygon(122 * scale)}" fill="none" stroke="#b9bdad" stroke-width="1.2"/>`).join("")}
@@ -4080,6 +4095,42 @@ function renderQQRewardMarkdown(data, showGreeting) {
4080
4095
  }
4081
4096
  __name(renderQQRewardMarkdown, "renderQQRewardMarkdown");
4082
4097
 
4098
+ // src/shared/bot/inlineCommands.ts
4099
+ function inlineCommandSpans(content) {
4100
+ const spans = [];
4101
+ let depth = 0, start = 0, labelStart = 0;
4102
+ for (let index = 0; index < content.length; index++) {
4103
+ if (content[index] === "[") {
4104
+ if (depth === 0) {
4105
+ start = index > 0 && content[index - 1] === "\\" ? index - 1 : index;
4106
+ labelStart = index + 1;
4107
+ }
4108
+ depth++;
4109
+ } else if (content[index] === "]" && depth > 0 && --depth === 0) {
4110
+ const label = content.slice(labelStart, index).replace(/\\$/, "");
4111
+ if (!label.trim() || /[\[\]\r\n]/.test(label) || /^\d+$/.test(label.trim())) continue;
4112
+ const alias = /^\(command:([^()[\]\r\n]+)\)/.exec(content.slice(index + 1));
4113
+ const command = alias ? alias[1].trim() : label.split(/[++]/, 1)[0].trim().replace(/^🔄\s*/u, "");
4114
+ if (!command) continue;
4115
+ const end = index + 1 + (alias?.[0].length ?? 0);
4116
+ spans.push({ start, end, label, command, ...alias ? { aliased: true } : {} });
4117
+ if (alias) index = end - 1;
4118
+ }
4119
+ }
4120
+ return spans;
4121
+ }
4122
+ __name(inlineCommandSpans, "inlineCommandSpans");
4123
+ function renderPlainInlineCommandAliases(content) {
4124
+ let result = "", offset = 0;
4125
+ for (const span of inlineCommandSpans(content)) {
4126
+ if (!span.aliased) continue;
4127
+ result += content.slice(offset, span.start) + (span.label.trim() === span.command ? `[${span.command}]` : `${span.label}[${span.command}]`);
4128
+ offset = span.end;
4129
+ }
4130
+ return result + content.slice(offset);
4131
+ }
4132
+ __name(renderPlainInlineCommandAliases, "renderPlainInlineCommandAliases");
4133
+
4083
4134
  // src/plugin/serverHandel/qqPageMessage.ts
4084
4135
  function compactListLabel(value) {
4085
4136
  const label = compactDisplayText(value);
@@ -4099,36 +4150,21 @@ function inlineCommandUrl(command) {
4099
4150
  return `mqqapi://aio/inlinecmd?command=${encoded}&enter=true&reply=false`;
4100
4151
  }
4101
4152
  __name(inlineCommandUrl, "inlineCommandUrl");
4102
- function inlineCommandSpans(content) {
4103
- const spans = [];
4104
- let depth = 0, start = 0, labelStart = 0;
4105
- for (let index = 0; index < content.length; index++) {
4106
- if (content[index] === "[") {
4107
- if (depth === 0) {
4108
- start = index > 0 && content[index - 1] === "\\" ? index - 1 : index;
4109
- labelStart = index + 1;
4110
- }
4111
- depth++;
4112
- } else if (content[index] === "]" && depth > 0 && --depth === 0) {
4113
- const label = content.slice(labelStart, index).replace(/\\$/, "");
4114
- if (!label.trim() || /[\[\]\r\n]/.test(label) || /^\d+$/.test(label.trim())) continue;
4115
- const command = label.split(/[++]/, 1)[0].trim().replace(/^🔄\s*/u, "");
4116
- if (command) spans.push({ start, end: index + 1, label, command });
4117
- }
4118
- }
4153
+ function inlineCommandSpans2(content) {
4154
+ const spans = inlineCommandSpans(content);
4119
4155
  const logLinks = battleLogLinkSpans(content);
4120
4156
  return spans.filter((span) => !logLinks.some((link) => span.start < link.end && span.end > link.start));
4121
4157
  }
4122
- __name(inlineCommandSpans, "inlineCommandSpans");
4158
+ __name(inlineCommandSpans2, "inlineCommandSpans");
4123
4159
  function hasInlineCommands(content) {
4124
- return inlineCommandSpans(content).length > 0;
4160
+ return inlineCommandSpans2(content).length > 0;
4125
4161
  }
4126
4162
  __name(hasInlineCommands, "hasInlineCommands");
4127
4163
  function renderInlineCommands(content) {
4128
4164
  const parts = [];
4129
4165
  let offset = 0;
4130
4166
  const spans = [
4131
- ...inlineCommandSpans(content).map((span) => ({ ...span, rendered: "[" + escapeMarkdown(span.label) + "](" + inlineCommandUrl(span.command) + ")" })),
4167
+ ...inlineCommandSpans2(content).map((span) => ({ ...span, rendered: "[" + escapeMarkdown(span.label) + "](" + inlineCommandUrl(span.command) + ")" })),
4132
4168
  ...battleLogLinkSpans(content).map((span) => ({ ...span, rendered: renderBattleLogLink(span) }))
4133
4169
  ].sort((a, b) => a.start - b.start);
4134
4170
  for (const span of spans) {
@@ -4431,7 +4467,8 @@ var AnalysisMsg = class {
4431
4467
  case 1 /* line */:
4432
4468
  const labeledRow = labeledEntries.get(lineIndex);
4433
4469
  const content = labeledRow ? labeledRow.map((entry) => entry.label + (entry.description ? " · " + entry.description : "") + ` [${entry.viewCommand}]`).join("") : withCommandHint(item.content, commands.get(lineIndex));
4434
- msg += `${content}
4470
+ const plainContent = item.codeBlock || item.mailPart === "body" || labeledRow ? content : renderPlainInlineCommandAliases(content);
4471
+ msg += `${plainContent}
4435
4472
  `;
4436
4473
  break;
4437
4474
  case 2 /* error */:
@@ -4457,12 +4494,12 @@ function renderQQCommandMarkdown(data, showGreeting) {
4457
4494
  return renderInlineCommands(onlyLine.content);
4458
4495
  }
4459
4496
  const title = data.line.find((item) => item.type === 0 /* title */)?.content;
4460
- const heading3 = title === void 0 ? "**🔔** 系统提示" : `**${escapeMarkdown(title)}**`;
4497
+ const heading2 = title === void 0 ? "**🔔** 系统提示" : `**${escapeMarkdown(title)}**`;
4461
4498
  const body = data.line.filter((item) => item.type !== 0 /* title */).flatMap((item) => {
4462
4499
  const content = item.type === 2 /* error */ ? `❌ ${item.content}` : item.content;
4463
4500
  return content.replace(/\r\n?/g, "\n").split("\n").map((line) => "> " + renderInlineCommands(line));
4464
4501
  }).join(" \n");
4465
- return heading3 + (body ? "\n\n" + body : "");
4502
+ return heading2 + (body ? "\n\n" + body : "");
4466
4503
  }
4467
4504
  const rewardMessage = isRewardMessage(data);
4468
4505
  if (!data.commandHelp && !data.pageNav && data.line.some((item) => item.type === 1 /* line */ && !item.reward && /^\[\d+\]\s*[^\r\n]+$/.test(item.content))) {
@@ -4494,8 +4531,10 @@ function renderQQCommandMarkdown(data, showGreeting) {
4494
4531
  break;
4495
4532
  }
4496
4533
  flushRewards();
4497
- if (!item.content.trim()) flushBody();
4498
- else bodyLines.push(renderInlineCommands(item.content));
4534
+ for (const line of item.content.replace(/\r\n?/g, "\n").split("\n")) {
4535
+ if (!line.trim()) flushBody();
4536
+ else bodyLines.push(renderInlineCommands(line));
4537
+ }
4499
4538
  break;
4500
4539
  case 2 /* error */:
4501
4540
  flushBody();
@@ -1,4 +1,4 @@
1
- /** The unavailable title remains an independent image rather than a text badge. */
1
+ /** Requested but unavailable title artwork has an independent image fallback. */
2
2
  export declare const characterTitlePlaceholder: string;
3
3
  /** Each tier adds finer foliage, white petal layers and metallic vinework. */
4
4
  export declare function characterTierOrnament(tier: number): string;
@@ -6,3 +6,5 @@ export declare function characterTierOrnament(tier: number): string;
6
6
  export declare const characterFloralWatermark = "<svg class=\"floral-watermark\" viewBox=\"0 0 760 880\" preserveAspectRatio=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n <g fill=\"currentColor\">\n <path d=\"M-9 138C39 105 55 49 78-10c-8 64-37 121-87 148ZM17 107C-5 94-8 61-4 38c21 23 36 43 21 69ZM44 60C32 29 40 10 57-12c9 30 9 51-13 72ZM63 24c23-16 49-13 65-2-23 11-46 16-65 2Z\"/>\n <path d=\"M719 787c-2-52 10-98 42-137-9 66-21 110-42 137Zm12-66c-21-16-17-40-19-65 19 20 27 40 19 65Zm10-28c14-26 36-35 56-34-17 18-35 34-56 34ZM2 780c8-17 33-27 51-20-19 19-34 25-51 20Zm34-20c9-24 29-35 47-32-10 22-27 36-47 32Z\"/>\n <g transform=\"translate(58 839) scale(1.5)\"><path d=\"M0 0c-31-14-41-45-23-61 27-7 32 37 23 61Z\"/><path d=\"M0 0c3-31 19-59 44-49 20 19-13 43-44 49Z\"/><path d=\"M0 0c30-10 56-6 55 18-14 25-40 2-55-18Z\"/><path d=\"M0 0c20 20 22 49 0 53-23-7-12-35 0-53Z\"/><path d=\"M0 0c-23 16-51 8-48-15 11-17 35-2 48 15Z\"/></g>\n <g transform=\"translate(696 71) scale(1.4)\"><path d=\"M0 0c-31-14-41-45-23-61 27-7 32 37 23 61Z\"/><path d=\"M0 0c3-31 19-59 44-49 20 19-13 43-44 49Z\"/><path d=\"M0 0c30-10 56-6 55 18-14 25-40 2-55-18Z\"/><path d=\"M0 0c20 20 22 49 0 53-23-7-12-35 0-53Z\"/><path d=\"M0 0c-23 16-51 8-48-15 11-17 35-2 48 15Z\"/></g>\n <g transform=\"translate(63 425) scale(.9)\"><path d=\"M0 0c-31-14-41-45-23-61 27-7 32 37 23 61Z\"/><path d=\"M0 0c3-31 19-59 44-49 20 19-13 43-44 49Z\"/><path d=\"M0 0c30-10 56-6 55 18-14 25-40 2-55-18Z\"/><path d=\"M0 0c20 20 22 49 0 53-23-7-12-35 0-53Z\"/><path d=\"M0 0c-23 16-51 8-48-15 11-17 35-2 48 15Z\"/></g>\n </g>\n <g fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><path d=\"M-4 191C19 101 61 150 86 46M-8 803c84-50 100-82 93-147M705 868c-5-90 24-171 74-226M660-9c55 69 50 129 106 165\"/><path d=\"M85 45c17 2 20 27 7 30-9 1-13-14-5-16M85 674c-24 10-27 43-8 45 10-2 6-17-1-14\"/></g>\n</svg>";
7
7
  /** The name of the configured gene-lock rank stays separate from this small crest. */
8
8
  export declare function characterGeneCrest(tier: number): string;
9
+ /** Quiet botanical divider above the six-axis radar; carries no visible label. */
10
+ export declare const characterAttributeDivider: string;
@@ -1,2 +1,2 @@
1
1
  /** Character-only moon-white paper, metallic frames and six botanical gene-lock themes. */
2
- export declare const characterCardStyles = "\n*{box-sizing:border-box}body{margin:0;width:760px}#capture{width:760px;position:relative;overflow:hidden;font-family:\"KaiTi\",\"STKaiti\",\"Microsoft YaHei\",sans-serif}\n.character-sheet{--ink:#374556;--subtle:#748192;--accent:#9b927d;--edge:#aab4bd;--light-edge:#fffefa;--metal-shadow:#8f9ba8;--panel:#fffefa;--panel-deep:#f0f3f5;--paper:#e8edf0;--flower:#fffefa;--leaf:#9aaabb;--leaf-light:#e2e9ed;--gem:#bbc9d4;--track:#e2e7dd;--track-edge:#afbea9;--health:#759569;--health-light:#b4cea2;--radar:#8995ae;--radar-fill:#aebbdc66;--radar-grid:#c4cbd7;--background:#dce3e9;--background-deep:#bac7d3;--ornament-size:86px;--portrait-ornament-size:74px;--frame-radius:27px;--panel-border:2px;padding:22px;color:var(--ink);background:radial-gradient(ellipse at 12% 6%,#fffefaef,transparent 51%),radial-gradient(ellipse at 85% 90%,#f6f8ffc4,transparent 51%),radial-gradient(ellipse at 78% 12%,#ffffff80,transparent 38%),linear-gradient(145deg,var(--background-deep),var(--background) 48%,var(--background-deep))}\n.gene-tier-0{--ink:#435162;--subtle:#7c8795;--accent:#989081;--edge:#b3bac1;--leaf:#9caab8;--leaf-light:#e4e9ef;--gem:#cbd3de;--background:#e3e8ed;--background-deep:#c6cfd9}\n.gene-tier-1{--ink:#365762;--subtle:#718b90;--accent:#b29d77;--edge:#bbc1b2;--metal-shadow:#949f9f;--panel-deep:#f0f6f3;--paper:#e7f0ec;--leaf:#81a6ad;--leaf-light:#d5e9e6;--gem:#a8d4cf;--radar:#7e9baf;--radar-fill:#b6d2dd69;--radar-grid:#bfced5;--background:#e5f0ed;--background-deep:#bfd6d5;--ornament-size:98px;--portrait-ornament-size:84px}\n.gene-tier-2{--ink:#354c69;--subtle:#72869f;--accent:#afa187;--edge:#b4bdcc;--metal-shadow:#8d9bb0;--panel-deep:#eef3f9;--paper:#e7edf6;--leaf:#829bbd;--leaf-light:#d5e1f1;--gem:#aac6e5;--radar:#8596c1;--radar-fill:#aebcde70;--radar-grid:#c1c9db;--background:#e2e9f5;--background-deep:#bccbdf;--ornament-size:110px;--portrait-ornament-size:94px}\n.gene-tier-3{--ink:#494867;--subtle:#827e9c;--accent:#b3a18b;--edge:#bdb7ca;--metal-shadow:#9992ad;--panel-deep:#f2f0f9;--paper:#ede8f6;--leaf:#9995c1;--leaf-light:#e1def2;--gem:#c0b5e6;--radar:#9b91c5;--radar-fill:#c1b7e16e;--radar-grid:#cdc5dd;--background:#eae6f4;--background-deep:#c9c1df;--ornament-size:123px;--portrait-ornament-size:104px}\n.gene-tier-4{--ink:#644b66;--subtle:#9a7f96;--accent:#b89880;--edge:#c8bac5;--metal-shadow:#ad97a6;--panel-deep:#f7eff4;--paper:#f2e8ee;--leaf:#b797b4;--leaf-light:#f0dce8;--gem:#d5b0ca;--radar:#b498bd;--radar-fill:#d2bddf6b;--radar-grid:#d9c9dc;--background:#f0e6ee;--background-deep:#dbc3d4;--ornament-size:137px;--portrait-ornament-size:115px}\n.gene-tier-5{--ink:#3c416e;--subtle:#797fa1;--accent:#b6995f;--edge:#c2b48f;--metal-shadow:#9c976f;--panel-deep:#f0effa;--paper:#e8e9f5;--leaf:#858dbf;--leaf-light:#d7ddf4;--gem:#b4b6e4;--radar:#9087c8;--radar-fill:#b8b0e476;--radar-grid:#c8c6dd;--background:#e4e5f5;--background-deep:#bdc5e1;--ornament-size:149px;--portrait-ornament-size:126px}\n.character-ambient,.character-paper-pattern{position:absolute;inset:0;pointer-events:none;overflow:hidden;color:var(--leaf)}.character-ambient{opacity:.23}.character-ambient .floral-watermark{width:100%;height:100%;display:block}.character-paper-pattern{opacity:.075;border-radius:25px}.character-paper-pattern .floral-watermark{width:100%;height:100%;display:block}\n.character-card{display:grid;grid-template-columns:344px minmax(0,1fr);gap:16px;align-items:stretch;position:relative;z-index:1}.portrait-column{min-width:0;display:flex;flex-direction:column;justify-content:center}\n.character-sheet .companion-panel{position:relative;min-width:0;border:2px solid var(--edge);border-radius:var(--frame-radius);background:radial-gradient(ellipse at 15% 6%,#ffffffbd,transparent 49%),linear-gradient(155deg,var(--panel),var(--panel-deep));box-shadow:inset 0 0 0 2px #ffffffb5,inset 0 0 0 3px color-mix(in srgb,var(--metal-shadow) 35%,transparent),0 2px 8px #52608026}\n.character-sheet .companion-panel:before{content:\"\";position:absolute;inset:7px;border:1px solid color-mix(in srgb,var(--edge) 65%,#ffffff);border-radius:calc(var(--frame-radius) - 7px);pointer-events:none}\n.character-sheet .companion-panel:after{content:\"\";position:absolute;inset:3px;border:1px solid #fffdf3b3;border-radius:calc(var(--frame-radius) - 2px);pointer-events:none}\n.character-sheet .panel-corner{position:absolute;width:var(--ornament-size);height:var(--ornament-size);pointer-events:none;z-index:3;color:var(--accent)}.panel-corner svg{width:100%;height:100%;display:block;overflow:visible;filter:drop-shadow(0 1px 1px #52608035)}.corner-tl{top:-13px;left:-13px}.corner-tr{top:-13px;right:-13px;transform:rotate(90deg)}.corner-bl{bottom:-13px;left:-13px;transform:rotate(-90deg)}.corner-br{bottom:-13px;right:-13px;transform:rotate(180deg)}\n.character-sheet .companion-portrait{padding:10px;border-radius:18px;flex:0 0 auto;background:linear-gradient(135deg,#fffefa 2%,var(--edge) 18%,#ffffff 30%,var(--accent) 41%,#fffdf2 51%,var(--edge) 71%,#ffffff 82%,var(--metal-shadow) 100%);box-shadow:inset 0 0 0 1px #ffffffd9,0 3px 9px #51628038}\n.character-sheet .companion-portrait:before{inset:5px;border-radius:12px;border-color:#fffdf1cf;box-shadow:0 0 0 1px color-mix(in srgb,var(--accent) 43%,transparent)}.character-sheet .companion-portrait:after{inset:2px;border-radius:15px;border-color:#fffefa9c}.character-sheet .companion-portrait .panel-corner{width:var(--portrait-ornament-size);height:var(--portrait-ornament-size)}\n.character-sheet .portrait-window{height:auto;border:2px solid var(--edge);border-radius:6px;overflow:hidden;background:var(--paper)}.character-artwork{display:block;width:100%;height:auto;object-fit:contain}.character-sheet .portrait-placeholder,.character-sheet .placeholder{height:100%;width:100%;display:flex;align-items:center;justify-content:center;flex-direction:column;gap:12px;color:var(--subtle);font:21px/1.5 \"Microsoft YaHei\",sans-serif}.character-sheet .placeholder small{font-size:16px}.character-sheet .portrait-window>.placeholder{height:448px}\n.character-stats{padding:17px 18px 15px;display:flex;flex-direction:column}.character-stats>.character-identity,.character-stats>.combat-row,.character-stats>.resource,.character-stats>.attribute-panel{position:relative;z-index:4}\n.character-identity{text-align:center;min-width:0}.title-image-slot{position:absolute;inset:12px;display:flex;align-items:center;justify-content:center;min-width:0;z-index:4;pointer-events:none}.title-artwork{display:block;width:auto;height:auto;max-width:100%;max-height:min(110px,100%);object-fit:contain;filter:drop-shadow(0 2px 3px #17213a70)}.title-artwork[alt=\"\u79F0\u53F7\u56FE\u7247\u5360\u4F4D\"]{max-width:min(244px,100%);max-height:min(44px,100%)}\n.character-name{font-size:43px;line-height:1.17;font-weight:700;letter-spacing:3px;margin:0 0 3px;overflow-wrap:anywhere;color:var(--ink);text-shadow:0 1px 0 #ffffff}.character-level{font:27px/1.25 Georgia,\"Microsoft YaHei\",serif;margin:0;color:var(--subtle);display:flex;align-items:baseline;justify-content:center;gap:7px;min-width:0}.character-level strong{font-size:28px;font-weight:500;overflow-wrap:anywhere;min-width:0}\n.gene-panel{position:relative;margin:8px auto 0;padding:4px 12px 4px 7px;display:flex;align-items:center;justify-content:center;gap:6px;width:fit-content;max-width:100%;border:1px solid color-mix(in srgb,var(--edge) 80%,#ffffff);border-radius:7px;background:linear-gradient(#ffffffd6,color-mix(in srgb,var(--paper) 75%,#ffffff));box-shadow:inset 0 0 0 2px #ffffffaa,0 1px 2px #54628210}\n.gene-panel:before,.gene-panel:after{content:\"\";position:absolute;top:50%;width:6px;height:6px;background:var(--light-edge);border:1px solid var(--edge);transform:translateY(-50%) rotate(45deg)}.gene-panel:before{left:-4px}.gene-panel:after{right:-4px}.gene-crest{flex:0 0 26px;height:29px;color:var(--accent)}.gene-crest svg{display:block;width:100%;height:100%}.gene-rank{font-size:18px;line-height:1.35;letter-spacing:1px;overflow-wrap:anywhere;font-weight:700;text-align:center;min-width:0;color:var(--ink)}\n.combat-row{display:flex;align-items:center;gap:8px;margin:14px 0 12px;min-width:0}.power-icon{flex:0 0 42px;height:42px;display:grid;place-items:center;border-radius:50%;border:1px solid var(--accent);box-shadow:inset 0 0 0 3px #fffef3b5;color:var(--accent);background:radial-gradient(ellipse at 38% 25%,#fffdf0,color-mix(in srgb,var(--accent) 14%,var(--panel)))}.power-icon svg{width:27px;height:27px;stroke:currentColor;fill:currentColor;stroke-width:2;stroke-linejoin:round}.power-label{font-size:21px;white-space:nowrap}.power-value{min-width:0;flex:1;text-align:right;overflow-wrap:anywhere;font:35px/1.2 Georgia,\"Microsoft YaHei\",serif;color:var(--accent);letter-spacing:-1px}\n.companion-resource{font-family:\"Microsoft YaHei\",sans-serif;margin-bottom:12px}.resource-heading{display:flex;justify-content:space-between;align-items:baseline;gap:8px;font-size:17px;color:var(--subtle);line-height:1.35}.resource-label{display:flex;align-items:center;gap:6px;font-weight:600;flex-shrink:0}.resource-label svg{width:18px;height:18px;fill:var(--health)}.resource-number{text-align:right;overflow-wrap:anywhere;min-width:0;font-size:15px}\n.resource-track{height:18px;position:relative;margin-top:5px;border:1px solid var(--track-edge);border-radius:10px;overflow:hidden;background:linear-gradient(var(--track),#f2f5ed);box-shadow:inset 0 1px 2px #6e805825}.resource-fill{display:block;position:absolute;top:0;bottom:0;left:0;border-radius:9px;background:linear-gradient(var(--health-light),var(--health) 55%,color-mix(in srgb,var(--health) 80%,var(--health-light)));border-right:1px solid #fff9}.resource-percent{position:absolute;inset:0;display:grid;place-items:center;font:11px/1 \"Microsoft YaHei\",sans-serif;color:#fffef7;text-shadow:0 1px 2px #435443}.exp-resource .resource-heading{color:var(--subtle)}.exp-resource .resource-label svg{fill:var(--radar)}.exp-resource .resource-track{height:16px;border-color:color-mix(in srgb,var(--radar) 60%,#fff);background:var(--paper)}.exp-resource .resource-fill{background:linear-gradient(var(--leaf-light),var(--radar) 56%,var(--leaf))}\n.companion-attributes{margin-top:5px;min-width:0;flex:1;display:flex;flex-direction:column;justify-content:center}.ornament-heading{display:flex;align-items:center;justify-content:center;gap:7px;margin:0 0 3px;font-size:22px;font-weight:700;letter-spacing:2px;line-height:1.3;color:var(--ink)}.ornament-heading:before,.ornament-heading:after{content:\"\";height:1px;flex:1;background:linear-gradient(90deg,transparent,var(--edge))}.ornament-heading:after{transform:rotate(180deg)}.heading-star{font-size:12px;color:var(--accent);letter-spacing:0}.companion-radar{width:100%;height:260px;display:block;overflow:visible}\n.radar-grid{fill:none;stroke:var(--radar-grid);stroke-width:1.1}.radar-axis{stroke:var(--radar-grid);stroke-width:1;opacity:.8}.radar-area{fill:var(--radar-fill);stroke:var(--radar);stroke-width:2.3;stroke-linejoin:round}.radar-dot{fill:#fffefa;stroke:var(--radar);stroke-width:2}.radar-label{font:19px \"KaiTi\",\"STKaiti\",\"Microsoft YaHei\",sans-serif;fill:var(--ink)}.radar-value{font:20px Georgia,\"Microsoft YaHei\",serif;fill:var(--ink)}.radar-scale{font:11px Georgia,serif;fill:var(--subtle)}.empty-attributes{padding:45px 10px;color:var(--subtle);font:18px/1.6 \"Microsoft YaHei\",sans-serif;text-align:center}.custom-attributes{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:5px 10px;margin:6px 0 0;font:15px/1.5 \"Microsoft YaHei\",sans-serif;color:var(--subtle)}.custom-attributes span{overflow-wrap:anywhere}.custom-attributes b{font-weight:500;color:var(--ink)}\n";
2
+ export declare const characterCardStyles = "\n*{box-sizing:border-box}body{margin:0;width:760px}#capture{width:760px;position:relative;overflow:hidden;font-family:\"KaiTi\",\"STKaiti\",\"Microsoft YaHei\",sans-serif}\n.character-sheet{--ink:#374556;--subtle:#748192;--accent:#9b927d;--edge:#aab4bd;--light-edge:#fffefa;--metal-shadow:#8f9ba8;--panel:#fffefa;--panel-deep:#f0f3f5;--paper:#e8edf0;--flower:#fffefa;--leaf:#9aaabb;--leaf-light:#e2e9ed;--gem:#bbc9d4;--track:#e2e7dd;--track-edge:#afbea9;--health:#759569;--health-light:#b4cea2;--radar:#8995ae;--radar-fill:#aebbdc66;--radar-grid:#c4cbd7;--background:#dce3e9;--background-deep:#bac7d3;--ornament-size:86px;--portrait-ornament-size:74px;--frame-radius:27px;--panel-border:2px;padding:22px;color:var(--ink);background:radial-gradient(ellipse at 12% 6%,#fffefaef,transparent 51%),radial-gradient(ellipse at 85% 90%,#f6f8ffc4,transparent 51%),radial-gradient(ellipse at 78% 12%,#ffffff80,transparent 38%),linear-gradient(145deg,var(--background-deep),var(--background) 48%,var(--background-deep))}\n.gene-tier-0{--ink:#435162;--subtle:#7c8795;--accent:#989081;--edge:#b3bac1;--leaf:#9caab8;--leaf-light:#e4e9ef;--gem:#cbd3de;--background:#e3e8ed;--background-deep:#c6cfd9}\n.gene-tier-1{--ink:#365762;--subtle:#718b90;--accent:#b29d77;--edge:#bbc1b2;--metal-shadow:#949f9f;--panel-deep:#f0f6f3;--paper:#e7f0ec;--leaf:#81a6ad;--leaf-light:#d5e9e6;--gem:#a8d4cf;--radar:#7e9baf;--radar-fill:#b6d2dd69;--radar-grid:#bfced5;--background:#e5f0ed;--background-deep:#bfd6d5;--ornament-size:98px;--portrait-ornament-size:84px}\n.gene-tier-2{--ink:#354c69;--subtle:#72869f;--accent:#afa187;--edge:#b4bdcc;--metal-shadow:#8d9bb0;--panel-deep:#eef3f9;--paper:#e7edf6;--leaf:#829bbd;--leaf-light:#d5e1f1;--gem:#aac6e5;--radar:#8596c1;--radar-fill:#aebcde70;--radar-grid:#c1c9db;--background:#e2e9f5;--background-deep:#bccbdf;--ornament-size:110px;--portrait-ornament-size:94px}\n.gene-tier-3{--ink:#494867;--subtle:#827e9c;--accent:#b3a18b;--edge:#bdb7ca;--metal-shadow:#9992ad;--panel-deep:#f2f0f9;--paper:#ede8f6;--leaf:#9995c1;--leaf-light:#e1def2;--gem:#c0b5e6;--radar:#9b91c5;--radar-fill:#c1b7e16e;--radar-grid:#cdc5dd;--background:#eae6f4;--background-deep:#c9c1df;--ornament-size:123px;--portrait-ornament-size:104px}\n.gene-tier-4{--ink:#644b66;--subtle:#9a7f96;--accent:#b89880;--edge:#c8bac5;--metal-shadow:#ad97a6;--panel-deep:#f7eff4;--paper:#f2e8ee;--leaf:#b797b4;--leaf-light:#f0dce8;--gem:#d5b0ca;--radar:#b498bd;--radar-fill:#d2bddf6b;--radar-grid:#d9c9dc;--background:#f0e6ee;--background-deep:#dbc3d4;--ornament-size:137px;--portrait-ornament-size:115px}\n.gene-tier-5{--ink:#3c416e;--subtle:#797fa1;--accent:#b6995f;--edge:#c2b48f;--metal-shadow:#9c976f;--panel-deep:#f0effa;--paper:#e8e9f5;--leaf:#858dbf;--leaf-light:#d7ddf4;--gem:#b4b6e4;--radar:#9087c8;--radar-fill:#b8b0e476;--radar-grid:#c8c6dd;--background:#e4e5f5;--background-deep:#bdc5e1;--ornament-size:149px;--portrait-ornament-size:126px}\n.character-ambient,.character-paper-pattern{position:absolute;inset:0;pointer-events:none;overflow:hidden;color:var(--leaf)}.character-ambient{opacity:.23}.character-ambient .floral-watermark{width:100%;height:100%;display:block}.character-paper-pattern{opacity:.075;border-radius:25px}.character-paper-pattern .floral-watermark{width:100%;height:100%;display:block}\n.character-card{display:grid;grid-template-columns:344px minmax(0,1fr);gap:16px;align-items:stretch;position:relative;z-index:1}.portrait-column{min-width:0;display:flex;flex-direction:column;justify-content:center}\n.character-sheet .companion-panel{position:relative;min-width:0;border:2px solid var(--edge);border-radius:var(--frame-radius);background:radial-gradient(ellipse at 15% 6%,#ffffffbd,transparent 49%),linear-gradient(155deg,var(--panel),var(--panel-deep));box-shadow:inset 0 0 0 2px #ffffffb5,inset 0 0 0 3px color-mix(in srgb,var(--metal-shadow) 35%,transparent),0 2px 8px #52608026}\n.character-sheet .companion-panel:before{content:\"\";position:absolute;inset:7px;border:1px solid color-mix(in srgb,var(--edge) 65%,#ffffff);border-radius:calc(var(--frame-radius) - 7px);pointer-events:none}\n.character-sheet .companion-panel:after{content:\"\";position:absolute;inset:3px;border:1px solid #fffdf3b3;border-radius:calc(var(--frame-radius) - 2px);pointer-events:none}\n.character-sheet .panel-corner{position:absolute;width:var(--ornament-size);height:var(--ornament-size);pointer-events:none;z-index:3;color:var(--accent)}.panel-corner svg{width:100%;height:100%;display:block;overflow:visible;filter:drop-shadow(0 1px 1px #52608035)}.corner-tl{top:-13px;left:-13px}.corner-tr{top:-13px;right:-13px;transform:rotate(90deg)}.corner-bl{bottom:-13px;left:-13px;transform:rotate(-90deg)}.corner-br{bottom:-13px;right:-13px;transform:rotate(180deg)}\n.character-sheet .companion-portrait{padding:10px;border-radius:18px;flex:0 0 auto;background:linear-gradient(135deg,#fffefa 2%,var(--edge) 18%,#ffffff 30%,var(--accent) 41%,#fffdf2 51%,var(--edge) 71%,#ffffff 82%,var(--metal-shadow) 100%);box-shadow:inset 0 0 0 1px #ffffffd9,0 3px 9px #51628038}\n.character-sheet .companion-portrait:before{inset:5px;border-radius:12px;border-color:#fffdf1cf;box-shadow:0 0 0 1px color-mix(in srgb,var(--accent) 43%,transparent)}.character-sheet .companion-portrait:after{inset:2px;border-radius:15px;border-color:#fffefa9c}.character-sheet .companion-portrait .panel-corner{width:var(--portrait-ornament-size);height:var(--portrait-ornament-size)}\n.character-sheet .portrait-window{height:auto;border:2px solid var(--edge);border-radius:6px;overflow:hidden;background:var(--paper)}.character-artwork{display:block;width:100%;height:auto;object-fit:contain}.character-sheet .portrait-placeholder,.character-sheet .placeholder{height:100%;width:100%;display:flex;align-items:center;justify-content:center;flex-direction:column;gap:12px;color:var(--subtle);font:21px/1.5 \"Microsoft YaHei\",sans-serif}.character-sheet .placeholder small{font-size:16px}.character-sheet .portrait-window>.placeholder{height:448px}\n.character-stats{padding:17px 18px 15px;display:flex;flex-direction:column}.character-stats>.character-identity,.character-stats>.resource,.character-stats>.attribute-panel{position:relative;z-index:4}\n.character-identity{text-align:center;min-width:0}.title-image-slot{position:absolute;inset:12px;display:flex;align-items:center;justify-content:center;min-width:0;z-index:4;pointer-events:none}.title-artwork{display:block;width:auto;height:auto;max-width:100%;max-height:min(110px,100%);object-fit:contain;filter:drop-shadow(0 2px 3px #17213a70)}.title-artwork[alt=\"\u79F0\u53F7\u56FE\u7247\u5360\u4F4D\"]{max-width:min(244px,100%);max-height:min(44px,100%)}\n.character-name{font-size:43px;line-height:1.17;font-weight:700;letter-spacing:3px;margin:0 0 3px;overflow-wrap:anywhere;color:var(--ink);text-shadow:0 1px 0 #ffffff}.character-level{font:27px/1.25 Georgia,\"Microsoft YaHei\",serif;margin:0;color:var(--subtle);display:flex;align-items:baseline;justify-content:center;gap:7px;min-width:0}.character-level strong{font-size:28px;font-weight:500;overflow-wrap:anywhere;min-width:0}\n.gene-panel{position:relative;margin:8px auto 0;padding:4px 12px 4px 7px;display:flex;align-items:center;justify-content:center;gap:6px;width:fit-content;max-width:100%;border:1px solid color-mix(in srgb,var(--edge) 80%,#ffffff);border-radius:7px;background:linear-gradient(#ffffffd6,color-mix(in srgb,var(--paper) 75%,#ffffff));box-shadow:inset 0 0 0 2px #ffffffaa,0 1px 2px #54628210}\n.gene-panel:before,.gene-panel:after{content:\"\";position:absolute;top:50%;width:6px;height:6px;background:var(--light-edge);border:1px solid var(--edge);transform:translateY(-50%) rotate(45deg)}.gene-panel:before{left:-4px}.gene-panel:after{right:-4px}.gene-crest{flex:0 0 26px;height:29px;color:var(--accent)}.gene-crest svg{display:block;width:100%;height:100%}.gene-rank{font-size:18px;line-height:1.35;letter-spacing:1px;overflow-wrap:anywhere;font-weight:700;text-align:center;min-width:0;color:var(--ink)}\n.exploration-rank-row{display:flex;align-items:center;justify-content:center;gap:7px;margin:10px 0 0;font:16px/1.4 \"KaiTi\",\"STKaiti\",\"Microsoft YaHei\",sans-serif;letter-spacing:1px;color:var(--subtle)}.exploration-rank-row>svg{width:17px;height:17px;flex-shrink:0;fill:none;stroke:var(--accent);stroke-width:1.5;stroke-linecap:round;stroke-linejoin:round}.exploration-rank-row>span{white-space:nowrap}.exploration-rank-row strong{font-size:18px;font-weight:700;color:var(--ink);overflow-wrap:anywhere;min-width:0}\n.combat-row{display:flex;align-items:center;justify-content:center;gap:8px;margin:12px 0 0;padding:0 6px;min-width:0;font:28px/1.2 \"KaiTi\",\"STKaiti\",\"Microsoft YaHei\",sans-serif;position:relative;z-index:4}.power-icon{flex:0 0 34px;height:34px;display:grid;place-items:center;color:var(--accent)}.power-icon svg{display:block;width:34px;height:34px;overflow:visible}.power-label{font:inherit;white-space:nowrap}.power-value{min-width:0;flex:0 1 auto;text-align:center;overflow-wrap:anywhere;font:inherit;color:var(--accent);letter-spacing:0}\n.companion-resource{font-family:\"Microsoft YaHei\",sans-serif;margin-bottom:12px}.resource-heading{display:flex;justify-content:space-between;align-items:baseline;gap:8px;font-size:17px;color:var(--subtle);line-height:1.35}.resource-label{display:flex;align-items:center;gap:6px;font-weight:600;flex-shrink:0}.resource-label svg{width:18px;height:18px;fill:var(--health)}.resource-number{text-align:right;overflow-wrap:anywhere;min-width:0;font-size:15px}\n.resource-track{height:18px;position:relative;margin-top:5px;border:1px solid var(--track-edge);border-radius:10px;overflow:hidden;background:linear-gradient(var(--track),#f2f5ed);box-shadow:inset 0 1px 2px #6e805825}.resource-fill{display:block;position:absolute;top:0;bottom:0;left:0;border-radius:9px;background:linear-gradient(var(--health-light),var(--health) 55%,color-mix(in srgb,var(--health) 80%,var(--health-light)));border-right:1px solid #fff9}.resource-percent{position:absolute;inset:0;display:grid;place-items:center;font:11px/1 \"Microsoft YaHei\",sans-serif;color:#fffef7;text-shadow:0 1px 2px #435443}.exp-resource{width:calc(100% - 20px);align-self:center;margin:0 0 14px}.exp-resource .resource-track{height:6px;margin-top:0;border-color:#d5c48b;border-radius:4px;background:#eee9d7;box-shadow:inset 0 1px 1px #9d82351a}.exp-resource .resource-fill{border-radius:3px;border-right:0;background:linear-gradient(#fff0a0,#e8bd48 60%,#d6a934)}\n.hp-resource{display:flex;align-items:center;margin-top:16px;margin-bottom:0}.hp-resource .resource-track{flex:1;min-width:0;height:20px;margin-top:0}.hp-bar-value{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;padding:0 8px;white-space:nowrap;font:12px/1 \"Microsoft YaHei\",sans-serif;color:#fffef7;text-shadow:0 1px 2px #435443,0 0 2px #435443}\n.companion-attributes{margin-top:5px;min-width:0;flex:1;display:flex;flex-direction:column;justify-content:center}.attribute-divider{height:28px;margin:0 0 3px;display:flex;align-items:center;justify-content:center}.attribute-divider svg{display:block;width:100%;height:36px}.companion-radar{width:100%;height:260px;display:block;overflow:visible}\n.radar-grid{fill:none;stroke:var(--radar-grid);stroke-width:1.1}.radar-axis{stroke:var(--radar-grid);stroke-width:1;opacity:.8}.radar-area{fill:var(--radar-fill);stroke:var(--radar);stroke-width:2.3;stroke-linejoin:round}.radar-dot{fill:#fffefa;stroke:var(--radar);stroke-width:2}.radar-label{font:19px \"KaiTi\",\"STKaiti\",\"Microsoft YaHei\",sans-serif;fill:var(--ink)}.radar-value{font:20px Georgia,\"Microsoft YaHei\",serif;fill:var(--ink)}.radar-scale{font:11px Georgia,serif;fill:var(--subtle)}.empty-attributes{padding:45px 10px;color:var(--subtle);font:18px/1.6 \"Microsoft YaHei\",sans-serif;text-align:center}.custom-attributes{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:5px 10px;margin:6px 0 0;font:15px/1.5 \"Microsoft YaHei\",sans-serif;color:var(--subtle)}.custom-attributes span{overflow-wrap:anywhere}.custom-attributes b{font-weight:500;color:var(--ink)}\n";
@@ -0,0 +1,11 @@
1
+ export type InlineCommandSpan = {
2
+ start: number;
3
+ end: number;
4
+ label: string;
5
+ command: string;
6
+ aliased?: true;
7
+ };
8
+ /** Boxed commands may use [label](command:input) to show a different action label. */
9
+ export declare function inlineCommandSpans(content: string): InlineCommandSpan[];
10
+ /** Keep aliased actions usable when the platform falls back to ordinary text. */
11
+ export declare function renderPlainInlineCommandAliases(content: string): string;
@@ -57,7 +57,7 @@ export interface CharacterCardData {
57
57
  }>;
58
58
  /** 当前基因锁大阶的配置名称,不包含层级;未开启时为基因锁未开启。 */
59
59
  geneLockRankName?: string;
60
- /** 当前称号的图片链接;未获得称号或图片不可用时省略,客户端显示图片占位。 */
60
+ /** 当前称号的图片链接;未获得称号或图片不可用时省略,客户端不显示称号横幅。 */
61
61
  titleImageUrl?: string;
62
62
  /** 已获得的真实基因锁大阶(0~5);0 表示尚未解锁。 */
63
63
  geneLockTier?: number;
@@ -78,6 +78,8 @@ export interface CharacterCardData {
78
78
  type?: CompanionSkillType;
79
79
  artworkUrl?: string;
80
80
  }>;
81
+ /** 玩家当前的轮回探索品阶(F~SSS),与角色等级、基因锁阶级分开显示。 */
82
+ explorationRank?: string;
81
83
  }
82
84
  export declare enum CompanionCardKind {
83
85
  pet = "pet",
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "koishi-plugin-dew-bot",
3
3
  "description": "基于《无限恐怖》小说改编的 RPG 机器人。踏入主神空间,与伙伴迎接轮回试炼,书写属于你的无限冒险。",
4
- "version": "1.0.261",
4
+ "version": "1.0.262",
5
5
  "main": "lib/index.js",
6
6
  "typings": "lib/index.d.ts",
7
7
  "files": [
@@ -19,15 +19,13 @@ function radar(card: BloodlineCardData): string {
19
19
  const attribute = card.attributes.find(value => value.id === id)
20
20
  return { name: attribute?.name?.trim() || name, value: finite(attribute?.value ?? 0) }
21
21
  })
22
- const max = Math.max(1, ...attributes.map(attribute => Math.max(0, attribute.value)))
23
- const magnitude = 10 ** Math.floor(Math.log10(max))
24
- const ceiling = Math.ceil(max / magnitude) * magnitude
22
+ const ceiling = Math.max(0, ...attributes.map(attribute => attribute.value))
25
23
  const point = (index: number, radius: number) => {
26
24
  const angle = -Math.PI / 2 + index * Math.PI / 3
27
25
  return [320 + Math.cos(angle) * radius, 190 + Math.sin(angle) * radius]
28
26
  }
29
27
  const polygon = (radius: number) => attributes.map((_, index) => point(index, radius).join(',')).join(' ')
30
- const data = attributes.map((attribute, index) => point(index, 122 * Math.max(0, Math.min(1, attribute.value / ceiling))))
28
+ const data = attributes.map((attribute, index) => point(index, 122 * (ceiling > 0 ? Math.max(0, attribute.value / ceiling) : 0)))
31
29
  return `<div class="radar-panel"><svg class="bloodline-radar" viewBox="0 0 640 396" role="img" aria-label="血统六维属性加成雷达图" xmlns="http://www.w3.org/2000/svg">
32
30
  <defs><linearGradient id="bloodline-radar-fill" x1="0" y1="0" x2="1" y2="1"><stop stop-color="var(--quality-accent)" stop-opacity=".35"/><stop offset="1" stop-color="var(--quality-accent)" stop-opacity=".12"/></linearGradient></defs>
33
31
  ${[.2, .4, .6, .8, 1].map(scale => `<polygon points="${polygon(122 * scale)}" fill="none" stroke="#b9bdad" stroke-width="1.2"/>`).join('')}
@@ -26,7 +26,7 @@ export async function layoutCard(document: Document = globalThis.document) {
26
26
  portrait.replaceWith(placeholder)
27
27
  }
28
28
  }
29
- // Title artwork is independent of the frame and always has its own placeholder.
29
+ // Requested title artwork has an independent fallback; untitled cards have no title element.
30
30
  const titleArtwork = document.querySelector<HTMLImageElement>('.title-artwork')
31
31
  if (titleArtwork) {
32
32
  try { await titleArtwork.decode() } catch {
@@ -1,4 +1,4 @@
1
- /** The unavailable title remains an independent image rather than a text badge. */
1
+ /** Requested but unavailable title artwork has an independent image fallback. */
2
2
  export const characterTitlePlaceholder = 'data:image/svg+xml;charset=utf-8,' + encodeURIComponent(`<svg xmlns="http://www.w3.org/2000/svg" width="420" height="84" viewBox="0 0 420 84">
3
3
  <defs>
4
4
  <linearGradient id="metal" x2="0" y2="1"><stop stop-color="#fffef4"/><stop offset=".3" stop-color="#bbb6a2"/><stop offset=".52" stop-color="#fffdf0"/><stop offset="1" stop-color="#9ba6bc"/></linearGradient>
@@ -53,3 +53,11 @@ export const characterFloralWatermark = `<svg class="floral-watermark" viewBox="
53
53
  export function characterGeneCrest(tier: number): string {
54
54
  return `<svg viewBox="0 0 60 60" xmlns="http://www.w3.org/2000/svg" aria-hidden="true"><path d="m30 4 20 10v20L30 55 10 34V14Z" fill="var(--paper)" stroke="currentColor" stroke-width="1.6"/><path d="m30 9 15 8v16L30 49 15 33V17Z" fill="none" stroke="var(--light-edge)" stroke-width="1"/><path d="M23 27v-6a7 7 0 0 1 14 0v6" fill="none" stroke="currentColor" stroke-width="2.5"/><rect x="20" y="26" width="20" height="15" rx="3" fill="currentColor"/><circle cx="30" cy="32" r="2" fill="var(--light-edge)"/><path d="M30 33v4" stroke="var(--light-edge)" stroke-width="1.8"/>${tier >= 3 ? '<path d="m30 2 3 4-3 4-3-4Z" fill="var(--gem)" stroke="var(--light-edge)"/>' : ''}${tier >= 5 ? '<path d="m18 12 1-7 6 4 5-7 5 7 6-4 1 7Z" fill="var(--accent)" stroke="var(--light-edge)" stroke-width=".7"/>' : ''}</svg>`
55
55
  }
56
+
57
+ /** Quiet botanical divider above the six-axis radar; carries no visible label. */
58
+ export const characterAttributeDivider = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 310 36">
59
+ ${ornamentPaint}
60
+ <g fill="none" stroke="var(--edge)" stroke-width=".8"><path d="M5 18h91m118 0h91"/><path d="M75 18c18 0 29-8 47-8m66 0c18 0 29 8 47 8"/><path d="M102 18c12 0 21 6 31 6m44 0c10 0 19-6 31-6"/></g>
61
+ <g fill="url(#character-leaf)" stroke="var(--light-edge)" stroke-width=".6"><path d="M120 11c-10-1-14-6-16-10 10 0 17 3 16 10Zm14 12c-10 2-18-1-22-6 10-2 18-1 22 6Zm56-12c10-1 14-6 16-10-10 0-17 3-16 10Zm-14 12c10 2 18-1 22-6-10-2-18-1-22 6Z"/></g>
62
+ ${flower(155, 18, .5)}<g fill="var(--accent)"><path d="m94 18 3-3 3 3-3 3Zm116 0 3-3 3 3-3 3Z"/></g>
63
+ </svg>`
@@ -17,14 +17,16 @@ export const characterCardStyles = `
17
17
  .character-sheet .companion-portrait{padding:10px;border-radius:18px;flex:0 0 auto;background:linear-gradient(135deg,#fffefa 2%,var(--edge) 18%,#ffffff 30%,var(--accent) 41%,#fffdf2 51%,var(--edge) 71%,#ffffff 82%,var(--metal-shadow) 100%);box-shadow:inset 0 0 0 1px #ffffffd9,0 3px 9px #51628038}
18
18
  .character-sheet .companion-portrait:before{inset:5px;border-radius:12px;border-color:#fffdf1cf;box-shadow:0 0 0 1px color-mix(in srgb,var(--accent) 43%,transparent)}.character-sheet .companion-portrait:after{inset:2px;border-radius:15px;border-color:#fffefa9c}.character-sheet .companion-portrait .panel-corner{width:var(--portrait-ornament-size);height:var(--portrait-ornament-size)}
19
19
  .character-sheet .portrait-window{height:auto;border:2px solid var(--edge);border-radius:6px;overflow:hidden;background:var(--paper)}.character-artwork{display:block;width:100%;height:auto;object-fit:contain}.character-sheet .portrait-placeholder,.character-sheet .placeholder{height:100%;width:100%;display:flex;align-items:center;justify-content:center;flex-direction:column;gap:12px;color:var(--subtle);font:21px/1.5 "Microsoft YaHei",sans-serif}.character-sheet .placeholder small{font-size:16px}.character-sheet .portrait-window>.placeholder{height:448px}
20
- .character-stats{padding:17px 18px 15px;display:flex;flex-direction:column}.character-stats>.character-identity,.character-stats>.combat-row,.character-stats>.resource,.character-stats>.attribute-panel{position:relative;z-index:4}
20
+ .character-stats{padding:17px 18px 15px;display:flex;flex-direction:column}.character-stats>.character-identity,.character-stats>.resource,.character-stats>.attribute-panel{position:relative;z-index:4}
21
21
  .character-identity{text-align:center;min-width:0}.title-image-slot{position:absolute;inset:12px;display:flex;align-items:center;justify-content:center;min-width:0;z-index:4;pointer-events:none}.title-artwork{display:block;width:auto;height:auto;max-width:100%;max-height:min(110px,100%);object-fit:contain;filter:drop-shadow(0 2px 3px #17213a70)}.title-artwork[alt="称号图片占位"]{max-width:min(244px,100%);max-height:min(44px,100%)}
22
22
  .character-name{font-size:43px;line-height:1.17;font-weight:700;letter-spacing:3px;margin:0 0 3px;overflow-wrap:anywhere;color:var(--ink);text-shadow:0 1px 0 #ffffff}.character-level{font:27px/1.25 Georgia,"Microsoft YaHei",serif;margin:0;color:var(--subtle);display:flex;align-items:baseline;justify-content:center;gap:7px;min-width:0}.character-level strong{font-size:28px;font-weight:500;overflow-wrap:anywhere;min-width:0}
23
23
  .gene-panel{position:relative;margin:8px auto 0;padding:4px 12px 4px 7px;display:flex;align-items:center;justify-content:center;gap:6px;width:fit-content;max-width:100%;border:1px solid color-mix(in srgb,var(--edge) 80%,#ffffff);border-radius:7px;background:linear-gradient(#ffffffd6,color-mix(in srgb,var(--paper) 75%,#ffffff));box-shadow:inset 0 0 0 2px #ffffffaa,0 1px 2px #54628210}
24
24
  .gene-panel:before,.gene-panel:after{content:"";position:absolute;top:50%;width:6px;height:6px;background:var(--light-edge);border:1px solid var(--edge);transform:translateY(-50%) rotate(45deg)}.gene-panel:before{left:-4px}.gene-panel:after{right:-4px}.gene-crest{flex:0 0 26px;height:29px;color:var(--accent)}.gene-crest svg{display:block;width:100%;height:100%}.gene-rank{font-size:18px;line-height:1.35;letter-spacing:1px;overflow-wrap:anywhere;font-weight:700;text-align:center;min-width:0;color:var(--ink)}
25
- .combat-row{display:flex;align-items:center;gap:8px;margin:14px 0 12px;min-width:0}.power-icon{flex:0 0 42px;height:42px;display:grid;place-items:center;border-radius:50%;border:1px solid var(--accent);box-shadow:inset 0 0 0 3px #fffef3b5;color:var(--accent);background:radial-gradient(ellipse at 38% 25%,#fffdf0,color-mix(in srgb,var(--accent) 14%,var(--panel)))}.power-icon svg{width:27px;height:27px;stroke:currentColor;fill:currentColor;stroke-width:2;stroke-linejoin:round}.power-label{font-size:21px;white-space:nowrap}.power-value{min-width:0;flex:1;text-align:right;overflow-wrap:anywhere;font:35px/1.2 Georgia,"Microsoft YaHei",serif;color:var(--accent);letter-spacing:-1px}
25
+ .exploration-rank-row{display:flex;align-items:center;justify-content:center;gap:7px;margin:10px 0 0;font:16px/1.4 "KaiTi","STKaiti","Microsoft YaHei",sans-serif;letter-spacing:1px;color:var(--subtle)}.exploration-rank-row>svg{width:17px;height:17px;flex-shrink:0;fill:none;stroke:var(--accent);stroke-width:1.5;stroke-linecap:round;stroke-linejoin:round}.exploration-rank-row>span{white-space:nowrap}.exploration-rank-row strong{font-size:18px;font-weight:700;color:var(--ink);overflow-wrap:anywhere;min-width:0}
26
+ .combat-row{display:flex;align-items:center;justify-content:center;gap:8px;margin:12px 0 0;padding:0 6px;min-width:0;font:28px/1.2 "KaiTi","STKaiti","Microsoft YaHei",sans-serif;position:relative;z-index:4}.power-icon{flex:0 0 34px;height:34px;display:grid;place-items:center;color:var(--accent)}.power-icon svg{display:block;width:34px;height:34px;overflow:visible}.power-label{font:inherit;white-space:nowrap}.power-value{min-width:0;flex:0 1 auto;text-align:center;overflow-wrap:anywhere;font:inherit;color:var(--accent);letter-spacing:0}
26
27
  .companion-resource{font-family:"Microsoft YaHei",sans-serif;margin-bottom:12px}.resource-heading{display:flex;justify-content:space-between;align-items:baseline;gap:8px;font-size:17px;color:var(--subtle);line-height:1.35}.resource-label{display:flex;align-items:center;gap:6px;font-weight:600;flex-shrink:0}.resource-label svg{width:18px;height:18px;fill:var(--health)}.resource-number{text-align:right;overflow-wrap:anywhere;min-width:0;font-size:15px}
27
- .resource-track{height:18px;position:relative;margin-top:5px;border:1px solid var(--track-edge);border-radius:10px;overflow:hidden;background:linear-gradient(var(--track),#f2f5ed);box-shadow:inset 0 1px 2px #6e805825}.resource-fill{display:block;position:absolute;top:0;bottom:0;left:0;border-radius:9px;background:linear-gradient(var(--health-light),var(--health) 55%,color-mix(in srgb,var(--health) 80%,var(--health-light)));border-right:1px solid #fff9}.resource-percent{position:absolute;inset:0;display:grid;place-items:center;font:11px/1 "Microsoft YaHei",sans-serif;color:#fffef7;text-shadow:0 1px 2px #435443}.exp-resource .resource-heading{color:var(--subtle)}.exp-resource .resource-label svg{fill:var(--radar)}.exp-resource .resource-track{height:16px;border-color:color-mix(in srgb,var(--radar) 60%,#fff);background:var(--paper)}.exp-resource .resource-fill{background:linear-gradient(var(--leaf-light),var(--radar) 56%,var(--leaf))}
28
- .companion-attributes{margin-top:5px;min-width:0;flex:1;display:flex;flex-direction:column;justify-content:center}.ornament-heading{display:flex;align-items:center;justify-content:center;gap:7px;margin:0 0 3px;font-size:22px;font-weight:700;letter-spacing:2px;line-height:1.3;color:var(--ink)}.ornament-heading:before,.ornament-heading:after{content:"";height:1px;flex:1;background:linear-gradient(90deg,transparent,var(--edge))}.ornament-heading:after{transform:rotate(180deg)}.heading-star{font-size:12px;color:var(--accent);letter-spacing:0}.companion-radar{width:100%;height:260px;display:block;overflow:visible}
28
+ .resource-track{height:18px;position:relative;margin-top:5px;border:1px solid var(--track-edge);border-radius:10px;overflow:hidden;background:linear-gradient(var(--track),#f2f5ed);box-shadow:inset 0 1px 2px #6e805825}.resource-fill{display:block;position:absolute;top:0;bottom:0;left:0;border-radius:9px;background:linear-gradient(var(--health-light),var(--health) 55%,color-mix(in srgb,var(--health) 80%,var(--health-light)));border-right:1px solid #fff9}.resource-percent{position:absolute;inset:0;display:grid;place-items:center;font:11px/1 "Microsoft YaHei",sans-serif;color:#fffef7;text-shadow:0 1px 2px #435443}.exp-resource{width:calc(100% - 20px);align-self:center;margin:0 0 14px}.exp-resource .resource-track{height:6px;margin-top:0;border-color:#d5c48b;border-radius:4px;background:#eee9d7;box-shadow:inset 0 1px 1px #9d82351a}.exp-resource .resource-fill{border-radius:3px;border-right:0;background:linear-gradient(#fff0a0,#e8bd48 60%,#d6a934)}
29
+ .hp-resource{display:flex;align-items:center;margin-top:16px;margin-bottom:0}.hp-resource .resource-track{flex:1;min-width:0;height:20px;margin-top:0}.hp-bar-value{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;padding:0 8px;white-space:nowrap;font:12px/1 "Microsoft YaHei",sans-serif;color:#fffef7;text-shadow:0 1px 2px #435443,0 0 2px #435443}
30
+ .companion-attributes{margin-top:5px;min-width:0;flex:1;display:flex;flex-direction:column;justify-content:center}.attribute-divider{height:28px;margin:0 0 3px;display:flex;align-items:center;justify-content:center}.attribute-divider svg{display:block;width:100%;height:36px}.companion-radar{width:100%;height:260px;display:block;overflow:visible}
29
31
  .radar-grid{fill:none;stroke:var(--radar-grid);stroke-width:1.1}.radar-axis{stroke:var(--radar-grid);stroke-width:1;opacity:.8}.radar-area{fill:var(--radar-fill);stroke:var(--radar);stroke-width:2.3;stroke-linejoin:round}.radar-dot{fill:#fffefa;stroke:var(--radar);stroke-width:2}.radar-label{font:19px "KaiTi","STKaiti","Microsoft YaHei",sans-serif;fill:var(--ink)}.radar-value{font:20px Georgia,"Microsoft YaHei",serif;fill:var(--ink)}.radar-scale{font:11px Georgia,serif;fill:var(--subtle)}.empty-attributes{padding:45px 10px;color:var(--subtle);font:18px/1.6 "Microsoft YaHei",sans-serif;text-align:center}.custom-attributes{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:5px 10px;margin:6px 0 0;font:15px/1.5 "Microsoft YaHei",sans-serif;color:var(--subtle)}.custom-attributes span{overflow-wrap:anywhere}.custom-attributes b{font-weight:500;color:var(--ink)}
30
32
  `
@@ -1,7 +1,7 @@
1
1
  import type { CharacterCardData } from '../shared/face'
2
2
  import { cardEscape } from './detailCardTemplate'
3
3
  import { characterCardStyles } from './characterCardStyles'
4
- import { characterFloralWatermark, characterGeneCrest, characterTierOrnament, characterTitlePlaceholder } from './characterCardAssets'
4
+ import { characterFloralWatermark, characterGeneCrest, characterTierOrnament, characterTitlePlaceholder, characterAttributeDivider } from './characterCardAssets'
5
5
  import type { CardEntryArtwork } from './companionCardTemplate'
6
6
 
7
7
  const finite = (value: number) => Number.isFinite(value) ? value : 0
@@ -12,7 +12,6 @@ const primaryAttributes = [
12
12
  ['intelligence', '智力'], ['spirit', '精神力'], ['resistance', '抗性'],
13
13
  ] as const
14
14
  const primaryIds: Set<string> = new Set(primaryAttributes.map(([id]) => id))
15
- const heading = (label: string) => `<h2 class="ornament-heading"><span class="heading-star" aria-hidden="true">✦</span>${cardEscape(label)}<span class="heading-star" aria-hidden="true">✦</span></h2>`
16
15
  const corners = (tier: number, positions: string[] = ['tl', 'tr', 'bl', 'br']) => positions.map(position => `<span class="panel-corner corner-${position}" data-decoration-level="${tier}">${characterTierOrnament(tier)}</span>`).join('')
17
16
 
18
17
  function radar(attributes: CharacterCardData['attributes']): string {
@@ -57,13 +56,15 @@ export function characterCardHtml(card: CharacterCardData, artwork?: string, _fr
57
56
  const hpPercent = ratio(card.hp, card.maxHp) * 100
58
57
  const experience = Math.max(0, Math.min(100, finite(card.experiencePercent)))
59
58
  const geneRank = card.geneLockRankName || '基因锁未开启'
59
+ const explorationRank = card.explorationRank?.trim()
60
60
  return `<!doctype html><html lang="zh-CN"><head><meta charset="utf-8"><style>${characterCardStyles}</style></head><body><main id="capture" class="companion-sheet character-adventure character-sheet gene-tier-${tier}" data-gene-lock-tier="${tier}" data-decoration-level="${tier}"><span class="character-ambient" aria-hidden="true">${characterFloralWatermark}</span><div class="character-card companion-main">
61
61
  <div class="portrait-column"><div class="companion-panel companion-portrait character-portrait">${corners(tier)}<div class="portrait-window portrait">${portrait ? `<img id="character-image" class="character-artwork" src="${cardEscape(portrait)}" data-fallback="${cardEscape(defaultArtwork ?? '')}" alt="${cardEscape(card.name)}的形象图">` : '<div class="placeholder"><span>形象尚未生成</span><small>角色形象</small></div>'}</div>
62
- <div class="title-image-slot character-title-centered"><img id="title-image" class="title-artwork" src="${cardEscape(titleArtwork || characterTitlePlaceholder)}" data-fallback="${cardEscape(characterTitlePlaceholder)}" alt="${titleArtwork ? '称号图片' : '称号图片占位'}"></div></div></div>
63
- <section class="companion-panel companion-stats character-stats" aria-label="角色属性"><span class="character-paper-pattern" aria-hidden="true">${characterFloralWatermark}</span>${corners(tier, ['tr', 'br'])}<div class="identity character-identity"><h1 id="character-name" class="character-name" title="${cardEscape(card.name)}">${cardEscape(card.name)}</h1><p class="level character-level"><span>Lv.</span><strong id="character-level">${number(card.level)}</strong></p><div class="gene-panel"><span class="gene-crest" aria-hidden="true">${characterGeneCrest(tier)}</span><div id="gene-rank" class="gene-rank" title="${cardEscape(geneRank)}">${cardEscape(geneRank)}</div></div></div>
64
- <div class="combat-row companion-power-row"><span class="power-emblem power-icon" aria-hidden="true"><svg viewBox="0 0 42 42"><path d="m9 5 5 1 16 21-4 4L9 11Zm24 0-5 1L12 27l4 4L33 11ZM14 27l14 10m0-10L14 37M9 35l4-4m20 4-4-4"/></svg></span><span class="power-label companion-power-label">战力</span><strong id="combat-power" class="power-value companion-power">${number(card.power)}</strong></div>
65
- <div class="resource companion-resource hp-resource"><div class="resource-heading"><span class="resource-label"><svg viewBox="0 0 32 32" aria-hidden="true"><path d="M16 28C6 21 2 17 2 10a7 7 0 0 1 14-2 7 7 0 0 1 14 2c0 7-4 11-14 18Z"/></svg>生命值</span><span id="hp-numbers" class="resource-number">${number(card.hp)} / ${number(card.maxHp)}</span></div><div class="resource-track" role="progressbar" aria-label="生命值" aria-valuemin="0" aria-valuemax="100" aria-valuenow="${hpPercent}"><span class="resource-fill fill" style="width:${hpPercent}%"></span><span class="bar-percent resource-percent">${number(hpPercent)}%</span></div></div>
66
- <div class="resource companion-resource exp-resource"><div class="resource-heading"><span class="resource-label"><svg viewBox="0 0 32 32" aria-hidden="true"><path d="m16 2 4.5 9.1 10 1.4-7.3 7.1 1.7 10-8.9-4.7-8.9 4.7 1.7-10-7.3-7.1 10-1.4Z"/></svg>经验进度</span><span id="exp-number" class="resource-number">${number(experience)}%</span></div><div class="resource-track" role="progressbar" aria-label="经验进度" aria-valuemin="0" aria-valuemax="100" aria-valuenow="${experience}"><span class="resource-fill" style="width:${experience}%"></span><span class="resource-percent">${number(experience)}%</span></div></div>
67
- <section class="attribute-panel companion-attributes" aria-label="一级属性">${heading('一级属性')}${radar(card.attributes)}</section></section>
62
+ ${titleArtwork || card.titleImageUrl ? `<div class="title-image-slot character-title-centered"><img id="title-image" class="title-artwork" src="${cardEscape(titleArtwork || characterTitlePlaceholder)}" data-fallback="${cardEscape(characterTitlePlaceholder)}" alt="${titleArtwork ? '称号图片' : '称号图片占位'}"></div>` : ''}</div>
63
+ <div class="combat-row companion-power-row"><span class="power-emblem power-icon" aria-hidden="true"><svg class="crossed-weapons" viewBox="0 0 42 42"><g fill="currentColor" stroke="var(--panel)" stroke-width=".9" stroke-linejoin="round"><g transform="rotate(-45 21 21)"><path d="M21 2 26 9v14l-5 4-5-4V9Z"/><path d="M12 26h18l-1 4H13Zm7 4h4v8h-4Zm-1 8h6v3h-6Z"/><path d="M21 7v15" fill="none" stroke="var(--light-edge)" stroke-width="1.1"/></g><g transform="rotate(45 21 21)"><path d="M21 2 26 9v14l-5 4-5-4V9Z"/><path d="M12 26h18l-1 4H13Zm7 4h4v8h-4Zm-1 8h6v3h-6Z"/><path d="M21 7v15" fill="none" stroke="var(--light-edge)" stroke-width="1.1"/></g></g></svg></span><span class="power-label companion-power-label">战力</span><strong id="combat-power" class="power-value companion-power">${number(card.power)}</strong></div>
64
+ </div>
65
+ <section class="companion-panel companion-stats character-stats" aria-label="角色属性"><span class="character-paper-pattern" aria-hidden="true">${characterFloralWatermark}</span>${corners(tier, ['tr', 'br'])}<div class="identity character-identity"><h1 id="character-name" class="character-name" title="${cardEscape(card.name)}">${cardEscape(card.name)}</h1><p class="level character-level"><span>Lv.</span><strong id="character-level">${number(card.level)}</strong></p><div class="gene-panel"><span class="gene-crest" aria-hidden="true">${characterGeneCrest(tier)}</span><div id="gene-rank" class="gene-rank" title="${cardEscape(geneRank)}">${cardEscape(geneRank)}</div></div><p class="exploration-rank-row"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M19.8 7.7A8 8 0 1 0 20 16M20 3v5h-5M15.8 12a3.8 3.8 0 1 1-3.8-3.8"/></svg><span>轮回等级</span> <strong id="exploration-rank">${cardEscape(explorationRank ? explorationRank + '级' : '—')}</strong></p></div>
66
+ <div class="resource companion-resource hp-resource"><div class="resource-track" role="progressbar" aria-label="生命值" aria-valuemin="0" aria-valuemax="100" aria-valuenow="${hpPercent}" aria-valuetext="${number(card.hp)} / ${number(card.maxHp)}"><span class="resource-fill fill" style="width:${hpPercent}%"></span><span id="hp-numbers" class="hp-bar-value">${number(card.hp)} / ${number(card.maxHp)}</span></div></div>
67
+ <div class="resource companion-resource exp-resource"><div id="experience-track" class="resource-track" role="progressbar" aria-label="经验进度" aria-valuemin="0" aria-valuemax="100" aria-valuenow="${experience}"><span class="resource-fill" style="width:${experience}%"></span></div></div>
68
+ <section class="attribute-panel companion-attributes" aria-label="一级属性"><div class="attribute-divider" aria-hidden="true">${characterAttributeDivider}</div>${radar(card.attributes)}</section></section>
68
69
  </div></main></body></html>`
69
70
  }
@@ -52,8 +52,12 @@ export function renderQQCommandMarkdown(data: IMessage, showGreeting: boolean):
52
52
  break
53
53
  }
54
54
  flushRewards()
55
- if (!item.content.trim()) flushBody()
56
- else bodyLines.push(renderInlineCommands(item.content))
55
+ // A single message item may contain a complete multiline battle summary.
56
+ // Keep its line/paragraph boundaries when log links select native Markdown.
57
+ for (const line of item.content.replace(/\r\n?/g, '\n').split('\n')) {
58
+ if (!line.trim()) flushBody()
59
+ else bodyLines.push(renderInlineCommands(line))
60
+ }
57
61
  break
58
62
  case MessageType.error:
59
63
  flushBody()
@@ -5,6 +5,7 @@ import { compactDisplayText, escapeMarkdown, renderQQTitle } from './qqMarkdownH
5
5
  import { isRewardMessage, renderRewardLine, renderRewardTitle } from './qqRewardMessage'
6
6
  import { artworkIdentity } from '../skillArtwork'
7
7
  import { battleLogLinkSpans, renderBattleLogLink } from './qqBattleLogLinks'
8
+ import { inlineCommandSpans as bracketedCommandSpans, type InlineCommandSpan } from '../../shared/bot/inlineCommands'
8
9
 
9
10
  export { escapeMarkdown, renderQQTitle } from './qqMarkdownHelpers'
10
11
 
@@ -54,27 +55,9 @@ export function inlineCommandUrl(command: string): string {
54
55
  }
55
56
 
56
57
 
57
- type InlineCommandSpan = { start: number; end: number; label: string; command: string }
58
-
59
58
  /** Bracketed commands may appear anywhere in prose, including escaped brackets. */
60
59
  function inlineCommandSpans(content: string): InlineCommandSpan[] {
61
- const spans: InlineCommandSpan[] = []
62
- let depth = 0, start = 0, labelStart = 0
63
- for (let index = 0; index < content.length; index++) {
64
- if (content[index] === '[') {
65
- if (depth === 0) {
66
- start = index > 0 && content[index - 1] === '\\' ? index - 1 : index
67
- labelStart = index + 1
68
- }
69
- depth++
70
- } else if (content[index] === ']' && depth > 0 && --depth === 0) {
71
- const label = content.slice(labelStart, index).replace(/\\$/, '')
72
- if (!label.trim() || /[\[\]\r\n]/.test(label) || /^\d+$/.test(label.trim())) continue
73
- // The refresh icon is part of the displayed label, not the command to send.
74
- const command = label.split(/[++]/, 1)[0].trim().replace(/^🔄\s*/u, '')
75
- if (command) spans.push({ start, end: index + 1, label, command })
76
- }
77
- }
60
+ const spans = bracketedCommandSpans(content)
78
61
  const logLinks = battleLogLinkSpans(content)
79
62
  return spans.filter(span => !logLinks.some(link => span.start < link.end && span.end > link.start))
80
63
  }
@@ -1,4 +1,5 @@
1
1
  import { IMessage, MessageType, type PageNavData } from '../face'
2
+ import { renderPlainInlineCommandAliases } from './inlineCommands'
2
3
 
3
4
  /** 把服务器下发的结构化行拼成纯文本;第一阶段只处理 title / line / error **/
4
5
  export class AnalysisMsg {
@@ -31,7 +32,9 @@ export class AnalysisMsg {
31
32
  ? labeledRow.map(entry => entry.label! + (entry.description ? ' · ' + entry.description : '')
32
33
  + ` [${entry.viewCommand}]`).join('')
33
34
  : withCommandHint(item.content, commands.get(lineIndex))
34
- msg += `${content}\n`
35
+ const plainContent = item.codeBlock || item.mailPart === 'body' || labeledRow
36
+ ? content : renderPlainInlineCommandAliases(content)
37
+ msg += `${plainContent}\n`
35
38
  break
36
39
  case MessageType.error:
37
40
  msg += `❌出错了!错误原因:${item.content},请反馈至联系开发者\n`
@@ -0,0 +1,39 @@
1
+ export type InlineCommandSpan = { start: number; end: number; label: string; command: string; aliased?: true }
2
+
3
+ /** Boxed commands may use [label](command:input) to show a different action label. */
4
+ export function inlineCommandSpans(content: string): InlineCommandSpan[] {
5
+ const spans: InlineCommandSpan[] = []
6
+ let depth = 0, start = 0, labelStart = 0
7
+ for (let index = 0; index < content.length; index++) {
8
+ if (content[index] === '[') {
9
+ if (depth === 0) {
10
+ start = index > 0 && content[index - 1] === '\\' ? index - 1 : index
11
+ labelStart = index + 1
12
+ }
13
+ depth++
14
+ } else if (content[index] === ']' && depth > 0 && --depth === 0) {
15
+ const label = content.slice(labelStart, index).replace(/\\$/, '')
16
+ if (!label.trim() || /[\[\]\r\n]/.test(label) || /^\d+$/.test(label.trim())) continue
17
+ const alias = /^\(command:([^()[\]\r\n]+)\)/.exec(content.slice(index + 1))
18
+ // Ordinary templates keep their existing command extraction rules.
19
+ const command = alias ? alias[1].trim() : label.split(/[++]/, 1)[0].trim().replace(/^🔄\s*/u, '')
20
+ if (!command) continue
21
+ const end = index + 1 + (alias?.[0].length ?? 0)
22
+ spans.push({ start, end, label, command, ...(alias ? { aliased: true as const } : {}) })
23
+ if (alias) index = end - 1
24
+ }
25
+ }
26
+ return spans
27
+ }
28
+
29
+ /** Keep aliased actions usable when the platform falls back to ordinary text. */
30
+ export function renderPlainInlineCommandAliases(content: string): string {
31
+ let result = '', offset = 0
32
+ for (const span of inlineCommandSpans(content)) {
33
+ if (!span.aliased) continue
34
+ result += content.slice(offset, span.start)
35
+ + (span.label.trim() === span.command ? `[${span.command}]` : `${span.label}[${span.command}]`)
36
+ offset = span.end
37
+ }
38
+ return result + content.slice(offset)
39
+ }
@@ -25,7 +25,7 @@ export interface ServiceType {
25
25
  }
26
26
 
27
27
  export const serviceProto: ServiceProto<ServiceType> = {
28
- "version": 37,
28
+ "version": 38,
29
29
  "services": [
30
30
  {
31
31
  "id": 0,
@@ -709,6 +709,14 @@ export const serviceProto: ServiceProto<ServiceType> = {
709
709
  }
710
710
  },
711
711
  "optional": true
712
+ },
713
+ {
714
+ "id": 14,
715
+ "name": "explorationRank",
716
+ "type": {
717
+ "type": "String"
718
+ },
719
+ "optional": true
712
720
  }
713
721
  ]
714
722
  },
@@ -38,7 +38,7 @@ export interface CharacterCardData {
38
38
  bloodlines: Array<{ name: string; quality: string; artworkUrl?: string }>;
39
39
  /** 当前基因锁大阶的配置名称,不包含层级;未开启时为基因锁未开启。 */
40
40
  geneLockRankName?: string;
41
- /** 当前称号的图片链接;未获得称号或图片不可用时省略,客户端显示图片占位。 */
41
+ /** 当前称号的图片链接;未获得称号或图片不可用时省略,客户端不显示称号横幅。 */
42
42
  titleImageUrl?: string;
43
43
  /** 已获得的真实基因锁大阶(0~5);0 表示尚未解锁。 */
44
44
  geneLockTier?: number;
@@ -46,6 +46,8 @@ export interface CharacterCardData {
46
46
  equipment?: Array<{ id: string; name: string; quality: string; slot: string; enhancement?: number }>;
47
47
  /** 当前构筑的全部技能,包括装备、递归附带与限时增益来源。 */
48
48
  skills?: Array<{ id: string; name: string; rank: number; quality?: string; type?: CompanionSkillType; artworkUrl?: string }>;
49
+ /** 玩家当前的轮回探索品阶(F~SSS),与角色等级、基因锁阶级分开显示。 */
50
+ explorationRank?: string;
49
51
  }
50
52
  export enum CompanionCardKind {
51
53
  pet = 'pet',