koishi-plugin-dew-bot 1.0.256 → 1.0.258

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
@@ -319,7 +319,7 @@ var characterCardStyles = `
319
319
  .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)}
320
320
  .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}
321
321
  .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}
322
- .character-identity{text-align:center;min-width:0}.title-image-slot{height:44px;display:flex;align-items:center;justify-content:center;margin:9px 10px 0}.title-artwork{display:block;width:var(--title-width,244px);max-width:100%;height:44px;object-fit:contain}
322
+ .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%)}
323
323
  .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}
324
324
  .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}
325
325
  .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)}
@@ -440,8 +440,8 @@ function characterCardHtml(card, artwork, _frame, defaultArtwork, titleArtwork,
440
440
  const experience = Math.max(0, Math.min(100, finite(card.experiencePercent)));
441
441
  const geneRank = card.geneLockRankName || "基因锁未开启";
442
442
  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">
443
- <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></div>
444
- <div class="title-image-slot character-title-below"><img id="title-image" class="title-artwork" src="${cardEscape(titleArtwork || characterTitlePlaceholder)}" data-fallback="${cardEscape(characterTitlePlaceholder)}" alt="${titleArtwork ? "称号图片" : "称号图片占位"}"></div></div>
443
+ <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>
444
+ <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>
445
445
  <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>
446
446
  <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>
447
447
  <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>
@@ -583,12 +583,226 @@ function companionCardHtml(card, artwork, icons) {
583
583
  }
584
584
  __name(companionCardHtml, "companionCardHtml");
585
585
 
586
+ // src/plugin/titleCardStyles.ts
587
+ var titleCardStyles = `
588
+ .title-sheet {
589
+ --dew-paper: light-dark(#fffaf0, #252721);
590
+ --dew-panel: light-dark(#f5f7f3, #2c302a);
591
+ --dew-ink: light-dark(#25382f, #e4eae3);
592
+ --dew-muted: light-dark(#737e74, #aeb8ac);
593
+ --dew-line: light-dark(#e5e9df, #40483c);
594
+ --dew-accent: light-dark(#2d7c69, #7dc6b1);
595
+ --dew-fill: light-dark(#2d7c6926, #7dc6b129);
596
+ --dew-link: light-dark(#1685df, #85bffa);
597
+ --dew-frame: light-dark(#b79c62, #a48a50);
598
+ --dew-frame-shadow: light-dark(#4a39251c, #00000042);
599
+ --dew-paper-top: light-dark(#fff7e5, #30362c);
600
+ --dew-paper-bottom: light-dark(#f7f2e8, #18231e);
601
+ --dew-gold-light: light-dark(#ead4a1, #dec58a);
602
+ --dew-gold-dark: light-dark(#94713d, #786136);
603
+ --dew-hero-glow: light-dark(#d4a64d20, #d4a64d19);
604
+ --dew-grid: light-dark(#b8bfae70, #9eaf8d26);
605
+ --dew-axis-ink: light-dark(#7c714e, #c4b68d);
606
+ padding: 52px 32px 48px; box-sizing: border-box;
607
+ display: flex; justify-content: center; align-items: flex-start;
608
+ font: 28px/1.6 'Microsoft YaHei', 'PingFang SC', system-ui, sans-serif;
609
+ color: var(--dew-ink);
610
+ }
611
+ .title-sheet .dew-preview { width: min(100%, 748px); }
612
+ .title-sheet .dew-card { position: relative; background: linear-gradient(155deg, var(--dew-paper-top), var(--dew-paper-bottom) 74%); border: 2.8px solid var(--dew-frame); border-radius: var(--dew-card-radius, 32px); overflow: hidden; box-shadow: 0 36px 64px var(--dew-frame-shadow), inset 0 0 0 4px var(--dew-paper), inset 0 0 0 6px color-mix(in srgb, var(--dew-gold-light) 38%, transparent); }
613
+ .title-sheet .dew-card::before { content: ''; position: absolute; inset: 16px; border: 2px solid color-mix(in srgb, var(--dew-frame) 60%, transparent); border-radius: max(0px, calc(var(--dew-card-radius, 32px) - 16px)); pointer-events: none; }
614
+ .title-sheet .dew-card::after { content: ''; position: absolute; inset: 18px; border-radius: max(0px, calc(var(--dew-card-radius, 32px) - 18px)); pointer-events: none; background: repeating-linear-gradient(0deg, transparent 0, transparent 6px, light-dark(#977b3810, #e9dbaa06) 6px, light-dark(#977b3810, #e9dbaa06) 8px); opacity: .6; }
615
+ .title-sheet .dew-corner { position: absolute; width: 38px; height: 38px; color: var(--dew-gold-light); pointer-events: none; }
616
+ .title-sheet .dew-corner::after { content: ''; position: absolute; inset: 10px; border: inherit; color: var(--dew-frame); opacity: .6; }
617
+ .title-sheet .dew-corner.tl { left: 24px; top: 24px; border-left: 3px solid; border-top: 3px solid; }
618
+ .title-sheet .dew-corner.tr { right: 24px; top: 24px; border-right: 3px solid; border-top: 3px solid; }
619
+ .title-sheet .dew-corner.bl { left: 24px; bottom: 24px; border-left: 3px solid; border-bottom: 3px solid; }
620
+ .title-sheet .dew-corner.br { right: 24px; bottom: 24px; border-right: 3px solid; border-bottom: 3px solid; }
621
+ .title-sheet .dew-head { display: flex; justify-content: flex-end; align-items: center; gap: 32px; padding: 52px 58px 0; }
622
+ .title-sheet .dew-section { font-size: 26px; letter-spacing: .28em; color: var(--dew-axis-ink); }
623
+ .title-sheet .dew-section::after { content: ''; display: block; width: 36px; height: 2px; margin-top: 14px; background: var(--dew-frame); }
624
+ .title-sheet .dew-quality { position: relative; display: flex; align-items: center; gap: 16px; font-size: 22px; padding: 0; }
625
+ .title-sheet .dew-quality b { display: grid; place-items: center; width: 68px; height: 76px; clip-path: polygon(50% 0, 100% 24%, 100% 76%, 50% 100%, 0 76%, 0 24%); background: linear-gradient(145deg, var(--dew-gold-light), var(--dew-frame) 54%, var(--dew-gold-dark)); font: 500 46px/1 Georgia, serif; color: light-dark(#4c391f, #342a19); }
626
+ .title-sheet .dew-quality span { color: var(--dew-axis-ink); writing-mode: vertical-rl; letter-spacing: .15em; }
627
+ .title-sheet .dew-banner { position: relative; height: 278px; display: flex; align-items: center; justify-content: center; padding: 0 34px; background: radial-gradient(ellipse at 50% 52%, var(--dew-hero-glow), transparent 67%); }
628
+ .title-sheet .dew-banner img { position: relative; width: 94%; height: auto; object-fit: contain; display: block; filter: drop-shadow(0 8px 18px light-dark(#9b773620, #0000004d)); }
629
+ .title-sheet .dew-divider { position: relative; height: 20px; margin: 0 58px; }
630
+ .title-sheet .dew-divider::before, .title-sheet .dew-divider::after { content: ''; position: absolute; top: 10px; width: calc(50% - 28px); height: 2px; background: linear-gradient(90deg, transparent, var(--dew-frame)); }
631
+ .title-sheet .dew-divider::before { left: 0; }
632
+ .title-sheet .dew-divider::after { right: 0; transform: rotate(180deg); }
633
+ .title-sheet .dew-divider span { position: absolute; left: calc(50% - 6px); top: 4px; width: 12px; height: 12px; transform: rotate(45deg); border: 2px solid var(--dew-gold-light); background: color-mix(in srgb, var(--dew-frame) 25%, transparent); box-sizing: border-box; }
634
+ .title-sheet .dew-divider span::after { content: ''; position: absolute; inset: -8px; border: 2px solid color-mix(in srgb, var(--dew-frame) 32%, transparent); }
635
+ .title-sheet .dew-radar-heading { margin: 36px 0 0; text-align: center; font-size: 22px; letter-spacing: .24em; color: var(--dew-axis-ink); }
636
+ .title-sheet .dew-radar { display: block; width: 100%; height: 606px; }
637
+ .title-sheet .dew-radar text { font-family: 'Microsoft YaHei', 'PingFang SC', system-ui, sans-serif; }
638
+ .title-sheet .dew-radar .dew-grid { fill: none; stroke: var(--dew-grid); stroke-width: 1; }
639
+ .title-sheet .dew-radar .dew-spoke { stroke: var(--dew-grid); stroke-width: 1; }
640
+ .title-sheet .dew-radar .dew-area { fill: url(#dew-title-radar-fill); stroke: var(--dew-accent); stroke-width: 1.8; stroke-linejoin: round; }
641
+ .title-sheet .dew-radar .dew-dot { fill: var(--dew-accent); stroke: var(--dew-paper-bottom); stroke-width: .7; }
642
+ .title-sheet .dew-radar .dew-glow { fill: var(--dew-accent); opacity: .12; }
643
+ .title-sheet .dew-radar .dew-axis { fill: var(--dew-axis-ink); font-size: 24px; }
644
+ .title-sheet .dew-radar .dew-value { fill: var(--dew-ink); font-size: 36px; font-weight: 500; letter-spacing: .01em; }
645
+ .title-sheet .dew-chart-space { padding: 0 38px 44px; }
646
+ .title-sheet .dew-actions { display: flex; align-items: center; gap: 24px; padding: 32px 14px 8px; flex-wrap: wrap; }
647
+ .title-sheet .dew-reset { background: transparent; border: 0; padding: 0; font: inherit; color: var(--dew-link); }
648
+ .title-sheet .dew-action-description { color: var(--dew-muted); font-size: 24px; }
649
+ .title-sheet .dew-notice { margin: 0 0 24px 14px; color: var(--dew-accent); font-size: 24px; }
650
+ .title-sheet .dew-crest { position: absolute; top: 34px; left: calc(50% - 6px); width: 12px; height: 12px; transform: rotate(45deg); border: 2px solid var(--dew-frame); pointer-events: none; }
651
+ .title-sheet .dew-quality .dew-quality-crown { display: none; position: absolute; top: -20px; left: 10px; width: 56px; height: 26px; background: linear-gradient(130deg, var(--dew-gold-light), var(--dew-frame)); clip-path: polygon(0 100%, 0 23%, 23% 58%, 50% 0, 77% 58%, 100% 23%, 100% 100%); }
652
+ .title-sheet .dew-quality-wings { display: none; position: absolute; left: -30px; right: 24px; top: 24px; height: 44px; pointer-events: none; }
653
+ .title-sheet .dew-quality-wings::before, .title-sheet .dew-quality-wings::after { content: ''; position: absolute; width: 36px; height: 36px; background: linear-gradient(130deg, var(--dew-gold-light), var(--dew-gold-dark)); clip-path: polygon(0 0, 100% 35%, 100% 100%, 30% 78%, 55% 57%, 15% 37%); opacity: .8; }
654
+ .title-sheet .dew-quality-wings::before { left: 0; }
655
+ .title-sheet .dew-quality-wings::after { right: 0; transform: scaleX(-1); }
656
+ .title-sheet .dew-aura { display: none; position: absolute; width: 360px; height: 220px; top: 50%; left: 50%; transform: translate(-50%, -50%); border: 2px solid color-mix(in srgb, var(--dew-frame) 24%, transparent); border-radius: 50%; pointer-events: none; }
657
+ .title-sheet .dew-orbit { fill: none; stroke: var(--dew-frame); stroke-width: .7; opacity: .25; }
658
+ .title-sheet .dew-tick { stroke: var(--dew-frame); stroke-width: 1; opacity: .35; }
659
+ .title-sheet .dew-constellation { fill: var(--dew-gold-light); opacity: .65; }
660
+ .title-sheet[data-quality="F"] .dew-card { background: var(--dew-paper); box-shadow: 0 8px 24px var(--dew-frame-shadow); }
661
+ .title-sheet[data-quality="F"] .dew-card::before, .title-sheet[data-quality="F"] .dew-corner, .title-sheet[data-quality="F"] .dew-crest, .title-sheet[data-quality="F"] .dew-section::after { display: none; }
662
+ .title-sheet[data-quality="F"] .dew-card::after { opacity: .16; }
663
+ .title-sheet[data-quality="F"] .dew-quality b { width: 56px; height: 56px; clip-path: none; border-radius: 8px; background: var(--dew-panel); color: var(--dew-ink); }
664
+ .title-sheet[data-quality="F"] .dew-banner { background: none; }
665
+ .title-sheet[data-quality="F"] .dew-banner img { filter: none; }
666
+ .title-sheet[data-quality="F"] .dew-divider span::after, .title-sheet[data-quality="F"] .dew-glow { display: none; }
667
+ .title-sheet[data-quality="E"] .dew-card { box-shadow: 0 12px 30px var(--dew-frame-shadow), inset 0 0 0 4px var(--dew-paper); }
668
+ .title-sheet[data-quality="E"] .dew-card::before, .title-sheet[data-quality="E"] .dew-corner.tr, .title-sheet[data-quality="E"] .dew-corner.bl, .title-sheet[data-quality="E"] .dew-crest { display: none; }
669
+ .title-sheet[data-quality="E"] .dew-corner { width: 24px; height: 24px; }
670
+ .title-sheet[data-quality="E"] .dew-corner::after { display: none; }
671
+ .title-sheet[data-quality="E"] .dew-quality b { width: 60px; height: 64px; clip-path: polygon(10px 0, calc(100% - 10px) 0, 100% 10px, 100% calc(100% - 10px), calc(100% - 10px) 100%, 10px 100%, 0 calc(100% - 10px), 0 10px); }
672
+ .title-sheet[data-quality="E"] .dew-banner { background: none; }
673
+ .title-sheet[data-quality="D"] .dew-corner::after { display: none; }
674
+ .title-sheet[data-quality="D"] .dew-card::after { opacity: .22; }
675
+ .title-sheet[data-quality="C"] .dew-quality b { clip-path: polygon(0 0, 100% 0, 100% 66%, 50% 100%, 0 66%); }
676
+ .title-sheet[data-quality="C"] .dew-corner { width: 40px; height: 40px; }
677
+ .title-sheet[data-quality="B"] .dew-quality b { clip-path: polygon(50% 0, 100% 28%, 90% 72%, 50% 100%, 10% 72%, 0 28%); }
678
+ .title-sheet[data-quality="B"] .dew-card::after { background: repeating-linear-gradient(45deg, transparent 0, transparent 60px, color-mix(in srgb, var(--dew-frame) 7%, transparent) 60px, color-mix(in srgb, var(--dew-frame) 7%, transparent) 62px), repeating-linear-gradient(-45deg, transparent 0, transparent 60px, color-mix(in srgb, var(--dew-frame) 7%, transparent) 60px, color-mix(in srgb, var(--dew-frame) 7%, transparent) 62px); }
679
+ .title-sheet[data-quality="A"] .dew-quality-wings, .title-sheet[data-quality="S"] .dew-quality-wings, .title-sheet[data-quality="SS"] .dew-quality-wings, .title-sheet[data-quality="SSS"] .dew-quality-wings { display: block; }
680
+ .title-sheet[data-quality="A"] .dew-corner, .title-sheet[data-quality="S"] .dew-corner { width: 46px; height: 46px; }
681
+ .title-sheet[data-quality="A"] .dew-crest { box-shadow: 16px -16px 0 -4px var(--dew-frame), -16px 16px 0 -4px var(--dew-frame); }
682
+ .title-sheet[data-quality="S"] .dew-card { box-shadow: 0 36px 70px var(--dew-frame-shadow), 0 0 36px color-mix(in srgb, var(--dew-frame) 12%, transparent), inset 0 0 0 4px var(--dew-paper), inset 0 0 0 8px color-mix(in srgb, var(--dew-gold-light) 38%, transparent); }
683
+ .title-sheet[data-quality="S"] .dew-aura, .title-sheet[data-quality="SS"] .dew-aura, .title-sheet[data-quality="SSS"] .dew-aura { display: block; }
684
+ .title-sheet[data-quality="S"] .dew-corner { border-width: 4px; }
685
+ .title-sheet[data-quality="SS"] .dew-quality .dew-quality-crown, .title-sheet[data-quality="SSS"] .dew-quality .dew-quality-crown { display: block; }
686
+ .title-sheet[data-quality="SS"] .dew-card::before { border-color: var(--dew-gold-light); opacity: .6; }
687
+ .title-sheet[data-quality="SS"] .dew-card::after { background: radial-gradient(circle at 18% 14%, var(--dew-gold-light) 0 2px, transparent 3px), radial-gradient(circle at 80% 21%, var(--dew-gold-light) 0 2px, transparent 3px), radial-gradient(circle at 14% 70%, var(--dew-gold-light) 0 2px, transparent 3px), radial-gradient(circle at 84% 84%, var(--dew-gold-light) 0 2px, transparent 3px); opacity: .55; }
688
+ .title-sheet[data-quality="SS"] .dew-aura { width: 392px; height: 232px; transform: translate(-50%, -50%) rotate(-12deg); box-shadow: 0 0 0 12px color-mix(in srgb, var(--dew-frame) 4%, transparent); }
689
+ .title-sheet[data-quality="SSS"] .dew-card { box-shadow: 0 40px 76px var(--dew-frame-shadow), 0 0 46px color-mix(in srgb, var(--dew-frame) 15%, transparent), inset 0 0 0 4px var(--dew-paper), inset 0 0 0 8px var(--dew-gold-dark), inset 0 0 0 10px color-mix(in srgb, var(--dew-gold-light) 70%, transparent); }
690
+ .title-sheet[data-quality="SSS"] .dew-card::before { inset: 20px; border-color: var(--dew-gold-light); opacity: .8; }
691
+ .title-sheet[data-quality="SSS"] .dew-card::after { background: repeating-linear-gradient(60deg, transparent 0, transparent 78px, color-mix(in srgb, var(--dew-frame) 7%, transparent) 78px, color-mix(in srgb, var(--dew-frame) 7%, transparent) 80px), repeating-linear-gradient(-60deg, transparent 0, transparent 78px, color-mix(in srgb, var(--dew-frame) 7%, transparent) 78px, color-mix(in srgb, var(--dew-frame) 7%, transparent) 80px); opacity: .6; }
692
+ .title-sheet[data-quality="SSS"] .dew-corner { width: 52px; height: 52px; color: var(--dew-gold-light); border-width: 4px; }
693
+ .title-sheet[data-quality="SSS"] .dew-corner::after { inset: 12px; color: var(--dew-frame); }
694
+ .title-sheet[data-quality="SSS"] .dew-crest { width: 16px; height: 16px; left: calc(50% - 8px); background: color-mix(in srgb, var(--dew-frame) 22%, transparent); box-shadow: 18px -18px 0 -4px var(--dew-frame), -18px 18px 0 -4px var(--dew-frame); }
695
+ .title-sheet[data-quality="SSS"] .dew-aura { width: 372px; height: 252px; border-color: color-mix(in srgb, var(--dew-frame) 40%, transparent); box-shadow: 0 0 0 12px color-mix(in srgb, var(--dew-frame) 5%, transparent), 0 0 0 26px color-mix(in srgb, var(--dew-frame) 3%, transparent); }
696
+
697
+ html,body { margin:0; padding:0; width:760px; background:#17232b; }
698
+ * { box-sizing:border-box; }
699
+ .title-sheet { color-scheme:dark; width:760px; padding:6px; display:block; background:#17232b; --dew-ink:#edf1e9; --dew-axis-ink:color-mix(in srgb,var(--dew-gold-light) 45%,var(--dew-ink)); --dew-grid:color-mix(in srgb,var(--dew-frame) 22%,transparent); --dew-panel:color-mix(in srgb,var(--dew-paper-top) 86%,var(--dew-frame)); --dew-hero-glow:color-mix(in srgb,var(--dew-frame) 16%,transparent); }
700
+ .title-sheet .dew-banner img { max-height:230px; }
701
+ .title-sheet .title-fallback { color:var(--dew-gold-light); font-size:48px; font-weight:600; line-height:1.4; text-align:center; overflow-wrap:anywhere; max-width:100%; margin:0; padding:16px; }
702
+ .title-sheet [hidden] { display:none !important; }
703
+ /* SVG coordinates already scale with its viewBox. */
704
+ .title-sheet .dew-radar .dew-axis { font-size:12px; }
705
+ .title-sheet .dew-radar .dew-value { font-size:18px; }
706
+ `;
707
+
708
+ // src/plugin/titleCardTemplate.ts
709
+ var qualities = ["F", "E", "D", "C", "B", "A", "S", "SS", "SSS"];
710
+ var themes = [
711
+ { radius: 10, border: 1, colors: ["#2b302c", "#222824", "#7c877a", "#b7c0ad", "#5e695d", "#a4baa9"] },
712
+ { radius: 10, border: 1.1, colors: ["#2a372c", "#1e2c24", "#799b7d", "#bbc9ac", "#546f50", "#a0c599"] },
713
+ { radius: 13, border: 1.25, colors: ["#2a3b40", "#1b2b32", "#83a9b4", "#d1e4e6", "#5d7e8b", "#a0d5dc"] },
714
+ { radius: 14, border: 1.3, colors: ["#263a48", "#17283b", "#6d9ec7", "#c5e2f4", "#506d93", "#9dd0f5"] },
715
+ { radius: 15, border: 1.45, colors: ["#3b2f48", "#241d38", "#a387ca", "#e6d2f0", "#735485", "#c8a6ec"] },
716
+ { radius: 16, border: 1.5, colors: ["#3b3529", "#28261f", "#bba363", "#eed7a0", "#816335", "#ddc58c"] },
717
+ { radius: 17, border: 1.8, colors: ["#432f26", "#2b211f", "#d9a45d", "#ffe0a0", "#9b682f", "#f0c580"] },
718
+ { radius: 18, border: 1.9, colors: ["#30334e", "#1c233d", "#9aa7e2", "#f0dfb0", "#8b733b", "#bcccf9"] },
719
+ { radius: 19, border: 2, colors: ["#344852", "#1c303a", "#c9dbdb", "#f6e6b7", "#a58b55", "#bcdfe1"] }
720
+ ];
721
+ var axes = [
722
+ ["intelligence", "智力"],
723
+ ["spirit", "精神力"],
724
+ ["constitution", "体质"],
725
+ ["agility", "敏捷"],
726
+ ["strength", "力量"],
727
+ ["resistance", "抗性"]
728
+ ];
729
+ var finite3 = /* @__PURE__ */ __name((value) => Number.isFinite(value) ? value : 0, "finite");
730
+ var number4 = /* @__PURE__ */ __name((value) => finite3(value).toLocaleString("zh-CN", { maximumFractionDigits: 2 }), "number");
731
+ function radar3(card, tier) {
732
+ const attributes = axes.map(([id, name2]) => {
733
+ const attribute = card.attributes.find((value) => value.id === id);
734
+ return { id, name: attribute?.name?.trim() || name2, value: finite3(attribute?.value ?? 0) };
735
+ });
736
+ const ceiling = Math.max(1, finite3(card.attributeLimit));
737
+ const cx = 166, cy = 151.5, radius = 91;
738
+ const point = /* @__PURE__ */ __name((index, distance) => {
739
+ const angle = -Math.PI / 2 + index * Math.PI / 3;
740
+ return [cx + Math.cos(angle) * distance, cy + Math.sin(angle) * distance];
741
+ }, "point");
742
+ const polygon = /* @__PURE__ */ __name((distance) => axes.map((_, index) => point(index, distance).join(",")).join(" "), "polygon");
743
+ const dots = attributes.map((attribute, index) => point(index, radius * Math.max(0, Math.min(1, attribute.value / ceiling))));
744
+ const ticks = tier >= 8 ? 48 : tier >= 6 ? 36 : 24;
745
+ return `<svg id="title-radar" class="dew-radar" viewBox="0 0 332 303" xmlns="http://www.w3.org/2000/svg" role="img" data-ceiling="${ceiling}" aria-label="${cardEscape(attributes.map((attribute) => attribute.name + " +" + number4(attribute.value) + "%").join(","))}">
746
+ <title>称号的六维属性加成</title>
747
+ <defs><radialGradient id="dew-title-radar-fill" cx="48%" cy="45%" r="70%"><stop offset="0%" stop-color="var(--dew-accent)" stop-opacity="${0.1 + tier * 0.03}"/><stop offset="100%" stop-color="var(--dew-accent)" stop-opacity="${tier < 3 ? ".04" : ".08"}"/></radialGradient></defs>
748
+ ${tier >= 3 ? `<circle cx="${cx}" cy="${cy}" r="${radius + 7}" class="dew-orbit"/>` : ""}
749
+ ${tier >= 4 ? Array.from({ length: ticks }, (_, index) => {
750
+ const angle = index * Math.PI * 2 / ticks, distance = radius + 7;
751
+ const length = index % (ticks / 6) === 0 ? 4 : 2;
752
+ return `<line x1="${cx + Math.cos(angle) * distance}" y1="${cy + Math.sin(angle) * distance}" x2="${cx + Math.cos(angle) * (distance + length)}" y2="${cy + Math.sin(angle) * (distance + length)}" class="dew-tick"/>`;
753
+ }).join("") : ""}
754
+ ${tier >= 8 ? `<ellipse cx="${cx}" cy="${cy}" rx="${radius + 11}" ry="${radius + 3}" transform="rotate(-18 ${cx} ${cy})" class="dew-orbit"/>` + [0.6, 1.7, 2.6, 3.7, 4.6, 5.7].map((index) => {
755
+ const [x, y] = point(index, radius + 9);
756
+ return `<circle cx="${x}" cy="${y}" r="${tier === 9 ? 1.7 : 1.1}" class="dew-constellation"/>`;
757
+ }).join("") : ""}
758
+ ${[0.25, 0.5, 0.75, 1].map((scale) => `<polygon points="${polygon(radius * scale)}" class="dew-grid"/>`).join("")}
759
+ ${axes.map((_, index) => {
760
+ const [x, y] = point(index, radius);
761
+ return `<line x1="${cx}" y1="${cy}" x2="${x}" y2="${y}" class="dew-spoke"/>`;
762
+ }).join("")}
763
+ <polygon points="${dots.map((dot) => dot.join(",")).join(" ")}" class="dew-area"/>
764
+ ${attributes.map((attribute, index) => {
765
+ const [x, y] = dots[index], [lx, ly] = point(index, radius + 35);
766
+ const value = "+" + number4(attribute.value) + "%";
767
+ return `<circle cx="${x}" cy="${y}" r="6" class="dew-glow"/>` + (tier >= 5 ? `<polygon points="${x},${y - 3.5} ${x + 3.5},${y} ${x},${y + 3.5} ${x - 3.5},${y}" class="dew-dot"/>` : `<circle cx="${x}" cy="${y}" r="3.1" class="dew-dot"/>`) + `<text x="${lx}" y="${ly - 3}" text-anchor="middle" class="dew-axis"${attribute.name.length > 5 ? ' textLength="66" lengthAdjust="spacingAndGlyphs"' : ""}>${cardEscape(attribute.name)}</text><text x="${lx}" y="${ly + 19}" text-anchor="middle" class="dew-value" data-attribute="${attribute.id}"${value.length > 7 ? ' textLength="76" lengthAdjust="spacingAndGlyphs"' : ""}>${cardEscape(value)}</text>`;
768
+ }).join("")}
769
+ </svg>`;
770
+ }
771
+ __name(radar3, "radar");
772
+ function titleCardHtml(card, artwork) {
773
+ const index = qualities.indexOf(card.quality);
774
+ const theme = themes[Math.max(0, index)], tier = index + 1;
775
+ const variables = ["paper-top", "paper-bottom", "frame", "gold-light", "gold-dark", "accent"].map((name2, position) => `--dew-${name2}:${theme.colors[position]}`).join(";");
776
+ return `<!doctype html><html lang="zh-CN"><head><meta charset="utf-8"><style>${titleCardStyles}</style></head><body><main id="capture" class="title-sheet" data-quality="${cardEscape(card.quality)}" data-decoration-level="${tier}" style="${variables};--dew-paper:${theme.colors[1]}">
777
+ <article class="dew-card" style="--dew-card-radius:${theme.radius * 2}px;border-width:${theme.border * 2}px">
778
+ <span class="dew-crest" aria-hidden="true"></span>
779
+ ${["tl", "tr", "bl", "br"].map((position) => `<span class="dew-corner ${position}" aria-hidden="true"></span>`).join("")}
780
+ <header class="dew-head"><div class="dew-quality" aria-hidden="true"><i class="dew-quality-wings"></i><b>✦</b><span class="dew-quality-crown"></span></div></header>
781
+ <div class="dew-banner"><span class="dew-aura" aria-hidden="true"></span>${artwork ? `<img class="title-banner-artwork" src="${cardEscape(artwork)}" alt="${cardEscape(card.name)}称号">` : ""}<h1 class="title-fallback"${artwork ? " hidden" : ""}>${cardEscape(card.name)}</h1></div>
782
+ <div class="dew-divider" aria-hidden="true"><span></span></div><p class="dew-radar-heading">属性加成</p>
783
+ <div class="dew-chart-space">${radar3(card, tier)}</div>
784
+ </article>
785
+ </main></body></html>`;
786
+ }
787
+ __name(titleCardHtml, "titleCardHtml");
788
+
586
789
  // src/plugin/skillCard.ts
587
790
  var import_node_crypto = require("node:crypto");
588
791
 
589
792
  // src/plugin/cardLayout.ts
590
793
  async function layoutCard(document2 = globalThis.document) {
591
794
  await document2.fonts.ready;
795
+ const titleBanner = document2.querySelector(".title-banner-artwork");
796
+ if (titleBanner) {
797
+ try {
798
+ await titleBanner.decode();
799
+ } catch {
800
+ titleBanner.remove();
801
+ const fallback = document2.querySelector(".title-fallback");
802
+ if (fallback) fallback.hidden = false;
803
+ }
804
+ }
805
+ if (document2.getElementById("capture")?.classList.contains("title-sheet")) return;
592
806
  for (const icon of document2.querySelectorAll(".skill-entry-artwork,.bloodline-entry-artwork")) {
593
807
  try {
594
808
  await icon.decode();
@@ -843,12 +1057,12 @@ function equipmentCardHtml(card, artwork, unknownFallback = false) {
843
1057
  __name(equipmentCardHtml, "equipmentCardHtml");
844
1058
 
845
1059
  // src/plugin/bloodlineCardTemplate.ts
846
- var finite3 = /* @__PURE__ */ __name((value) => Number.isFinite(value) ? value : 0, "finite");
847
- var number4 = /* @__PURE__ */ __name((value) => {
848
- const display = finite3(value).toLocaleString("en-US", { useGrouping: false, maximumFractionDigits: 2 });
1060
+ var finite4 = /* @__PURE__ */ __name((value) => Number.isFinite(value) ? value : 0, "finite");
1061
+ var number5 = /* @__PURE__ */ __name((value) => {
1062
+ const display = finite4(value).toLocaleString("en-US", { useGrouping: false, maximumFractionDigits: 2 });
849
1063
  return display === "-0" ? "0" : display;
850
1064
  }, "number");
851
- var signed = /* @__PURE__ */ __name((value) => (finite3(value) >= 0 ? "+" : "") + number4(value), "signed");
1065
+ var signed = /* @__PURE__ */ __name((value) => (finite4(value) >= 0 ? "+" : "") + number5(value), "signed");
852
1066
  var primaryAttributes3 = [
853
1067
  ["strength", "力量"],
854
1068
  ["agility", "敏捷"],
@@ -858,10 +1072,10 @@ var primaryAttributes3 = [
858
1072
  ["resistance", "抗性"]
859
1073
  ];
860
1074
  var classicIds = new Set(primaryAttributes3.map(([id]) => id));
861
- function radar3(card) {
1075
+ function radar4(card) {
862
1076
  const attributes = primaryAttributes3.map(([id, name2]) => {
863
1077
  const attribute = card.attributes.find((value) => value.id === id);
864
- return { name: attribute?.name?.trim() || name2, value: finite3(attribute?.value ?? 0) };
1078
+ return { name: attribute?.name?.trim() || name2, value: finite4(attribute?.value ?? 0) };
865
1079
  });
866
1080
  const max = Math.max(1, ...attributes.map((attribute) => Math.max(0, attribute.value)));
867
1081
  const magnitude = 10 ** Math.floor(Math.log10(max));
@@ -885,9 +1099,9 @@ function radar3(card) {
885
1099
  const nameFontSize = Math.max(14, Math.min(22, Math.floor(180 / attribute.name.length)));
886
1100
  return `<g transform="translate(${x} ${y})"><text text-anchor="middle" y="-3" font-size="${nameFontSize}" fill="#626d57">${cardEscape(attribute.name)}</text><text text-anchor="middle" y="27" font-size="${fontSize}" font-weight="600" fill="var(--quality-ink)">${cardEscape(value)}</text></g>`;
887
1101
  }).join("")}
888
- </svg><p class="radar-scale">各轴同尺度 · 满刻度 ${number4(ceiling)}</p></div>`;
1102
+ </svg><p class="radar-scale">各轴同尺度 · 满刻度 ${number5(ceiling)}</p></div>`;
889
1103
  }
890
- __name(radar3, "radar");
1104
+ __name(radar4, "radar");
891
1105
  var equipmentIcons = {
892
1106
  weapon: '<path d="M8 16 18 6l2-2 4 4-2 2-10 10M7 15l10 10M7 25l5-5M5 27l2-2"/>',
893
1107
  armor: '<path d="M11 5 5 9l-2 7 6 2v10h16V18l6-2-2-7-6-4c-2 5-10 5-12 0Z"/>',
@@ -920,15 +1134,15 @@ var bloodlineStyles = `
920
1134
  .equipment-tile b{font-size:26px;line-height:1.35;color:var(--quality-ink);font-weight:600;overflow-wrap:anywhere}
921
1135
  `;
922
1136
  function bloodlineCardHtml(card, artwork) {
923
- const nextExperience = finite3(card.nextExperience ?? 0);
924
- const experience = Math.max(0, finite3(card.experience ?? 0));
925
- const experienceHtml = card.experience === void 0 ? "" : nextExperience > 0 ? `<div class="experience-block"><div class="experience-label"><span>血统经验</span><b>${number4(experience)}<small> / ${number4(nextExperience)}</small></b></div><div class="experience-track"><i style="width:${Math.max(0, Math.min(100, experience / nextExperience * 100))}%"></i></div>${card.nextQuality ? `<div class="experience-next">下一级品质 <b>${cardEscape(card.nextQuality.replace(/\s*级$/, ""))} 级</b></div>` : ""}</div>` : `<div class="experience-block"><div class="experience-label"><span>血统经验</span><b>${number4(experience)}</b></div><div class="experience-next"><b>已达最高品质</b></div></div>`;
1137
+ const nextExperience = finite4(card.nextExperience ?? 0);
1138
+ const experience = Math.max(0, finite4(card.experience ?? 0));
1139
+ const experienceHtml = card.experience === void 0 ? "" : nextExperience > 0 ? `<div class="experience-block"><div class="experience-label"><span>血统经验</span><b>${number5(experience)}<small> / ${number5(nextExperience)}</small></b></div><div class="experience-track"><i style="width:${Math.max(0, Math.min(100, experience / nextExperience * 100))}%"></i></div>${card.nextQuality ? `<div class="experience-next">下一级品质 <b>${cardEscape(card.nextQuality.replace(/\s*级$/, ""))} 级</b></div>` : ""}</div>` : `<div class="experience-block"><div class="experience-label"><span>血统经验</span><b>${number5(experience)}</b></div><div class="experience-next"><b>已达最高品质</b></div></div>`;
926
1140
  const equipment = [...card.equipmentTypes].sort((a, b) => Number(/^[a-z]/i.test(a.type)) - Number(/^[a-z]/i.test(b.type)));
927
- const unlocks = equipment.length ? `<div class="equipment-grid">${equipment.map((rule) => `<div class="equipment-tile">${equipmentIcon(rule.type)}<span>${cardEscape(rule.type)}</span><b>× ${number4(rule.count)}</b></div>`).join("")}</div>` : '<p class="empty-line">当前品质暂无解锁装备</p>';
1141
+ const unlocks = equipment.length ? `<div class="equipment-grid">${equipment.map((rule) => `<div class="equipment-tile">${equipmentIcon(rule.type)}<span>${cardEscape(rule.type)}</span><b>× ${number5(rule.count)}</b></div>`).join("")}</div>` : '<p class="empty-line">当前品质暂无解锁装备</p>';
928
1142
  const perLevel = card.type === "血统道具" && card.level === 1;
929
1143
  const customAttributes = card.attributes.filter((attribute) => !classicIds.has(attribute.id));
930
1144
  const extraAttributes = customAttributes.length ? `<div class="bloodline-custom-attributes">${customAttributes.map((attribute) => cardStatRow(attribute.name?.trim() || attribute.id, signed(attribute.value), true)).join("")}</div>` : "";
931
- const body = `<div class="bloodline-facts">${cardStatRow(perLevel ? "血统战力(Lv.1)" : "血统战力", formatCombatPower(finite3(card.power)), true)}${experienceHtml}${card.count ? cardStatRow("拥有数量", card.count) : ""}${card.referencePrice ? cardStatRow("参考价格", card.referencePrice) : ""}</div>` + cardSection("血统描述", `<p class="description">${cardEscape(card.description?.trim() || "暂无血统描述")}</p>`) + cardSection("属性加成", `<p class="attribute-context">当前品质 · ${perLevel ? "每级属性加成" : "按角色 Lv." + number4(card.level) + " 计算"}</p>${radar3(card)}${extraAttributes}`) + cardSection("解锁装备", unlocks) + (card.useDescription?.trim() ? cardSection("使用说明", `<p class="description">${cardEscape(card.useDescription.trim())}</p>`) : "");
1145
+ const body = `<div class="bloodline-facts">${cardStatRow(perLevel ? "血统战力(Lv.1)" : "血统战力", formatCombatPower(finite4(card.power)), true)}${experienceHtml}${card.count ? cardStatRow("拥有数量", card.count) : ""}${card.referencePrice ? cardStatRow("参考价格", card.referencePrice) : ""}</div>` + cardSection("血统描述", `<p class="description">${cardEscape(card.description?.trim() || "暂无血统描述")}</p>`) + cardSection("属性加成", `<p class="attribute-context">当前品质 · ${perLevel ? "每级属性加成" : "按角色 Lv." + number5(card.level) + " 计算"}</p>${radar4(card)}${extraAttributes}`) + cardSection("解锁装备", unlocks) + (card.useDescription?.trim() ? cardSection("使用说明", `<p class="description">${cardEscape(card.useDescription.trim())}</p>`) : "");
932
1146
  return detailCardHtml({ name: card.name, quality: card.quality, type: card.type, artwork, artworkClass: "skill-artwork", fallbackGlyph: "血", tags: card.tags, body, sheetClass: "bloodline-sheet" }).replace("</style>", bloodlineStyles + "</style>");
933
1147
  }
934
1148
  __name(bloodlineCardHtml, "bloodlineCardHtml");
@@ -1132,10 +1346,10 @@ var RENDER_CACHE_TTL_MS = 10 * 60 * 1e3;
1132
1346
  var renderedCards = /* @__PURE__ */ new Map();
1133
1347
  var pendingCards = /* @__PURE__ */ new Map();
1134
1348
  var renderedBytes = 0;
1135
- function renderKey(card, itemCard, characterCard, equipmentCard, bloodlineCard, companionCard, normalizeArtwork = true) {
1136
- const source = bloodlineCard ?? equipmentCard ?? companionCard ?? characterCard ?? itemCard ?? card;
1349
+ function renderKey(card, itemCard, characterCard, equipmentCard, bloodlineCard, companionCard, titleCard, normalizeArtwork = true) {
1350
+ const source = titleCard ?? bloodlineCard ?? equipmentCard ?? companionCard ?? characterCard ?? itemCard ?? card;
1137
1351
  if (!source) return void 0;
1138
- const kind = bloodlineCard ? "bloodline" : equipmentCard ? "equipment" : companionCard ? "companion" : characterCard ? "character" : itemCard ? "item" : "skill";
1352
+ const kind = titleCard ? "title" : bloodlineCard ? "bloodline" : equipmentCard ? "equipment" : companionCard ? "companion" : characterCard ? "character" : itemCard ? "item" : "skill";
1139
1353
  const entryCard = kind === "companion" ? companionCard : void 0;
1140
1354
  const entryAddress = /* @__PURE__ */ __name((address) => normalizeArtwork ? artworkIdentity(address) ?? address : address, "entryAddress");
1141
1355
  const data = {
@@ -1179,17 +1393,17 @@ function rememberRender(key, image2) {
1179
1393
  }
1180
1394
  }
1181
1395
  __name(rememberRender, "rememberRender");
1182
- async function renderSkillCard(ctx, card, leaderboard, itemCard, characterCard, equipmentCard, bloodlineCard, companionCard) {
1396
+ async function renderSkillCard(ctx, card, leaderboard, itemCard, characterCard, equipmentCard, bloodlineCard, companionCard, titleCard) {
1183
1397
  if (!ctx.puppeteer) throw new Error("Puppeteer 未启用");
1184
- if (JSON.stringify(bloodlineCard ?? equipmentCard ?? companionCard ?? characterCard ?? itemCard ?? card ?? leaderboard ?? {}).length > 24e3) throw new Error("卡片内容过长");
1185
- const key = leaderboard && !bloodlineCard && !equipmentCard && !companionCard ? void 0 : renderKey(card, itemCard, characterCard, equipmentCard, bloodlineCard, companionCard);
1186
- if (!key) return (await renderUncached(ctx, card, leaderboard, itemCard, characterCard, equipmentCard, bloodlineCard, companionCard)).image;
1398
+ if (JSON.stringify(titleCard ?? bloodlineCard ?? equipmentCard ?? companionCard ?? characterCard ?? itemCard ?? card ?? leaderboard ?? {}).length > 24e3) throw new Error("卡片内容过长");
1399
+ const key = leaderboard && !titleCard && !bloodlineCard && !equipmentCard && !companionCard ? void 0 : renderKey(card, itemCard, characterCard, equipmentCard, bloodlineCard, companionCard, titleCard);
1400
+ if (!key) return (await renderUncached(ctx, card, leaderboard, itemCard, characterCard, equipmentCard, bloodlineCard, companionCard, titleCard)).image;
1187
1401
  const hit = cachedRender(key);
1188
1402
  if (hit) return hit;
1189
- const pendingKey = renderKey(card, itemCard, characterCard, equipmentCard, bloodlineCard, companionCard, false);
1403
+ const pendingKey = renderKey(card, itemCard, characterCard, equipmentCard, bloodlineCard, companionCard, titleCard, false);
1190
1404
  const active = pendingCards.get(pendingKey);
1191
1405
  if (active) return active;
1192
- const work = renderUncached(ctx, card, leaderboard, itemCard, characterCard, equipmentCard, bloodlineCard, companionCard).then(({ image: image2, cacheable }) => {
1406
+ const work = renderUncached(ctx, card, leaderboard, itemCard, characterCard, equipmentCard, bloodlineCard, companionCard, titleCard).then(({ image: image2, cacheable }) => {
1193
1407
  if (cacheable) rememberRender(key, image2);
1194
1408
  return image2;
1195
1409
  }).finally(() => {
@@ -1199,7 +1413,7 @@ async function renderSkillCard(ctx, card, leaderboard, itemCard, characterCard,
1199
1413
  return work;
1200
1414
  }
1201
1415
  __name(renderSkillCard, "renderSkillCard");
1202
- async function renderUncached(ctx, card, leaderboard, itemCard, characterCard, equipmentCard, bloodlineCard, companionCard) {
1416
+ async function renderUncached(ctx, card, leaderboard, itemCard, characterCard, equipmentCard, bloodlineCard, companionCard, titleCard) {
1203
1417
  if (running >= 2) {
1204
1418
  if (waiting.length >= 16) throw new Error("绘图繁忙");
1205
1419
  await new Promise((resolve) => waiting.push(resolve));
@@ -1215,15 +1429,18 @@ async function renderUncached(ctx, card, leaderboard, itemCard, characterCard, e
1215
1429
  }, "release");
1216
1430
  try {
1217
1431
  const work = /* @__PURE__ */ __name(async () => {
1218
- const entryArtworkPromise = loadCardEntryArtwork(!bloodlineCard && !equipmentCard ? companionCard : void 0);
1219
- const remoteAddress = bloodlineCard ? bloodlineCard.artworkUrl : equipmentCard ? equipmentCard.artworkUrl : companionCard ? companionCard.artworkUrl : characterCard ? characterCard.artworkUrl : itemCard ? itemCard.artworkUrl : leaderboard ? leaderboard.backgroundUrl : card?.artworkUrl;
1220
- const dedicatedArt = !bloodlineCard && equipmentCard ? await loadSkillArtwork(equipmentCard.artworkUrl) : void 0;
1221
- const unknownArt = !bloodlineCard && equipmentCard && !dedicatedArt ? await equipmentFallback() : void 0;
1222
- const art = !bloodlineCard && equipmentCard ? dedicatedArt ?? unknownArt : await loadSkillArtwork(remoteAddress);
1432
+ const entryArtworkPromise = loadCardEntryArtwork(!titleCard && !bloodlineCard && !equipmentCard ? companionCard : void 0);
1433
+ const remoteAddress = titleCard ? titleCard.artworkUrl : bloodlineCard ? bloodlineCard.artworkUrl : equipmentCard ? equipmentCard.artworkUrl : companionCard ? companionCard.artworkUrl : characterCard ? characterCard.artworkUrl : itemCard ? itemCard.artworkUrl : leaderboard ? leaderboard.backgroundUrl : card?.artworkUrl;
1434
+ const dedicatedArt = !titleCard && !bloodlineCard && equipmentCard ? await loadSkillArtwork(equipmentCard.artworkUrl) : void 0;
1435
+ const unknownArt = !titleCard && !bloodlineCard && equipmentCard && !dedicatedArt ? await equipmentFallback() : void 0;
1436
+ const art = !titleCard && !bloodlineCard && equipmentCard ? dedicatedArt ?? unknownArt : await loadSkillArtwork(remoteAddress);
1223
1437
  const entryArtwork = await entryArtworkPromise;
1224
1438
  let html;
1439
+ let loadedCharacterTitle;
1225
1440
  let localAssetsReady = true;
1226
- if (bloodlineCard) {
1441
+ if (titleCard) {
1442
+ html = titleCardHtml(titleCard, art);
1443
+ } else if (bloodlineCard) {
1227
1444
  html = bloodlineCardHtml(bloodlineCard, art);
1228
1445
  } else if (equipmentCard) {
1229
1446
  localAssetsReady = Boolean(art);
@@ -1235,6 +1452,7 @@ async function renderUncached(ctx, card, leaderboard, itemCard, characterCard, e
1235
1452
  characterAsset(),
1236
1453
  loadSkillArtwork(characterCard.titleImageUrl)
1237
1454
  ]);
1455
+ loadedCharacterTitle = titleArtwork;
1238
1456
  localAssetsReady = Boolean(art || defaultArtwork) && (!characterCard.titleImageUrl || Boolean(titleArtwork));
1239
1457
  html = characterCardHtml(characterCard, art, void 0, defaultArtwork, titleArtwork, entryArtwork.icons);
1240
1458
  } else if (itemCard) {
@@ -1251,7 +1469,7 @@ async function renderUncached(ctx, card, leaderboard, itemCard, characterCard, e
1251
1469
  page = void 0;
1252
1470
  throw new Error("绘图超时");
1253
1471
  }
1254
- await page.setViewport({ width: 760, height: 1107, deviceScaleFactor: (companionCard || characterCard) && !bloodlineCard && !equipmentCard ? 2 : 1 });
1472
+ await page.setViewport({ width: 760, height: 1107, deviceScaleFactor: titleCard || (companionCard || characterCard) && !bloodlineCard && !equipmentCard ? 2 : 1 });
1255
1473
  await page.setJavaScriptEnabled(false);
1256
1474
  await page.setRequestInterception(true);
1257
1475
  page.on("request", (req) => {
@@ -1260,13 +1478,27 @@ async function renderUncached(ctx, card, leaderboard, itemCard, characterCard, e
1260
1478
  });
1261
1479
  await page.setContent(html, { waitUntil: "load", timeout: 1e4 });
1262
1480
  await page.evaluate(layoutCard);
1481
+ let titleDecoded = true;
1482
+ if (titleCard && art) {
1483
+ titleDecoded = await page.evaluate((expected) => {
1484
+ const image2 = document.querySelector(".title-banner-artwork");
1485
+ return Boolean(image2 && image2.naturalWidth > 0 && image2.getAttribute("src") === expected);
1486
+ }, art);
1487
+ if (!titleDecoded) discardSkillArtwork(titleCard.artworkUrl, art);
1488
+ } else if (loadedCharacterTitle && characterCard) {
1489
+ titleDecoded = await page.evaluate((expected) => {
1490
+ const image2 = document.querySelector(".title-artwork");
1491
+ return Boolean(image2 && image2.naturalWidth > 0 && image2.getAttribute("src") === expected && image2.getAttribute("src") !== image2.dataset.fallback);
1492
+ }, loadedCharacterTitle);
1493
+ if (!titleDecoded) discardSkillArtwork(characterCard.titleImageUrl, loadedCharacterTitle);
1494
+ }
1263
1495
  const expectedIcons = (entryArtwork.icons.skills?.filter(Boolean).length ?? 0) + (entryArtwork.icons.bloodlines?.filter(Boolean).length ?? 0);
1264
1496
  const decodedEntries = expectedIcons ? await page.evaluate(() => ({
1265
1497
  skills: [...document.querySelectorAll(".skill-entry-artwork")].map((icon) => Number(icon.dataset.entryIndex)),
1266
1498
  bloodlines: [...document.querySelectorAll(".bloodline-entry-artwork")].map((icon) => Number(icon.dataset.entryIndex))
1267
1499
  })) : { skills: [], bloodlines: [] };
1268
1500
  const decodedIcons = decodedEntries.skills.length + decodedEntries.bloodlines.length;
1269
- const activeEntries = !bloodlineCard && !equipmentCard ? companionCard : void 0;
1501
+ const activeEntries = !titleCard && !bloodlineCard && !equipmentCard ? companionCard : void 0;
1270
1502
  for (const kind of ["skills", "bloodlines"]) {
1271
1503
  entryArtwork.icons[kind]?.forEach((image2, index) => {
1272
1504
  if (image2 && !decodedEntries[kind].includes(index)) discardSkillArtwork(activeEntries?.[kind]?.[index].artworkUrl, image2);
@@ -1276,7 +1508,7 @@ async function renderUncached(ctx, card, leaderboard, itemCard, characterCard, e
1276
1508
  if (!target) throw new Error("卡片元素缺失");
1277
1509
  const bounds = await target.boundingBox();
1278
1510
  if (!bounds || bounds.height > 1e4) throw new Error("卡片内容过长");
1279
- return { image: Buffer.from(await target.screenshot({ type: "jpeg", quality: 90 })), cacheable: localAssetsReady && entryArtwork.complete && decodedIcons === expectedIcons && (!remoteAddress || Boolean(!bloodlineCard && equipmentCard ? dedicatedArt : art)) };
1511
+ return { image: Buffer.from(await target.screenshot({ type: "jpeg", quality: 90 })), cacheable: localAssetsReady && titleDecoded && entryArtwork.complete && decodedIcons === expectedIcons && (!remoteAddress || Boolean(!titleCard && !bloodlineCard && equipmentCard ? dedicatedArt : art)) };
1280
1512
  }, "work");
1281
1513
  workPromise = work();
1282
1514
  return await Promise.race([workPromise, new Promise((_, reject) => {
@@ -1481,10 +1713,12 @@ function compactListLabel(value) {
1481
1713
  return /[📖📚]/u.test(label) ? label.replace(/技能书(?=$|×\d+(?:\.\d+)?(?:||$)||)/g, "") : label;
1482
1714
  }
1483
1715
  __name(compactListLabel, "compactListLabel");
1484
- function renderEntryArtwork(address) {
1716
+ function renderEntryArtwork(address, layout) {
1485
1717
  if (!artworkIdentity(address)) return "";
1486
1718
  const url = new URL(address).href.replace(/[()]/g, (character) => "%" + character.charCodeAt(0).toString(16).toUpperCase());
1487
- return `![图标 #24px #24px](${url}) `;
1719
+ return layout === "title" ? `![称号 #480px #147px](${url})
1720
+
1721
+ ` : `![图标 #24px #24px](${url}) `;
1488
1722
  }
1489
1723
  __name(renderEntryArtwork, "renderEntryArtwork");
1490
1724
  function inlineCommandUrl(command) {
@@ -1505,7 +1739,7 @@ function inlineCommandSpans(content) {
1505
1739
  } else if (content[index] === "]" && depth > 0 && --depth === 0) {
1506
1740
  const label = content.slice(labelStart, index).replace(/\\$/, "");
1507
1741
  if (!label.trim() || /[\[\]\r\n]/.test(label) || /^\d+$/.test(label.trim())) continue;
1508
- const command = label.split(/[++]/, 1)[0].trim();
1742
+ const command = label.split(/[++]/, 1)[0].trim().replace(/^🔄\s*/u, "");
1509
1743
  if (command) spans.push({ start, end: index + 1, label, command });
1510
1744
  }
1511
1745
  }
@@ -1613,7 +1847,7 @@ __name(renderInventoryFilters, "renderInventoryFilters");
1613
1847
  function renderQQInventoryMarkdown(data, showGreeting) {
1614
1848
  const nav = data.pageNav;
1615
1849
  const entryCommands = new Map(nav.entries?.map((entry) => [entry.lineIndex, entry.viewCommand]) ?? []);
1616
- const entryArtwork = new Map(nav.entries?.map((entry) => [entry.lineIndex, renderEntryArtwork(entry.artworkUrl)]) ?? []);
1850
+ const entryArtwork = new Map(nav.entries?.map((entry) => [entry.lineIndex, renderEntryArtwork(entry.artworkUrl, entry.artworkLayout)]) ?? []);
1617
1851
  const sections = [];
1618
1852
  const items = [];
1619
1853
  const flushItems = /* @__PURE__ */ __name(() => {
@@ -1658,7 +1892,8 @@ function renderQQPageMarkdown(data, showGreeting) {
1658
1892
  row.push(entry);
1659
1893
  labeledEntries.set(entry.lineIndex, row);
1660
1894
  }
1661
- const entryArtwork = new Map(nav?.entries?.map((entry) => [entry.lineIndex, renderEntryArtwork(entry.artworkUrl)]) ?? []);
1895
+ const entryArtwork = new Map(nav?.entries?.map((entry) => [entry.lineIndex, renderEntryArtwork(entry.artworkUrl, entry.artworkLayout)]) ?? []);
1896
+ const titleArtworkLines = new Set(nav?.entries?.filter((entry) => entry.artworkLayout === "title" && artworkIdentity(entry.artworkUrl)).map((entry) => entry.lineIndex) ?? []);
1662
1897
  const listPattern = /^\[(\d+)\]\s*([^\r\n]+)$/;
1663
1898
  const pagePattern = /^第\s*\d+\s*\/\s*\d+\s*页(?:\s*[||]\s*共\s*(\d+)\s*([项条]))?$/;
1664
1899
  const title = data.line.find((item) => item.type === 0 /* title */);
@@ -1698,7 +1933,7 @@ function renderQQPageMarkdown(data, showGreeting) {
1698
1933
  if (labeledRow) {
1699
1934
  flushList();
1700
1935
  flushRewards();
1701
- bodyLines.push(labeledRow.map((entry) => renderEntryArtwork(entry.artworkUrl) + `[${escapeMarkdown(entry.label)}](${inlineCommandUrl(entry.viewCommand)})` + (entry.description ? " " + escapeMarkdown(entry.description) : "")).join(" | "));
1936
+ bodyLines.push(labeledRow.map((entry) => renderEntryArtwork(entry.artworkUrl, entry.artworkLayout) + `[${escapeMarkdown(entry.label)}](${inlineCommandUrl(entry.viewCommand)})` + (entry.description ? " " + escapeMarkdown(entry.description) : "")).join(""));
1702
1937
  break;
1703
1938
  }
1704
1939
  if (item.reward) {
@@ -1713,9 +1948,9 @@ function renderQQPageMarkdown(data, showGreeting) {
1713
1948
  if (listEntry) {
1714
1949
  flushBody();
1715
1950
  const command2 = entryCommands.get(lineIndex);
1716
- const content = compactListLabel(listEntry[2]);
1717
- const label = escapeMarkdown(content);
1718
- const name2 = command2 ? `[${label}](${inlineCommandUrl(command2)})` : renderInlineCommands(content);
1951
+ const content2 = compactListLabel(listEntry[2]);
1952
+ const label = escapeMarkdown(content2);
1953
+ const name2 = command2 ? `[${label}](${inlineCommandUrl(command2)})` : renderInlineCommands(content2);
1719
1954
  listLines.push(`${listEntry[1]}. ${entryArtwork.get(lineIndex) ?? ""}${name2}`);
1720
1955
  break;
1721
1956
  }
@@ -1737,7 +1972,11 @@ function renderQQPageMarkdown(data, showGreeting) {
1737
1972
  break;
1738
1973
  }
1739
1974
  const command = entryCommands.get(lineIndex);
1740
- bodyLines.push(command ? (entryArtwork.get(lineIndex) ?? "") + `[${escapeMarkdown(compactListLabel(item.content))}](${inlineCommandUrl(command)})` : renderInlineCommands(item.content));
1975
+ const content = command ? (entryArtwork.get(lineIndex) ?? "") + `[${escapeMarkdown(compactListLabel(item.content))}](${inlineCommandUrl(command)})` : renderInlineCommands(item.content);
1976
+ if (command && titleArtworkLines.has(lineIndex)) {
1977
+ flushBody();
1978
+ sections.push(content);
1979
+ } else bodyLines.push(content);
1741
1980
  break;
1742
1981
  }
1743
1982
  case 2 /* error */:
@@ -1814,7 +2053,7 @@ var AnalysisMsg = class {
1814
2053
  break;
1815
2054
  case 1 /* line */:
1816
2055
  const labeledRow = labeledEntries.get(lineIndex);
1817
- const content = labeledRow ? labeledRow.map((entry) => entry.label + (entry.description ? " · " + entry.description : "") + ` [${entry.viewCommand}]`).join(" | ") : withCommandHint(item.content, commands.get(lineIndex));
2056
+ const content = labeledRow ? labeledRow.map((entry) => entry.label + (entry.description ? " · " + entry.description : "") + ` [${entry.viewCommand}]`).join("") : withCommandHint(item.content, commands.get(lineIndex));
1818
2057
  msg += `${content}
1819
2058
  `;
1820
2059
  break;
@@ -1897,12 +2136,15 @@ __name(renderQQCommandMarkdown, "renderQQCommandMarkdown");
1897
2136
  // src/plugin/serverHandel/qqTransactionMessage.ts
1898
2137
  function renderQQTransactionMarkdown(data, showGreeting) {
1899
2138
  const rewardMessage = isRewardMessage(data);
2139
+ const shortageMessage = data.transaction === true && data.line.some((item) => item.type === 0 /* title */ && item.content === "❌材料不足");
1900
2140
  const sections = [];
1901
2141
  const bodyLines = [];
1902
2142
  const actions = [];
1903
2143
  const rewardLines = [];
2144
+ let quotingItems = false;
1904
2145
  const flushBody = /* @__PURE__ */ __name(() => {
1905
2146
  if (bodyLines.length) sections.push(bodyLines.splice(0).join(" \n"));
2147
+ quotingItems = false;
1906
2148
  }, "flushBody");
1907
2149
  const flushActions = /* @__PURE__ */ __name(() => {
1908
2150
  if (actions.length) sections.push(actions.splice(0).join(" · "));
@@ -1915,11 +2157,12 @@ function renderQQTransactionMarkdown(data, showGreeting) {
1915
2157
  flushActions();
1916
2158
  flushRewards();
1917
2159
  }, "flush");
1918
- if (showGreeting && !data.reward) sections.push(escapeMarkdown("Ciallo~(∠・ω< )"));
2160
+ if (showGreeting && !data.reward && !shortageMessage) sections.push(escapeMarkdown("Ciallo~(∠・ω< )"));
1919
2161
  for (const item of data.line) {
1920
2162
  if (item.type === 0 /* title */) {
1921
2163
  flush();
1922
- if (rewardMessage) bodyLines.push(renderRewardTitle(item.content));
2164
+ if (shortageMessage) sections.push(renderRewardTitle(item.content));
2165
+ else if (rewardMessage) bodyLines.push(renderRewardTitle(item.content));
1923
2166
  else sections.push(renderQQTitle(item.content));
1924
2167
  continue;
1925
2168
  }
@@ -1929,6 +2172,10 @@ function renderQQTransactionMarkdown(data, showGreeting) {
1929
2172
  continue;
1930
2173
  }
1931
2174
  if (item.type !== 1 /* line */) continue;
2175
+ if (shortageMessage && (item.content === "【还缺】" || item.content === "本次未扣除任何道具")) {
2176
+ flushBody();
2177
+ continue;
2178
+ }
1932
2179
  if (item.reward) {
1933
2180
  flushBody();
1934
2181
  flushActions();
@@ -1949,11 +2196,18 @@ function renderQQTransactionMarkdown(data, showGreeting) {
1949
2196
  continue;
1950
2197
  }
1951
2198
  const section = /^┄━═(.+)═━┄$/.exec(item.content);
2199
+ if (section?.[1] === "💰交易记录") {
2200
+ flushBody();
2201
+ continue;
2202
+ }
1952
2203
  if (section || item.content === "【还缺】") {
1953
2204
  flushBody();
1954
2205
  sections.push(`**${escapeMarkdown(section?.[1] ?? "还缺")}**`);
1955
2206
  } else {
1956
- bodyLines.push(renderInlineCommands(item.content));
2207
+ const quotedItem = data.transaction === true && (/^.+ -\d+(?:\.\d+)?(?:(拥有\d+(?:\.\d+)?))?$/.test(item.content) || shortageMessage && /^.+ ×\d+(?:\.\d+)?(拥有\d+(?:\.\d+)?/需要\d+(?:\.\d+)?)$/.test(item.content));
2208
+ if (quotedItem !== quotingItems) flushBody();
2209
+ quotingItems = quotedItem;
2210
+ bodyLines.push(`${quotedItem ? "> " : ""}${renderInlineCommands(item.content)}`);
1957
2211
  }
1958
2212
  }
1959
2213
  flush();
@@ -2075,6 +2329,30 @@ ${commands.join("\n")}` : content);
2075
2329
  }
2076
2330
  };
2077
2331
 
2332
+ // src/plugin/serverHandel/titleCardMessage.ts
2333
+ function titleCardFollowup(data) {
2334
+ const card = data.titleCard;
2335
+ if (!card) return void 0;
2336
+ const detail = `${card.quality} · ${card.name}`;
2337
+ const lines = data.line.flatMap((item) => {
2338
+ if (item.type === 0 /* title */) return [];
2339
+ return item.content.replace(/\r\n?/g, "\n").split("\n").flatMap((line) => {
2340
+ const content = line.trim();
2341
+ if (!content || content === card.name || content === detail) return [];
2342
+ const bonus = /^(.*?)\s+\+[\d.,]+%$/u.exec(content);
2343
+ if (bonus && card.attributes.some((attribute) => {
2344
+ if (!bonus[1].endsWith(attribute.name)) return false;
2345
+ const prefix = bonus[1].slice(0, -attribute.name.length);
2346
+ return !/[\p{L}\p{N}]/u.test(prefix);
2347
+ })) return [];
2348
+ return [item.type === 2 /* error */ ? `❌ ${content}` : content];
2349
+ });
2350
+ });
2351
+ if (!lines.length) return void 0;
2352
+ return { text: lines.join("\n"), markdown: lines.map(renderInlineCommands).join(" \n") };
2353
+ }
2354
+ __name(titleCardFollowup, "titleCardFollowup");
2355
+
2078
2356
  // src/plugin/serverHandel/image.ts
2079
2357
  var ImageHandler = class extends handel_base {
2080
2358
  static {
@@ -2085,8 +2363,8 @@ var ImageHandler = class extends handel_base {
2085
2363
  let rendered = false;
2086
2364
  const renderStartedAt = this.timing ? performance.now() : 0;
2087
2365
  try {
2088
- if (!data.companionCard && !data.characterCard && !data.itemCard && !data.card && !data.leaderboard && !data.equipmentCard && !data.bloodlineCard) throw new Error("缺少卡片数据");
2089
- const image2 = await renderSkillCard(this.ctx, data.card, data.leaderboard, data.itemCard, data.characterCard, data.equipmentCard, data.bloodlineCard, data.companionCard);
2366
+ if (!data.titleCard && !data.companionCard && !data.characterCard && !data.itemCard && !data.card && !data.leaderboard && !data.equipmentCard && !data.bloodlineCard) throw new Error("缺少卡片数据");
2367
+ const image2 = await renderSkillCard(this.ctx, data.card, data.leaderboard, data.itemCard, data.characterCard, data.equipmentCard, data.bloodlineCard, data.companionCard, data.titleCard);
2090
2368
  if (this.timing) this.timing.imageBytes = image2.length;
2091
2369
  content = import_koishi.h.image(image2, "image/jpeg").toString();
2092
2370
  rendered = true;
@@ -2103,7 +2381,25 @@ var ImageHandler = class extends handel_base {
2103
2381
  await fallback.start(data);
2104
2382
  return;
2105
2383
  }
2384
+ const sendStartedAt = this.timing ? performance.now() : 0;
2106
2385
  await this.send(data, content, "image");
2386
+ const followup = titleCardFollowup(data);
2387
+ if (!followup) return;
2388
+ const session = sessions_default.get(sessionKey(data.botId, data.userId, data.channelId));
2389
+ let sentMarkdown = false;
2390
+ if (canSendQQPage(session, data)) {
2391
+ try {
2392
+ await sendQQMarkdown(session, followup.markdown);
2393
+ sentMarkdown = true;
2394
+ } catch (error) {
2395
+ this.ctx.logger.warn("称号操作 Markdown 发送失败,改用普通文字", error instanceof Error ? error.message : error);
2396
+ }
2397
+ }
2398
+ if (!sentMarkdown) await this.send({ ...data, notification: true }, followup.text);
2399
+ if (this.timing) {
2400
+ if (this.timing.delivery !== "missing-session") this.timing.delivery = "image";
2401
+ this.timing.sendMs = performance.now() - sendStartedAt;
2402
+ }
2107
2403
  }
2108
2404
  };
2109
2405
 
@@ -2171,7 +2467,7 @@ var import_tsrpc = require("tsrpc");
2171
2467
 
2172
2468
  // src/shared/bot/serviceProto.ts
2173
2469
  var serviceProto = {
2174
- "version": 31,
2470
+ "version": 35,
2175
2471
  "services": [
2176
2472
  {
2177
2473
  "id": 0,
@@ -2382,6 +2678,15 @@ var serviceProto = {
2382
2678
  "target": "messageFace/CompanionCardData"
2383
2679
  },
2384
2680
  "optional": true
2681
+ },
2682
+ {
2683
+ "id": 16,
2684
+ "name": "titleCard",
2685
+ "type": {
2686
+ "type": "Reference",
2687
+ "target": "messageFace/TitleCardData"
2688
+ },
2689
+ "optional": true
2385
2690
  }
2386
2691
  ]
2387
2692
  },
@@ -3465,6 +3770,14 @@ var serviceProto = {
3465
3770
  "type": "String"
3466
3771
  },
3467
3772
  "optional": true
3773
+ },
3774
+ {
3775
+ "id": 5,
3776
+ "name": "artworkLayout",
3777
+ "type": {
3778
+ "type": "String"
3779
+ },
3780
+ "optional": true
3468
3781
  }
3469
3782
  ]
3470
3783
  }
@@ -3903,6 +4216,73 @@ var serviceProto = {
3903
4216
  }
3904
4217
  ]
3905
4218
  },
4219
+ "messageFace/TitleCardData": {
4220
+ "type": "Interface",
4221
+ "properties": [
4222
+ {
4223
+ "id": 0,
4224
+ "name": "name",
4225
+ "type": {
4226
+ "type": "String"
4227
+ }
4228
+ },
4229
+ {
4230
+ "id": 1,
4231
+ "name": "quality",
4232
+ "type": {
4233
+ "type": "String"
4234
+ }
4235
+ },
4236
+ {
4237
+ "id": 2,
4238
+ "name": "artworkUrl",
4239
+ "type": {
4240
+ "type": "String"
4241
+ },
4242
+ "optional": true
4243
+ },
4244
+ {
4245
+ "id": 3,
4246
+ "name": "attributeLimit",
4247
+ "type": {
4248
+ "type": "Number"
4249
+ }
4250
+ },
4251
+ {
4252
+ "id": 4,
4253
+ "name": "attributes",
4254
+ "type": {
4255
+ "type": "Array",
4256
+ "elementType": {
4257
+ "type": "Interface",
4258
+ "properties": [
4259
+ {
4260
+ "id": 0,
4261
+ "name": "id",
4262
+ "type": {
4263
+ "type": "String"
4264
+ }
4265
+ },
4266
+ {
4267
+ "id": 1,
4268
+ "name": "name",
4269
+ "type": {
4270
+ "type": "String"
4271
+ }
4272
+ },
4273
+ {
4274
+ "id": 2,
4275
+ "name": "value",
4276
+ "type": {
4277
+ "type": "Number"
4278
+ }
4279
+ }
4280
+ ]
4281
+ }
4282
+ }
4283
+ }
4284
+ ]
4285
+ },
3906
4286
  "PtlMessage/ReqMessage": {
3907
4287
  "type": "Interface",
3908
4288
  "extends": [