@sophiacyl/dsh-plugin-waves 0.0.0-stage → 0.1.0-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (140) hide show
  1. package/DSH-GO-VERIFICATION.md +57 -0
  2. package/ICON.png +0 -0
  3. package/LICENSE +674 -0
  4. package/README.md +251 -2
  5. package/cordis.patch.yml +8 -0
  6. package/install.ps1 +0 -0
  7. package/install.sh +209 -0
  8. package/lib/client.js +1511 -0
  9. package/lib/host/aliashint.js +70 -0
  10. package/lib/host/attachments.js +25 -0
  11. package/lib/host/command.js +34 -0
  12. package/lib/host/consts.js +16 -0
  13. package/lib/host/dataguide.js +35 -0
  14. package/lib/host/imagebridge.js +41 -0
  15. package/lib/host/native-result.js +91 -0
  16. package/lib/host/native.js +191 -0
  17. package/lib/host/proxy.js +59 -0
  18. package/lib/host/results.js +67 -0
  19. package/lib/host/service.js +263 -0
  20. package/lib/host/tool.js +185 -0
  21. package/lib/host/troubleshoot.js +31 -0
  22. package/lib/host/worldbook.js +80 -0
  23. package/lib/index.js +74 -0
  24. package/locale/en.json +6 -0
  25. package/locale/zh.json +6 -0
  26. package/package.json +136 -4
  27. package/service/ICON.png +0 -0
  28. package/service/ai/AI_DATA.md +221 -0
  29. package/service/knowledge/additional/manual.jsonl +5 -0
  30. package/service/knowledge/wiki/baike.jsonl +1231 -0
  31. package/service/knowledge/wiki/chengjiu.jsonl +36 -0
  32. package/service/knowledge/wiki/gonglve.jsonl +383 -0
  33. package/service/knowledge/wiki/huodong.jsonl +427 -0
  34. package/service/knowledge/wiki/renwu.jsonl +412 -0
  35. package/service/knowledge/wiki/tujian.jsonl +2889 -0
  36. package/service/knowledge/wiki/zixun.jsonl +47 -0
  37. package/service/plugins/__init__.py +0 -0
  38. package/service/pyproject.toml +40 -0
  39. package/service/rover/__init__.py +0 -0
  40. package/service/rover/ai_core/__init__.py +0 -0
  41. package/service/rover/ai_core/models.py +50 -0
  42. package/service/rover/ai_core/rag/__init__.py +5 -0
  43. package/service/rover/ai_core/rag/knowledge.py +23 -0
  44. package/service/rover/ai_core/register.py +86 -0
  45. package/service/rover/ai_core/resource.py +6 -0
  46. package/service/rover/ai_core/skills/__init__.py +0 -0
  47. package/service/rover/ai_core/skills/operations.py +8 -0
  48. package/service/rover/api.py +720 -0
  49. package/service/rover/api_render.py +161 -0
  50. package/service/rover/app_life.py +154 -0
  51. package/service/rover/aps.py +47 -0
  52. package/service/rover/avatar.py +85 -0
  53. package/service/rover/bot.py +133 -0
  54. package/service/rover/builtin.py +152 -0
  55. package/service/rover/cards/__init__.py +26 -0
  56. package/service/rover/cards/_shared.py +129 -0
  57. package/service/rover/cards/char_list.py +258 -0
  58. package/service/rover/cards/char_panel.py +1353 -0
  59. package/service/rover/cards/echo_list.py +206 -0
  60. package/service/rover/cards/matrix_roles.py +25 -0
  61. package/service/rover/cards/rank_boards.py +714 -0
  62. package/service/rover/cards/rank_total.py +664 -0
  63. package/service/rover/compat.py +215 -0
  64. package/service/rover/config.py +163 -0
  65. package/service/rover/custom_panel.py +220 -0
  66. package/service/rover/data_store.py +31 -0
  67. package/service/rover/gss.py +19 -0
  68. package/service/rover/handler.py +191 -0
  69. package/service/rover/help/__init__.py +0 -0
  70. package/service/rover/help/draw_new_plugin_help.py +332 -0
  71. package/service/rover/help/model.py +16 -0
  72. package/service/rover/help/utils.py +7 -0
  73. package/service/rover/hooks.py +420 -0
  74. package/service/rover/html_actions.py +236 -0
  75. package/service/rover/i18n.py +19 -0
  76. package/service/rover/knowledge.py +235 -0
  77. package/service/rover/logger.py +102 -0
  78. package/service/rover/main.py +33 -0
  79. package/service/rover/media.py +222 -0
  80. package/service/rover/message_models.py +23 -0
  81. package/service/rover/models.py +92 -0
  82. package/service/rover/native_media.py +64 -0
  83. package/service/rover/ocr.py +79 -0
  84. package/service/rover/pool.py +43 -0
  85. package/service/rover/restart.py +36 -0
  86. package/service/rover/segment.py +143 -0
  87. package/service/rover/server.py +131 -0
  88. package/service/rover/status/__init__.py +0 -0
  89. package/service/rover/status/models.py +10 -0
  90. package/service/rover/status/plugin_status.py +17 -0
  91. package/service/rover/subscribe.py +148 -0
  92. package/service/rover/sv.py +403 -0
  93. package/service/rover/templates/char_list.html +182 -0
  94. package/service/rover/templates/char_panel.html +465 -0
  95. package/service/rover/templates/echo_list.html +221 -0
  96. package/service/rover/templates/rank_all_total.html +193 -0
  97. package/service/rover/templates/rank_board.html +107 -0
  98. package/service/rover/templates/rank_level_total.html +57 -0
  99. package/service/rover/templates/rank_matrix_total.html +66 -0
  100. package/service/rover/templates/rank_phantom_total.html +180 -0
  101. package/service/rover/templates/rank_slash_total.html +38 -0
  102. package/service/rover/trigger.py +98 -0
  103. package/service/rover/utils/__init__.py +0 -0
  104. package/service/rover/utils/boardcast/__init__.py +0 -0
  105. package/service/rover/utils/boardcast/models.py +15 -0
  106. package/service/rover/utils/cookie_manager/__init__.py +0 -0
  107. package/service/rover/utils/cookie_manager/qrlogin.py +24 -0
  108. package/service/rover/utils/database/__init__.py +0 -0
  109. package/service/rover/utils/database/base_models.py +1771 -0
  110. package/service/rover/utils/database/models.py +80 -0
  111. package/service/rover/utils/database/startup.py +60 -0
  112. package/service/rover/utils/default_bg/bg.jpg +0 -0
  113. package/service/rover/utils/download_resource/__init__.py +0 -0
  114. package/service/rover/utils/download_resource/download_core.py +164 -0
  115. package/service/rover/utils/download_resource/download_file.py +46 -0
  116. package/service/rover/utils/fonts/__init__.py +0 -0
  117. package/service/rover/utils/fonts/fonts.py +33 -0
  118. package/service/rover/utils/image/__init__.py +0 -0
  119. package/service/rover/utils/image/convert.py +51 -0
  120. package/service/rover/utils/image/image_tools.py +30 -0
  121. package/service/rover/utils/image/utils.py +70 -0
  122. package/service/rover/utils/plugins_config/__init__.py +0 -0
  123. package/service/rover/utils/plugins_config/gs_config.py +506 -0
  124. package/service/rover/utils/plugins_config/models.py +182 -0
  125. package/service/rover/web_app.py +3 -0
  126. package/service/rover/webconsole/__init__.py +0 -0
  127. package/service/rover/webconsole/mount_app.py +26 -0
  128. package/service/tools/migrate.py +351 -0
  129. package/service/tools/regression.py +87 -0
  130. package/service/tools/smoke.py +63 -0
  131. package/service/tools/update_plugins.py +212 -0
  132. package/service/tools/verify_actions.py +104 -0
  133. package/service/tools/verify_card.py +82 -0
  134. package/service/tools/wwdata.py +665 -0
  135. package/tests/compat.test.js +181 -0
  136. package/tests/imagebridge.test.js +58 -0
  137. package/tests/media.test.js +68 -0
  138. package/tests/native.test.js +127 -0
  139. package/tests/runtime.test.js +69 -0
  140. package/tests/service.test.js +48 -0
@@ -0,0 +1,221 @@
1
+ <!DOCTYPE html>
2
+ <html lang="zh-CN">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <title>鸣潮声骸仓库</title>
6
+ <style>
7
+ @import url('{{ font_css_url }}');
8
+
9
+ * { box-sizing: border-box; }
10
+
11
+ body {
12
+ margin: 0; padding: 0;
13
+ background-color: #0f1115;
14
+ font-family: 'Source Han Sans CN', 'Noto Sans CJK JP', sans-serif;
15
+ width: 1600px;
16
+ }
17
+
18
+ .container {
19
+ position: relative;
20
+ width: 1600px; padding: 40px;
21
+ display: flex; flex-direction: column; align-items: center; gap: 35px;
22
+ }
23
+
24
+ .bg-layer {
25
+ position: absolute; top: 0; left: 0; right: 0; bottom: 0;
26
+ z-index: 0; pointer-events: none;
27
+ }
28
+ .bg-layer::after {
29
+ content: ""; position: absolute;
30
+ top: 0; left: 0; width: 100%; height: 100%;
31
+ background-color: rgba(0, 0, 0, 0.2); z-index: 1;
32
+ }
33
+ .bg-image { width: 100%; height: 100%; object-fit: cover; display: block; }
34
+
35
+ .user-card {
36
+ position: relative; z-index: 1; width: 100%;
37
+ background:
38
+ radial-gradient(circle at 100% 0%, rgba(212, 177, 99, 0.15) 0%, transparent 40%),
39
+ linear-gradient(180deg, rgba(30, 34, 42, 0.9) 0%, rgba(15, 17, 21, 0.95) 100%);
40
+ border-radius: 16px; padding: 25px 40px;
41
+ display: flex; align-items: center;
42
+ border: 1px solid rgba(255, 255, 255, 0.08);
43
+ border-top: 1px solid rgba(255, 255, 255, 0.2);
44
+ box-shadow: 0 10px 30px -10px rgba(0, 0, 0, 0.5);
45
+ }
46
+ .user-card::before {
47
+ content: ""; position: absolute;
48
+ top: 0; left: 0; width: 100%; height: 100%;
49
+ background-image:
50
+ linear-gradient(rgba(255, 255, 255, 0.03) 1px, transparent 1px),
51
+ linear-gradient(90deg, rgba(255, 255, 255, 0.03) 1px, transparent 1px);
52
+ background-size: 30px 30px;
53
+ mask-image: linear-gradient(to bottom, black, transparent 80%);
54
+ pointer-events: none; border-radius: 16px;
55
+ }
56
+ .deco-text {
57
+ position: absolute; top: 20px; right: 30px;
58
+ font-family: 'Oswald', sans-serif; font-size: 14px;
59
+ letter-spacing: 4px; color: rgba(255, 255, 255, 0.1);
60
+ font-weight: bold; pointer-events: none;
61
+ }
62
+ .avatar-container {
63
+ width: 100px; height: 100px; flex-shrink: 0;
64
+ margin-right: 30px; position: relative;
65
+ border-radius: 50%; border: 3px solid #2a2e35;
66
+ background-color: #222;
67
+ box-shadow: 0 4px 10px rgba(0, 0, 0, 0.5);
68
+ }
69
+ .avatar-ring {
70
+ position: absolute; top: -6px; left: -6px; right: -6px; bottom: -6px;
71
+ border-radius: 50%; border: 1px solid rgba(255, 255, 255, 0.05);
72
+ border-left: 2px solid #d4b163; transform: rotate(-45deg);
73
+ box-shadow: -2px 2px 10px rgba(212, 177, 99, 0.2);
74
+ pointer-events: none; z-index: 0;
75
+ }
76
+ .avatar { width: 100%; height: 100%; object-fit: cover; display: block; border-radius: 50%; position: relative; z-index: 1; }
77
+ .user-info { flex: 1; display: flex; flex-direction: column; justify-content: center; z-index: 2; }
78
+ .user-row-1 {
79
+ display: flex; align-items: baseline; margin-bottom: 10px;
80
+ border-bottom: 1px solid rgba(255, 255, 255, 0.08);
81
+ padding-bottom: 8px; position: relative;
82
+ }
83
+ .user-row-1::after {
84
+ content: ""; position: absolute; bottom: -1px; left: 0;
85
+ width: 40px; height: 2px; background: #d4b163; box-shadow: 0 0 8px #d4b163;
86
+ }
87
+ .user-name { font-size: 42px; font-weight: 800; color: #fff; margin-right: 20px; text-shadow: 0 4px 10px rgba(0, 0, 0, 0.5); }
88
+ .user-uid {
89
+ font-family: 'JetBrains Mono', monospace; font-size: 20px; color: #d4b163;
90
+ background: rgba(0, 0, 0, 0.4); padding: 4px 12px; border-radius: 6px;
91
+ border: 1px solid rgba(212, 177, 99, 0.2); letter-spacing: 1.5px; font-weight: bold;
92
+ }
93
+ .page-badge {
94
+ margin-left: auto; font-family: 'Oswald', sans-serif; font-size: 22px; font-weight: bold;
95
+ color: #d8d8d8; background: rgba(212, 177, 99, 0.15);
96
+ border: 1px solid rgba(212, 177, 99, 0.3); border-radius: 6px; padding: 4px 12px;
97
+ }
98
+ .user-stats { display: flex; gap: 40px; }
99
+ .stat-item { display: flex; flex-direction: column; }
100
+ .stat-value {
101
+ font-family: 'Oswald', sans-serif; font-size: 30px; font-weight: 700; line-height: 1.1;
102
+ background: linear-gradient(180deg, #fff 50%, #d4b163 100%);
103
+ -webkit-background-clip: text; -webkit-text-fill-color: transparent;
104
+ }
105
+ .stat-label { font-size: 12px; color: #6d717a; font-weight: bold; letter-spacing: 1px; margin-top: 2px; }
106
+
107
+ /* 卡格按 PIL 版坐标绝对定位, 行间 30px 交叠让角色头像压住上一行 */
108
+ .echo-grid { position: relative; z-index: 1; width: {{ grid_w }}px; height: {{ grid_h }}px; }
109
+ .echo-grid .echo-card { position: absolute; width: {{ card_w }}px; height: {{ card_h }}px; overflow: hidden; }
110
+ .echo-card i { display: block; background-size: 100% 100%; background-repeat: no-repeat; }
111
+ .card-bg, .card-title { position: absolute; left: 0; top: 50px; width: 350px; }
112
+ .card-bg { height: 550px; background-image: url('{{ sh_bg_url }}'); }
113
+ .card-title { height: 150px; }
114
+
115
+ .role-avatar { position: absolute; left: 230px; top: 10px; width: 180px; height: 180px; }
116
+
117
+ .phantom-icon { position: absolute; left: 20px; top: 70px; width: 100px; height: 100px; }
118
+ .phantom-icon .icon { width: 100%; height: 100%; }
119
+ .phantom-icon .fetter { position: absolute; top: 0; }
120
+
121
+ .phantom-name {
122
+ position: absolute; left: 130px; top: 90px; transform: translateY(-50%);
123
+ font-size: 28px; font-weight: bold; color: rgb(234, 183, 4);
124
+ white-space: nowrap; text-shadow: 0 2px 6px rgba(0, 0, 0, 0.6);
125
+ }
126
+
127
+ .pill {
128
+ position: absolute; top: 108px; height: 30px; border-radius: 8px;
129
+ display: flex; align-items: center; font-size: 24px; color: #fff;
130
+ }
131
+ .pill.level { left: 128px; width: 84px; padding-left: 8px; background: rgba(0, 0, 0, 0.8); }
132
+ .pill.score { left: 228px; width: 92px; padding-left: 5px; background: rgba(186, 55, 42, 0.8); }
133
+
134
+ .cost { position: absolute; left: 128px; top: 140px; display: flex; }
135
+ .cost i { width: 30px; height: 30px; background-image: url('{{ promote_url }}'); }
136
+
137
+ .props {
138
+ position: absolute; left: 15px; top: {{ prop_top }}px; width: 335px;
139
+ display: flex; flex-direction: column; gap: {{ prop_gap }}px;
140
+ }
141
+ .prop { height: 40px; display: flex; align-items: center; }
142
+ .prop i { width: 40px; height: 40px; }
143
+ .prop-name { margin-left: 5px; font-size: 24px; white-space: nowrap; }
144
+ .prop-value { margin-left: auto; padding-right: 7px; font-size: 24px; }
145
+
146
+ .footer { position: relative; z-index: 1; text-align: center; width: 100%; opacity: 0.6; }
147
+ .footer img { max-width: 100%; }
148
+ </style>
149
+ <style>
150
+ {% for url in assets %}.a{{ loop.index0 }} { background-image: url('{{ url }}'); }
151
+ {% endfor %}
152
+ </style>
153
+ </head>
154
+ <body>
155
+ <div class="container">
156
+ <div class="bg-layer">
157
+ <img src="{{ bg_url }}" class="bg-image">
158
+ </div>
159
+
160
+ <div class="user-card">
161
+ <div class="deco-text">ECHO STORAGE</div>
162
+ <div class="avatar-container">
163
+ <div class="avatar-ring"></div>
164
+ <img src="{{ avatar_url }}" class="avatar">
165
+ </div>
166
+ <div class="user-info">
167
+ <div class="user-row-1">
168
+ <div class="user-name">{{ user_name }}</div>
169
+ <div class="user-uid">特征码 {{ user_id }}</div>
170
+ {% if page_text %}<div class="page-badge">{{ page_text }}</div>{% endif %}
171
+ </div>
172
+ <div class="user-stats">
173
+ {% if show_stats %}
174
+ <div class="stat-item">
175
+ <div class="stat-value">Lv.{{ level }}</div>
176
+ <div class="stat-label">账号等级</div>
177
+ </div>
178
+ <div class="stat-item">
179
+ <div class="stat-value">Lv.{{ world_level }}</div>
180
+ <div class="stat-label">世界等级</div>
181
+ </div>
182
+ {% endif %}
183
+ </div>
184
+ </div>
185
+ </div>
186
+
187
+ <div class="echo-grid">
188
+ {% for echo in echoes %}
189
+ <div class="echo-card" style="left: {{ echo.left }}px; top: {{ echo.top }}px;"{% if echo.command %} data-ww-cmd="{{ echo.command }}"{% endif %}>
190
+ <i class="card-bg"></i>
191
+ <i class="card-title {{ echo.title_class }}"></i>
192
+ <i class="role-avatar {{ echo.role_class }}"></i>
193
+ <div class="phantom-icon">
194
+ <i class="icon {{ echo.icon_class }}"></i>
195
+ <i class="fetter {{ echo.fetter_class }}" style="left: {{ echo.fetter_left }}%; width: {{ echo.fetter_size }}%; height: {{ echo.fetter_size }}%;"></i>
196
+ </div>
197
+ <div class="phantom-name">{{ echo.name }}</div>
198
+ <div class="pill level">Lv.{{ echo.level }}</div>
199
+ <div class="pill score">{{ echo.score }}分</div>
200
+ <div class="cost">
201
+ {% for _ in range(echo.cost) %}<i></i>{% endfor %}
202
+ </div>
203
+ <div class="props">
204
+ {% for prop in echo.props %}
205
+ <div class="prop">
206
+ <i class="{{ prop.icon_class }}"></i>
207
+ <div class="prop-name" style="color: {{ prop.name_color }};">{{ prop.name }}</div>
208
+ <div class="prop-value" style="color: {{ prop.value_color }};">{{ prop.value }}</div>
209
+ </div>
210
+ {% endfor %}
211
+ </div>
212
+ </div>
213
+ {% endfor %}
214
+ </div>
215
+
216
+ {% if footer_url %}
217
+ <div class="footer"><img src="{{ footer_url }}"></div>
218
+ {% endif %}
219
+ </div>
220
+ </body>
221
+ </html>
@@ -0,0 +1,193 @@
1
+ <!DOCTYPE html>
2
+ <html lang="zh-CN">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <title>{{ header.title_name }}</title>
6
+ <style>
7
+ @import url('{{ font_css_url }}');
8
+
9
+ * { box-sizing: border-box; }
10
+ body { margin: 0; background: #0f1115; }
11
+
12
+ .container {
13
+ position: relative;
14
+ width: {{ card_w }}px;
15
+ padding-bottom: 40px;
16
+ background-image: url('{{ bg }}');
17
+ background-size: cover;
18
+ background-position: center;
19
+ font-family: 'Source Han Sans CN', 'Noto Sans CJK JP', 'Noto Sans CJK KR', sans-serif;
20
+ font-weight: 700;
21
+ color: #fff;
22
+ overflow: hidden;
23
+ }
24
+
25
+ /* 文本按 PIL 锚点定位: lm 左中 / mm 中中 / rm 右中 */
26
+ .t { position: absolute; white-space: nowrap; line-height: 1; transform: translateY(-50%); }
27
+ .t.mm { transform: translate(-50%, -50%); }
28
+
29
+ /* ---------------- 头部: 标题底图 + 立绘, 底部渐隐 ---------------- */
30
+ .header { position: relative; width: 100%; height: {{ header_h }}px; }
31
+ .header .art {
32
+ position: absolute; inset: 0; overflow: hidden;
33
+ -webkit-mask-image: {{ header.mask }}; mask-image: {{ header.mask }};
34
+ }
35
+ .header .art img { position: absolute; }
36
+ .header .title-bg { left: {{ header.title_dx }}px; top: 0; width: 1050px; height: 500px; }
37
+ .header .logo { left: 50px; top: 65px; width: 200px; height: 80px; }
38
+ .header .time { left: 170px; top: 205px; font-size: 20px; color: {{ grey }}; }
39
+ .header .title { left: 240px; top: 265px; font-size: 30px; color: #000; }
40
+ .header .ver {
41
+ position: absolute; top: 255px; width: 100px; height: 30px;
42
+ border-radius: 6px; background: rgba(0, 79, 152, .9);
43
+ font-size: 24px; line-height: 30px; text-align: center;
44
+ }
45
+ .header .stat-v { top: 335px; font-size: 44px; }
46
+ .header .stat-l { top: 375px; font-size: 20px; color: {{ gold }}; }
47
+
48
+ /* ---------------- 上榜条件 ---------------- */
49
+ .cond { position: relative; width: 100%; height: {{ cond.height }}px; }
50
+ .cond .panel {
51
+ position: absolute; left: 20px; top: 20px;
52
+ width: {{ card_w - 40 }}px; height: {{ cond.height - 35 }}px;
53
+ border-radius: 8px; background: rgba(36, 36, 41, .9);
54
+ }
55
+ .cond .accent {
56
+ position: absolute; left: 20px; top: 20px;
57
+ width: {{ card_w - 40 }}px; height: 7px; background: rgb(203, 161, 95);
58
+ }
59
+ .cond .label { left: 40px; top: 60px; font-size: 28px; color: {{ grey }}; }
60
+ .cond .line { left: 185px; font-size: 20px; color: {{ gold }}; }
61
+ .cond .right { right: {{ card_w - 1260 }}px; font-size: 20px; color: {{ gold }}; }
62
+ .cond .notes { right: {{ card_w - 1260 }}px; top: {{ cond.height - 30 }}px; font-size: 16px; color: {{ gold }}; }
63
+
64
+ /* ---------------- 排行行 ---------------- */
65
+ .row {
66
+ position: relative; width: {{ card_w }}px; height: {{ row_h }}px;
67
+ background-image: url('{{ bar }}'); background-size: 100% 100%;
68
+ overflow: hidden;
69
+ }
70
+ .row .glow { position: absolute; left: 120px; right: 70px; top: 6px; bottom: 6px; border-radius: 14px; }
71
+ .row.t1 .glow { background: linear-gradient(90deg, rgba(255, 196, 60, .18), rgba(255, 196, 60, 0) 42%); }
72
+ .row.t2 .glow { background: linear-gradient(90deg, rgba(208, 216, 226, .16), rgba(208, 216, 226, 0) 42%); }
73
+ .row.t3 .glow { background: linear-gradient(90deg, rgba(214, 150, 92, .16), rgba(214, 150, 92, 0) 42%); }
74
+
75
+ .row .medal { position: absolute; left: 37px; top: 27px; width: 55px; height: 55px; }
76
+ .row .box {
77
+ position: absolute; top: 30px; height: 50px; border-radius: 8px;
78
+ background: rgba(54, 54, 54, .9); font-size: 34px; line-height: 50px; text-align: center;
79
+ }
80
+ .row .avatar { position: absolute; left: 100px; top: 0; width: 180px; height: 180px; }
81
+ .row .chain, .row .level {
82
+ position: absolute; top: 30px; height: 20px; border-radius: 6px;
83
+ font-size: 18px; line-height: 20px; padding-left: 5px; overflow: hidden;
84
+ }
85
+ .row .chain { left: 190px; width: 46px; }
86
+ .row .level { left: 240px; width: 60px; background: rgba(54, 54, 54, .9); }
87
+ .row .attr { position: absolute; left: 300px; top: 20px; width: 40px; height: 40px; }
88
+ .row .uid { left: 350px; top: 40px; font-size: 20px; }
89
+ .row .name {
90
+ left: 210px; top: 75px; font-size: 20px;
91
+ max-width: 130px; overflow: hidden; text-overflow: ellipsis;
92
+ }
93
+ .row .badge { position: absolute; left: 346px; top: 60px; width: 208px; height: 39px; }
94
+ .row .badge .plate { position: absolute; left: 4px; top: 5px; width: 201px; height: 31px; border-radius: 6px; background: rgba(54, 54, 54, .6); }
95
+ .row .badge .bg { position: absolute; left: 104px; top: 19px; transform: translate(-50%, -50%); }
96
+ .row .badge .txt { position: absolute; left: 104px; top: 20px; transform: translate(-50%, -50%); font-size: 18px; white-space: nowrap; }
97
+ .row .emblem { position: absolute; left: 545px; top: 2px; width: 110px; height: 110px; }
98
+ .row .score-v { left: 707px; top: 45px; font-size: 34px; }
99
+ .row .score-l { left: 707px; top: 75px; font-size: 16px; color: {{ gold }}; }
100
+ .row .sonata { position: absolute; left: 790px; top: 15px; width: 50px; height: 50px; }
101
+ .row .sonata-l { left: 815px; top: 75px; }
102
+ /* 武器块按原版 600x300 画布排版, 整体缩到 260x130 */
103
+ .row .weapon { position: absolute; left: 850px; top: 25px; width: 600px; height: 300px; transform: scale(.43333); transform-origin: top left; }
104
+ .row .weapon .icon { position: absolute; left: 45px; top: 0; width: 140px; height: 140px; }
105
+ .row .weapon .wname { left: 200px; top: 30px; font-size: 40px; color: {{ gold }}; max-width: 390px; overflow: hidden; text-overflow: ellipsis; }
106
+ .row .weapon .wlv { left: 203px; top: 75px; font-size: 30px; }
107
+ .row .weapon .reson { position: absolute; left: 205px; top: 105px; width: 65px; height: 30px; border-radius: 7px; }
108
+ .row .weapon .reson span { position: absolute; left: 15px; top: 15px; transform: translateY(-50%); font-size: 24px; }
109
+ .row .dmg-v { left: 1140px; top: 45px; font-size: 34px; color: {{ gold }}; }
110
+ .row .dmg-l { left: 1140px; top: 75px; font-size: 16px; }
111
+ .hot { position: absolute; border-radius: 10px; }
112
+
113
+ .icon-img { background-size: 100% 100%; background-repeat: no-repeat; }
114
+ {% for a in assets %}.{{ a.cls }} { background-image: url('{{ a.uri }}'); }
115
+ {% endfor %}
116
+ </style>
117
+ </head>
118
+ <body>
119
+ <div class="container">
120
+ <div class="header">
121
+ <div class="art">
122
+ <img class="title-bg" src="{{ header.title_bg }}">
123
+ <img class="logo" src="{{ header.logo }}">
124
+ <div class="t time">{{ header.time }}</div>
125
+ <div class="t title">{{ header.title_name }}</div>
126
+ <div class="ver" style="left: {{ header.version_x }}px">{{ header.version }}</div>
127
+ {% for s in header.stats %}
128
+ <div class="t mm stat-v" style="left: {{ s.x }}px">{{ s.value }}</div>
129
+ <div class="t mm stat-l" style="left: {{ s.x }}px">{{ s.label }}</div>
130
+ {% endfor %}
131
+ {% if header.pile %}
132
+ <img class="pile" src="{{ header.pile.uri }}"
133
+ style="left: {{ header.pile.x }}px; top: {{ header.pile.y }}px; width: {{ header.pile.w }}px; height: {{ header.pile.h }}px">
134
+ {% endif %}
135
+ </div>
136
+ </div>
137
+
138
+ <div class="cond">
139
+ <div class="panel"></div>
140
+ <div class="accent"></div>
141
+ <div class="t label">上榜条件</div>
142
+ <div class="t line" style="top: 50px">{{ cond.line1 }}</div>
143
+ <div class="t line" style="top: 85px">{{ cond.line2 }}</div>
144
+ {% if cond.modal %}<div class="t line" style="top: 120px">{{ cond.modal }}</div>{% endif %}
145
+ {% if cond.modal_current %}<div class="t right" style="top: 120px">{{ cond.modal_current }}</div>{% endif %}
146
+ <div class="t notes">{{ cond.notes }}</div>
147
+ </div>
148
+
149
+ {% for row in rows %}
150
+ <div class="row{% if row.rank <= 3 %} t{{ row.rank }}{% endif %}"{% if row.cmd %} data-ww-cmd="{{ row.cmd }}"{% endif %}>
151
+ <div class="glow"></div>
152
+ {% if row.medal %}
153
+ <div class="medal icon-img {{ row.medal }}"></div>
154
+ {% else %}
155
+ <div class="box" style="left: {{ row.box.left }}px; width: {{ row.box.w }}px">{{ row.box.text }}</div>
156
+ {% endif %}
157
+ <img class="avatar" src="{{ row.avatar }}">
158
+ <div class="chain" style="background: {{ row.chain_color }}">{{ row.chain }}</div>
159
+ <div class="level">{{ row.level }}</div>
160
+ <div class="attr icon-img {{ row.attribute }}"></div>
161
+ <div class="t uid" style="color: {{ row.uid_color }}">{{ row.uid }}</div>
162
+ <div class="t name">{{ row.name }}</div>
163
+ {% if row.badge %}
164
+ <div class="badge">
165
+ {% if row.badge.cls %}
166
+ <div class="bg icon-img {{ row.badge.cls }}" style="width: {{ row.badge.w }}px; height: {{ row.badge.h }}px"></div>
167
+ {% else %}<div class="plate"></div>{% endif %}
168
+ <div class="txt">{{ row.badge.name }}</div>
169
+ </div>
170
+ {% endif %}
171
+ {% if row.score %}
172
+ <div class="emblem icon-img {{ row.score.cls }}"></div>
173
+ <div class="t mm score-v">{{ row.score.value }}</div>
174
+ <div class="t mm score-l">{{ row.score.label }}</div>
175
+ {% endif %}
176
+ {% if row.sonata %}<div class="sonata icon-img {{ row.sonata }}"></div>{% endif %}
177
+ <div class="t mm sonata-l" style="font-size: {{ row.sonata_size }}px">{{ row.sonata_label }}</div>
178
+ <div class="weapon">
179
+ <div class="icon icon-img {{ row.weapon.cls }}"></div>
180
+ <div class="t wname">{{ row.weapon.name }}</div>
181
+ <div class="t wlv">{{ row.weapon.level }}</div>
182
+ <div class="reson" style="background: {{ row.weapon.reson_color }}"><span>{{ row.weapon.reson }}</span></div>
183
+ </div>
184
+ <div class="t mm dmg-v">{{ row.damage }}</div>
185
+ <div class="t mm dmg-l">{{ row.damage_name }}</div>
186
+ <div class="hot" style="left: 545px; top: 2px; width: 215px; height: 106px" data-ww-cmd="{{ cmd_score }}"></div>
187
+ <div class="hot" style="left: 775px; top: 12px; width: 72px; height: 86px" data-ww-cmd="{{ cmd_phantom }}"></div>
188
+ <div class="hot" style="left: 1060px; top: 25px; width: 160px; height: 66px" data-ww-cmd="{{ cmd_damage }}"></div>
189
+ </div>
190
+ {% endfor %}
191
+ </div>
192
+ </body>
193
+ </html>
@@ -0,0 +1,107 @@
1
+ <!DOCTYPE html>
2
+ <html lang="zh-CN">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <title>{{ header.title }}</title>
6
+ <style>
7
+ @import url('{{ font_css_url }}');
8
+
9
+ * { box-sizing: border-box; }
10
+ body { margin: 0; background: #0f1115; }
11
+
12
+ .container {
13
+ position: relative;
14
+ width: {{ card_w }}px;
15
+ padding-bottom: 40px;
16
+ background-image: url('{{ bg }}');
17
+ background-size: cover;
18
+ background-position: center;
19
+ font-family: 'Source Han Sans CN', 'Noto Sans CJK JP', 'Noto Sans CJK KR', sans-serif;
20
+ font-weight: 700;
21
+ color: #fff;
22
+ overflow: hidden;
23
+ }
24
+
25
+ /* 文本按 PIL 锚点定位: lm 左中 / mm 中中 / rm 右中 */
26
+ .t { position: absolute; white-space: nowrap; line-height: 1; transform: translateY(-50%); }
27
+ .t.mm { transform: translate(-50%, -50%); }
28
+ .t.rm { transform: translate(-100%, -50%); }
29
+
30
+ /* ---------------- 头部: 标题底图 + 图标, 底部渐隐 ---------------- */
31
+ .header { position: relative; width: 100%; height: 510px; }
32
+ .header .art {
33
+ position: absolute; left: 0; top: 0; width: {{ card_w }}px; height: 500px; overflow: hidden;
34
+ -webkit-mask-image: {{ header.mask }}; mask-image: {{ header.mask }};
35
+ }
36
+ .header .bg { position: absolute; left: 0; top: 0; width: {{ card_w }}px; height: 500px; }
37
+ .header .icon { position: absolute; left: 60px; top: 240px; width: 128px; height: 128px; }
38
+ .header .title { left: 220px; top: 290px; font-size: 58px; }
39
+ .header .subs {
40
+ position: absolute; left: 225px; top: 360px; transform: translateY(-50%);
41
+ display: flex; gap: 16px; font-size: 20px; color: {{ grey }}; white-space: nowrap;
42
+ }
43
+
44
+ /* ---------------- 排行行: 底板 110 高, 行距 120 ---------------- */
45
+ .row {
46
+ position: relative; width: {{ card_w }}px; height: 110px; margin-bottom: 10px;
47
+ background: url('{{ bar }}') no-repeat left top; background-size: {{ card_w }}px 110px;
48
+ overflow: hidden;
49
+ }
50
+ .row .medal { left: 37px; top: 27px; width: 55px; height: 55px; }
51
+ .row .box {
52
+ position: absolute; top: 30px; height: 50px; border-radius: 8px;
53
+ background: rgba(54, 54, 54, .9); font-size: 34px; line-height: 50px; text-align: center;
54
+ }
55
+ .row .avatar { position: absolute; left: 100px; top: 0; width: 180px; height: 180px; }
56
+ .row .name { left: 210px; top: 75px; }
57
+ .row .uid { top: 40px; font-size: 20px; }
58
+
59
+ /* bot 主人徽章: 底图以 208x39 中心为锚点, 名字压在中间 */
60
+ .badge { position: absolute; width: 208px; height: 39px; }
61
+ .badge .plate { position: absolute; left: 4px; top: 5px; width: 201px; height: 31px; border-radius: 6px; background: rgba(54, 54, 54, .6); }
62
+ .badge .bg { left: 104px; top: 19px; transform: translate(-50%, -50%); }
63
+ .badge .txt { position: absolute; left: 104px; top: 20px; transform: translate(-50%, -50%); font-size: 18px; white-space: nowrap; }
64
+
65
+ .icon-img { position: absolute; background-size: 100% 100%; background-repeat: no-repeat; }
66
+ {% block style %}{% endblock %}
67
+ {% for a in assets %}.{{ a.cls }} { background-image: url('{{ a.uri }}'); }
68
+ {% endfor %}
69
+ </style>
70
+ </head>
71
+ <body>
72
+ <div class="container">
73
+ <div class="header">
74
+ <div class="art">
75
+ <img class="bg" src="{{ header.art }}">
76
+ <img class="icon" src="{{ header.icon }}">
77
+ <div class="t title">{{ header.title }}</div>
78
+ <div class="subs">{% for s in header.subs %}<span>{{ s }}</span>{% endfor %}</div>
79
+ </div>
80
+ </div>
81
+
82
+ {% block board %}{% endblock %}
83
+
84
+ {% for row in rows %}
85
+ <div class="row">
86
+ {% if row.medal %}
87
+ <div class="medal icon-img {{ row.medal }}"></div>
88
+ {% else %}
89
+ <div class="box" style="left: {{ row.box.left }}px; width: {{ row.box.w }}px">{{ row.box.text }}</div>
90
+ {% endif %}
91
+ <img class="avatar" src="{{ row.avatar }}">
92
+ <div class="t name" style="font-size: {{ row.name_size }}px">{{ row.name }}</div>
93
+ <div class="t uid" style="left: {{ row.uid_x }}px; color: {{ row.uid_color }}">{{ row.uid }}</div>
94
+ {% if row.badge %}
95
+ <div class="badge" style="left: {{ row.badge.x }}px; top: {{ row.badge.y }}px">
96
+ {% if row.badge.cls %}
97
+ <div class="bg icon-img {{ row.badge.cls }}" style="width: {{ row.badge.w }}px; height: {{ row.badge.h }}px"></div>
98
+ {% else %}<div class="plate"></div>{% endif %}
99
+ <div class="txt">{{ row.badge.name }}</div>
100
+ </div>
101
+ {% endif %}
102
+ {% block row scoped %}{% endblock %}
103
+ </div>
104
+ {% endfor %}
105
+ </div>
106
+ </body>
107
+ </html>
@@ -0,0 +1,57 @@
1
+ {% extends "rank_board.html" %}
2
+
3
+ {% block style %}
4
+ /* ---------------- 排行说明 ---------------- */
5
+ .cond { position: relative; width: 100%; height: 130px; }
6
+ .cond .panel {
7
+ position: absolute; left: 20px; top: 20px; width: {{ card_w - 60 }}px; height: 90px;
8
+ border-radius: 8px; background: rgba(36, 36, 41, .9);
9
+ }
10
+ .cond .accent { position: absolute; left: 20px; top: 20px; width: {{ card_w - 60 }}px; height: 7px; background: rgb(203, 161, 95); }
11
+ .cond .label { left: 40px; top: 60px; font-size: 28px; color: {{ grey }}; }
12
+ .cond .line { left: 185px; font-size: 20px; color: {{ gold }}; }
13
+ .cond .notes { left: 1260px; top: 100px; font-size: 16px; color: {{ gold }}; }
14
+
15
+ /* ---------------- 行内: 角色数 / 前十角色 / 最高分 / 总分 ---------------- */
16
+ .row .count-l { left: 210px; top: 45px; font-size: 18px; }
17
+ .row .count-v { left: 280px; top: 45px; font-size: 20px; color: {{ red }}; }
18
+ /* 挂了命令的格子会被交互桥改成 position:relative, 这里按更高优先级钉回绝对定位 */
19
+ .row .ch {
20
+ position: absolute; top: 35px; width: 40px; height: 40px;
21
+ -webkit-mask-image: {{ char_mask }}; mask-image: {{ char_mask }};
22
+ }
23
+ .row .cs {
24
+ position: absolute; top: 77px; width: 40px; transform: translateY(-50%);
25
+ text-align: center; font-size: 12px; color: {{ gold }};
26
+ }
27
+ .row .best-v { left: 1080px; top: 45px; font-size: 30px; color: lightgreen; }
28
+ .row .best-l { left: 1080px; top: 75px; font-size: 16px; }
29
+ .row .total-v { left: 1180px; top: 45px; font-size: 34px; }
30
+ .row .total-l { left: 1180px; top: 75px; font-size: 16px; }
31
+ {% endblock %}
32
+
33
+ {% block board %}
34
+ <div class="cond">
35
+ <div class="panel"></div>
36
+ <div class="accent"></div>
37
+ <div class="t label">排行说明</div>
38
+ <div class="t line" style="top: 50px">{{ cond.line1 }}</div>
39
+ <div class="t line" style="top: 85px">{{ cond.line2 }}</div>
40
+ <div class="t rm notes">{{ cond.notes }}</div>
41
+ </div>
42
+ {% endblock %}
43
+
44
+ {% block row %}
45
+ <div class="t count-l">角色数:</div>
46
+ <div class="t count-v">{{ row.count }}</div>
47
+ {% for c in row.chars %}
48
+ <div class="ch icon-img {{ c.cls }}" style="left: {{ c.x }}px"{% if c.cmd %} data-ww-cmd="{{ c.cmd }}"{% endif %}></div>
49
+ <div class="cs" style="left: {{ c.x }}px">{{ c.score }}</div>
50
+ {% endfor %}
51
+ {% if row.best %}
52
+ <div class="t mm best-v">{{ row.best }}</div>
53
+ <div class="t mm best-l">最高分</div>
54
+ {% endif %}
55
+ <div class="t mm total-v">{{ row.total }}</div>
56
+ <div class="t mm total-l">总分</div>
57
+ {% endblock %}
@@ -0,0 +1,66 @@
1
+ {% extends "rank_board.html" %}
2
+
3
+ {% block style %}
4
+ /* ---------------- 行内: 队伍块 / 总分 ---------------- */
5
+ .row .gold { position: absolute; left: 210px; top: 40px; display: flex; align-items: center; gap: 4px; transform: translateY(-50%); }
6
+ .row .gold .l { font-size: 12px; }
7
+ .row .gold .v { font-size: 18px; color: {{ red }}; }
8
+ .row .team-count { left: 454px; top: 40px; font-size: 20px; color: {{ grey }}; }
9
+ .row .score { top: 55px; font-size: 44px; }
10
+
11
+ /* 挂了命令的格子会被交互桥改成 position:relative, 这里按更高优先级钉回绝对定位 */
12
+ .row .ch { position: absolute; top: {{ char_y }}px; width: {{ char_size }}px; height: {{ char_size }}px; }
13
+ .row .ch .pic { position: absolute; inset: 0; background-size: 100% 100%; }
14
+ .row .ch .pic.round { border-radius: 50%; }
15
+ .row .ch .chain { position: absolute; right: 0; bottom: 0; width: {{ chain_size }}px; height: {{ chain_size }}px; }
16
+ .row .ch .chain span { position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%); font-size: 12px; }
17
+
18
+ /* 队伍不足 3 人时的占位格 */
19
+ .row .hole {
20
+ position: absolute; top: {{ char_y }}px; width: {{ char_size }}px; height: {{ char_size }}px;
21
+ background: rgba(60, 60, 60, .5); border: 1px solid rgba(120, 120, 120, .78);
22
+ }
23
+ .row .hole span { position: absolute; left: 50%; transform: translate(-50%, -50%); font-size: 12px; color: {{ grey }}; }
24
+
25
+ .row .buff { top: {{ buff_y }}px; width: {{ buff_size }}px; height: {{ buff_size }}px; }
26
+ .row .buff .plate { position: absolute; inset: 0; border-radius: 5px; background: rgba(0, 0, 0, .8); }
27
+ .row .buff .pic { position: absolute; inset: 0; background-size: 100% 100%; }
28
+ .row .team-score { top: 80px; font-size: 18px; }
29
+
30
+ /* 满分档的水晶炫彩 */
31
+ .crystal { background-image: {{ crystal }}; -webkit-background-clip: text; background-clip: text; color: transparent; }
32
+ {% endblock %}
33
+
34
+ {% block row %}
35
+ {% if single_team %}
36
+ <div class="gold"><span class="l">上场队伍角色金数:</span><span class="v">{{ row.gold }}</span></div>
37
+ {% elif row.team_count %}
38
+ <div class="t mm team-count">{{ row.team_count }}</div>
39
+ {% endif %}
40
+ <div class="t mm score{% if not row.score_color %} crystal{% endif %}"
41
+ style="left: {{ row.score_x }}px{% if row.score_color %}; color: {{ row.score_color }}{% endif %}">{{ row.score }}</div>
42
+ {% for team in row.teams %}
43
+ {% for c in team.chars %}
44
+ <div class="ch icon-img" style="left: {{ c.x }}px"{% if c.cmd %} data-ww-cmd="{{ c.cmd }}"{% endif %}>
45
+ <div class="pic {{ c.cls }}{% if c.round %} round{% endif %}"></div>
46
+ {% if c.chain != -1 %}<div class="chain" style="background: {{ c.chain_color }}"><span>{{ c.chain }}</span></div>{% endif %}
47
+ </div>
48
+ {% endfor %}
49
+ {% for x in team.holes %}
50
+ <div class="hole" style="left: {{ x }}px">
51
+ <span style="top: {{ hole_first_y }}px">模版</span>
52
+ <span style="top: {{ hole_second_y }}px">角色</span>
53
+ </div>
54
+ {% endfor %}
55
+ {% if team.buff %}
56
+ <div class="buff icon-img" style="left: {{ team.buff_x }}px">
57
+ <div class="plate"></div>
58
+ <div class="pic {{ team.buff }}"></div>
59
+ </div>
60
+ {% endif %}
61
+ {% if not single_team %}
62
+ <div class="t mm team-score{% if not team.score_color %} crystal{% endif %}"
63
+ style="left: {{ team.score_x }}px{% if team.score_color %}; color: {{ team.score_color }}{% endif %}">{{ team.score }}</div>
64
+ {% endif %}
65
+ {% endfor %}
66
+ {% endblock %}