malcolmrey commited on
Commit
ca3ae70
·
verified ·
1 Parent(s): 5399051

Upload 4 files

Browse files

new version of the browser (index from 01.11.2025)

Files changed (4) hide show
  1. comparator.js +30 -3
  2. data-civitai.js +5 -1
  3. data-huggingface.js +0 -0
  4. index.html +1044 -239
comparator.js CHANGED
@@ -119,6 +119,18 @@ function filterByType(element) {
119
  if (document.getElementById('selectedFlux').checked && !element.flux) {
120
  return false;
121
  }
 
 
 
 
 
 
 
 
 
 
 
 
122
  break;
123
  case 'missing': {
124
  if (document.getElementById('selectedLocon').checked && element.locon) {
@@ -139,6 +151,18 @@ function filterByType(element) {
139
  if (document.getElementById('selectedFlux').checked && element.flux) {
140
  return false;
141
  }
 
 
 
 
 
 
 
 
 
 
 
 
142
  break;
143
  }
144
  }
@@ -182,14 +206,17 @@ function searchModels(value) {
182
  Lora: ${yesNo(element.lora)}
183
  ${linkPrefix(element.loraHFLink)}HF: ${yesNo(
184
  element.loraHF,
185
- )}${linkSuffix(element.loraHFLink)}
186
  Embedding: ${yesNo(element.embedding)}
187
  ${linkPrefix(element.embeddingHFLink)}HF: ${yesNo(
188
  element.embeddingHF,
189
- )}${linkSuffix(element.embeddingHFLink)}
190
  Flux: ${yesNo(element.flux)} ${linkPrefix(element.fluxHFLink)}HF: ${yesNo(
191
  element.fluxHF,
192
- )}${linkSuffix(element.fluxHFLink)}
 
 
 
193
  </div>
194
  </div>`;
195
  });
 
119
  if (document.getElementById('selectedFlux').checked && !element.flux) {
120
  return false;
121
  }
122
+
123
+ if (document.getElementById('selectedWan').checked && !element.wan) {
124
+ return false;
125
+ }
126
+
127
+ if (document.getElementById('selectedSdxl').checked && !element.sdxl) {
128
+ return false;
129
+ }
130
+
131
+ if (document.getElementById('selectedQwen').checked && !element.qwen) {
132
+ return false;
133
+ }
134
  break;
135
  case 'missing': {
136
  if (document.getElementById('selectedLocon').checked && element.locon) {
 
151
  if (document.getElementById('selectedFlux').checked && element.flux) {
152
  return false;
153
  }
154
+
155
+ if (document.getElementById('selectedWan').checked && element.wan) {
156
+ return false;
157
+ }
158
+
159
+ if (document.getElementById('selectedSdxl').checked && element.sdxl) {
160
+ return false;
161
+ }
162
+
163
+ if (document.getElementById('selectedQwen').checked && element.qwen) {
164
+ return false;
165
+ }
166
  break;
167
  }
168
  }
 
206
  Lora:&nbsp;${yesNo(element.lora)}
207
  ${linkPrefix(element.loraHFLink)}HF: ${yesNo(
208
  element.loraHF,
209
+ )}${linkSuffix(element.loraHFLink)}<br>
210
  Embedding: ${yesNo(element.embedding)}
211
  ${linkPrefix(element.embeddingHFLink)}HF: ${yesNo(
212
  element.embeddingHF,
213
+ )}${linkSuffix(element.embeddingHFLink)}<br>
214
  Flux: ${yesNo(element.flux)} ${linkPrefix(element.fluxHFLink)}HF: ${yesNo(
215
  element.fluxHF,
216
+ )}${linkSuffix(element.fluxHFLink)}<br>
217
+ WAN: ${yesNo(element.wan)} ${linkPrefix(element.wanHFLink)}HF: ${yesNo(
218
+ element.wanHF,
219
+ )}${linkSuffix(element.wanHFLink)}
220
  </div>
221
  </div>`;
222
  });
data-civitai.js CHANGED
@@ -10198,5 +10198,9 @@ const models = {
10198
  "imageUrl": "https://image.civitai.com/xG1nkqKTMzGDvpLrqFT7WA/67a80bd3-5418-49e8-8759-2618c5e17f74/width=450/4361259.jpeg",
10199
  "createdAt": "2023-12-09T22:58:18.789Z"
10200
  }
10201
- ]
 
 
 
 
10202
  }
 
10198
  "imageUrl": "https://image.civitai.com/xG1nkqKTMzGDvpLrqFT7WA/67a80bd3-5418-49e8-8759-2618c5e17f74/width=450/4361259.jpeg",
10199
  "createdAt": "2023-12-09T22:58:18.789Z"
10200
  }
10201
+ ],
10202
+ "fluxes": [],
10203
+ "wans": [],
10204
+ "sdxls": [],
10205
+ "qwens": []
10206
  }
data-huggingface.js ADDED
The diff for this file is too large to render. See raw diff
 
index.html CHANGED
@@ -1,239 +1,1044 @@
1
- <!DOCTYPE html>
2
-
3
- <head>
4
- <meta charset="utf-8" />
5
- <script type="text/javascript" src="data-civitai.js"></script>
6
- <script type="text/javascript" src="data-local.js"></script>
7
- <script type="text/javascript" src="comparator.js"></script>
8
- <style>
9
- body {
10
- color: whitesmoke;
11
- background-color: rgb(13, 46, 11);
12
- }
13
-
14
- a {
15
- text-decoration: none;
16
- color: white;
17
- }
18
-
19
- .mainContent {
20
- position: absolute;
21
- margin-top: 20px;
22
- display: flex;
23
- justify-content: center;
24
- flex-wrap: wrap;
25
- }
26
-
27
- .searchBox {
28
- margin-top: 15px;
29
- margin-left: 20px;
30
- }
31
-
32
- .found {
33
- width: 30px;
34
- }
35
-
36
- .element {
37
- padding: 0;
38
- margin: 3px;
39
- border: 1px solid rgb(5, 90, 0);
40
- display: inline-block;
41
- position: relative;
42
- width: 192px;
43
- height: 283px;
44
- }
45
-
46
- .element .modelType {
47
- position: absolute;
48
- padding: 2px;
49
- padding-left: 4px;
50
- padding-right: 4px;
51
- right: 0;
52
- background-color: rgba(0, 0, 0, 0.5);
53
- bottom: 22px;
54
- }
55
-
56
- .element .modelName {
57
- background-color: rgba(50, 217, 8, 0.15);
58
- text-overflow: ellipsis;
59
- overflow: hidden;
60
- white-space: nowrap;
61
- padding-left: 2px;
62
- }
63
-
64
- .element .modelCreator {
65
- position: absolute;
66
- background-color: rgba(0, 0, 0, 0.5);
67
- padding: 2px;
68
- padding-left: 4px;
69
- padding-right: 4px;
70
- bottom: 22px;
71
- }
72
-
73
- .statsBox {
74
- position: relative;
75
- top: -88px;
76
- padding-left: 6px;
77
- background-color: rgba(0, 0, 0, 0.5);
78
- }
79
-
80
- .statsBox .statsDownloadCount {
81
- position: absolute;
82
- top: 0;
83
- left: 0;
84
- }
85
-
86
- .statsBox .statsFavoriteCount {
87
- position: absolute;
88
- top: 0;
89
- left: 52px;
90
- }
91
-
92
- .statsBox .statsCommentCount {
93
- position: absolute;
94
- top: 0;
95
- left: 105px;
96
- }
97
-
98
- .statsBox .statsRating {
99
- position: absolute;
100
- top: 0;
101
- left: 141px;
102
- }
103
- </style>
104
- </head>
105
-
106
- <body>
107
- <input
108
- class="searchBox"
109
- id="search"
110
- type="text"
111
- onkeyup="javascript:searchModels(this.value);"
112
- placeholder="search"
113
- />
114
- <button
115
- onclick="javascript:clearCurrentSearchValue(); javascript:searchModels(getCurrentSearchValue())"
116
- >
117
- Clear
118
- </button>
119
-
120
- Found: <input class="found" id="found" type="text" readonly />
121
-
122
- <label for="searchMode">Mode</label>
123
- <select
124
- name="searchMode"
125
- id="searchMode"
126
- onchange="javascript:searchModels(getCurrentSearchValue());"
127
- >
128
- <option value="available">Available</option>
129
- <option value="missing">Missing</option>
130
- </select>
131
-
132
- <label>
133
- <input
134
- id="selectedLora"
135
- type="checkbox"
136
- onclick="javascript:searchModels(getCurrentSearchValue());"
137
- />
138
- LoRA
139
- </label>
140
-
141
- <label>
142
- <input
143
- id="selectedLocon"
144
- type="checkbox"
145
- onclick="javascript:searchModels(getCurrentSearchValue());"
146
- />
147
- LoCon
148
- </label>
149
-
150
- <label>
151
- <input
152
- id="selectedEmbedding"
153
- type="checkbox"
154
- onclick="javascript:searchModels(getCurrentSearchValue());"
155
- />
156
- Embedding
157
- </label>
158
-
159
- <label>
160
- <input
161
- id="selectedFlux"
162
- type="checkbox"
163
- onclick="javascript:searchModels(getCurrentSearchValue());"
164
- />
165
- Flux
166
- </label>
167
-
168
- <div id="mainContent" class="mainContent"></div>
169
-
170
- <script type="text/javascript">
171
- function yesNo(value) {
172
- if (value === undefined) {
173
- return '❓';
174
- }
175
- return value ? '✅' : '❌';
176
- }
177
-
178
- const notMatched = {
179
- lycoris: [],
180
- lora: [],
181
- embedding: [],
182
- flux: [],
183
- };
184
-
185
- models.lycorises.forEach((lycoris) => {
186
- const key = prepareKey(lycoris.name);
187
- if (presence[key] !== undefined) {
188
- presence[key].loconCivitai = true;
189
-
190
- setImageUrl(key, lycoris.imageUrl);
191
- presence[key].loconCivitaiLink = lycoris.url;
192
- } else if (!isKnownSkippableKey(key)) {
193
- notMatched.lycoris.push(key);
194
- }
195
- });
196
-
197
- models.loras.forEach((lora) => {
198
- const key = prepareKey(lora.name);
199
- if (presence[key] !== undefined) {
200
- presence[key].loraCivitai = true;
201
-
202
- setImageUrl(key, lora.imageUrl);
203
- presence[key].loraCivitaiLink = lora.url;
204
- } else if (!isKnownSkippableKey(key)) {
205
- notMatched.lora.push(key);
206
- }
207
- });
208
-
209
- models.embeddings.forEach((embedding) => {
210
- const key = prepareKey(embedding.name);
211
- if (presence[key] !== undefined) {
212
- presence[key].embeddingCivitai = true;
213
-
214
- setImageUrl(key, embedding.imageUrl);
215
- presence[key].embeddingCivitaiLink = embedding.url;
216
- } else if (!isKnownSkippableKey(key)) {
217
- notMatched.embedding.push(key);
218
- }
219
- });
220
-
221
- console.log(notMatched);
222
-
223
- const presenceModels = [];
224
- for (const property in presence) {
225
- const element = {
226
- key: property,
227
- locon: presence[property].locon,
228
- lora: presence[property].lora,
229
- embedding: presence[property].embedding,
230
- flux: presence[property].flux,
231
- mega: undefined,
232
- imageUrl: presence[property]?.imageUrl ?? undefined,
233
- };
234
- presenceModels.push(element);
235
- }
236
-
237
- searchModels(getCurrentSearchValue());
238
- </script>
239
- </body>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>Mal's Models</title>
7
+ <script type="text/javascript" src="data-civitai.js"></script>
8
+ <script type="text/javascript" src="data-huggingface.js"></script>
9
+ <script type="text/javascript" src="comparator.js"></script>
10
+ <link rel="preconnect" href="https://fonts.googleapis.com">
11
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
12
+ <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
13
+ <style>
14
+ * {
15
+ margin: 0;
16
+ padding: 0;
17
+ box-sizing: border-box;
18
+ }
19
+
20
+ body {
21
+ font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
22
+ background: linear-gradient(135deg, #0f172a 0%, #1e293b 100%);
23
+ color: #f1f5f9;
24
+ min-height: 100vh;
25
+ padding: 20px;
26
+ }
27
+
28
+ a {
29
+ text-decoration: none;
30
+ color: #60a5fa;
31
+ transition: color 0.2s;
32
+ }
33
+
34
+ a:hover {
35
+ color: #93c5fd;
36
+ }
37
+
38
+ .header {
39
+ max-width: 1400px;
40
+ margin: 0 auto 30px;
41
+ background: rgba(30, 41, 59, 0.7);
42
+ backdrop-filter: blur(10px);
43
+ border-radius: 16px;
44
+ padding: 24px;
45
+ box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
46
+ border: 1px solid rgba(148, 163, 184, 0.1);
47
+ }
48
+
49
+ .header h1 {
50
+ font-size: 28px;
51
+ font-weight: 700;
52
+ margin-bottom: 20px;
53
+ background: linear-gradient(135deg, #60a5fa 0%, #a78bfa 100%);
54
+ -webkit-background-clip: text;
55
+ -webkit-text-fill-color: transparent;
56
+ background-clip: text;
57
+ text-align: center;
58
+ }
59
+
60
+ .controls {
61
+ display: flex;
62
+ flex-wrap: wrap;
63
+ gap: 12px;
64
+ align-items: center;
65
+ justify-content: center;
66
+ }
67
+
68
+ .search-container {
69
+ flex: 1 1 auto;
70
+ min-width: 300px;
71
+ max-width: 600px;
72
+ position: relative;
73
+ }
74
+
75
+ .search-container input[type="text"] {
76
+ width: 100%;
77
+ padding: 12px 16px;
78
+ padding-right: 100px;
79
+ background: rgba(15, 23, 42, 0.6);
80
+ border: 2px solid rgba(148, 163, 184, 0.2);
81
+ border-radius: 12px;
82
+ color: #f1f5f9;
83
+ font-size: 15px;
84
+ font-family: inherit;
85
+ transition: all 0.3s;
86
+ }
87
+
88
+ .search-container input[type="text"]:focus {
89
+ outline: none;
90
+ border-color: #60a5fa;
91
+ background: rgba(15, 23, 42, 0.8);
92
+ box-shadow: 0 0 0 4px rgba(96, 165, 250, 0.1);
93
+ }
94
+
95
+ .search-container input[type="text"]::placeholder {
96
+ color: #64748b;
97
+ }
98
+
99
+ .clear-btn {
100
+ position: absolute;
101
+ right: 8px;
102
+ top: 50%;
103
+ transform: translateY(-50%);
104
+ padding: 6px 14px;
105
+ background: rgba(239, 68, 68, 0.2);
106
+ border: 1px solid rgba(239, 68, 68, 0.3);
107
+ border-radius: 8px;
108
+ color: #fca5a5;
109
+ font-size: 13px;
110
+ font-weight: 500;
111
+ cursor: pointer;
112
+ transition: all 0.2s;
113
+ }
114
+
115
+ .clear-btn:hover {
116
+ background: rgba(239, 68, 68, 0.3);
117
+ color: #fecaca;
118
+ }
119
+
120
+ .mode-select {
121
+ padding: 12px 40px 12px 16px;
122
+ background: rgba(15, 23, 42, 0.6);
123
+ border: 2px solid rgba(148, 163, 184, 0.2);
124
+ border-radius: 12px;
125
+ color: #f1f5f9;
126
+ font-size: 15px;
127
+ font-family: inherit;
128
+ cursor: pointer;
129
+ transition: all 0.3s;
130
+ font-weight: 500;
131
+ height: 48px;
132
+ line-height: 20px;
133
+ min-width: 150px;
134
+ flex-shrink: 0;
135
+ appearance: none;
136
+ -webkit-appearance: none;
137
+ -moz-appearance: none;
138
+ background-image: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%2394a3b8' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3e%3cpolyline points='6 9 12 15 18 9'%3e%3c/polyline%3e%3c/svg%3e");
139
+ background-repeat: no-repeat;
140
+ background-position: right 12px center;
141
+ background-size: 20px;
142
+ box-shadow: none;
143
+ outline: none;
144
+ }
145
+
146
+ .mode-select::-ms-expand {
147
+ display: none;
148
+ }
149
+
150
+ .mode-select:focus {
151
+ outline: none;
152
+ border-color: #60a5fa;
153
+ box-shadow: 0 0 0 4px rgba(96, 165, 250, 0.1);
154
+ }
155
+
156
+ .mode-select option {
157
+ background: #0f172a;
158
+ color: #e2e8f0;
159
+ padding: 16px 20px;
160
+ font-size: 15px;
161
+ font-weight: 500;
162
+ border: none;
163
+ outline: none;
164
+ }
165
+
166
+ .mode-select option:hover {
167
+ background: linear-gradient(90deg, rgba(96, 165, 250, 0.15) 0%, rgba(96, 165, 250, 0.05) 100%);
168
+ color: #60a5fa;
169
+ font-weight: 600;
170
+ }
171
+
172
+ .mode-select option:checked,
173
+ .mode-select option:focus {
174
+ background: linear-gradient(90deg, rgba(96, 165, 250, 0.25) 0%, rgba(96, 165, 250, 0.15) 100%);
175
+ color: #93c5fd;
176
+ font-weight: 600;
177
+ box-shadow: none;
178
+ outline: none;
179
+ }
180
+
181
+ .checkbox-group {
182
+ display: flex;
183
+ flex-wrap: wrap;
184
+ gap: 8px;
185
+ align-items: center;
186
+ }
187
+
188
+ .checkbox-label {
189
+ display: flex;
190
+ align-items: center;
191
+ gap: 8px;
192
+ padding: 10px 16px;
193
+ background: rgba(15, 23, 42, 0.4);
194
+ border: 2px solid rgba(148, 163, 184, 0.2);
195
+ border-radius: 10px;
196
+ cursor: pointer;
197
+ transition: all 0.2s;
198
+ font-size: 14px;
199
+ font-weight: 500;
200
+ user-select: none;
201
+ }
202
+
203
+ .checkbox-label:hover {
204
+ background: rgba(15, 23, 42, 0.6);
205
+ border-color: rgba(148, 163, 184, 0.3);
206
+ }
207
+
208
+ .checkbox-label input[type="checkbox"] {
209
+ width: 18px;
210
+ height: 18px;
211
+ cursor: pointer;
212
+ accent-color: #60a5fa;
213
+ }
214
+
215
+ .checkbox-label input[type="checkbox"]:checked + span {
216
+ color: #60a5fa;
217
+ }
218
+
219
+ .stats-bar {
220
+ display: flex;
221
+ align-items: center;
222
+ gap: 12px;
223
+ padding: 12px 20px;
224
+ background: rgba(15, 23, 42, 0.6);
225
+ border-radius: 10px;
226
+ font-size: 14px;
227
+ font-weight: 600;
228
+ flex-shrink: 0;
229
+ }
230
+
231
+ .stats-bar label {
232
+ color: #94a3b8;
233
+ }
234
+
235
+ .stats-bar input {
236
+ width: 70px;
237
+ padding: 6px 10px;
238
+ background: rgba(30, 41, 59, 0.8);
239
+ border: 1px solid rgba(148, 163, 184, 0.2);
240
+ border-radius: 6px;
241
+ color: #60a5fa;
242
+ text-align: center;
243
+ font-weight: 700;
244
+ font-size: 16px;
245
+ }
246
+
247
+ .mainContent {
248
+ max-width: 1400px;
249
+ margin: 0 auto;
250
+ display: grid;
251
+ grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
252
+ gap: 20px;
253
+ padding: 0 4px;
254
+ }
255
+
256
+ .element {
257
+ background: rgba(30, 41, 59, 0.6);
258
+ backdrop-filter: blur(10px);
259
+ border-radius: 16px;
260
+ overflow: hidden;
261
+ border: 1px solid rgba(148, 163, 184, 0.1);
262
+ transition: all 0.3s;
263
+ box-shadow: 0 4px 16px rgba(0, 0, 0, 0.2);
264
+ display: flex;
265
+ flex-direction: column;
266
+ cursor: pointer;
267
+ }
268
+
269
+ .element:hover {
270
+ transform: translateY(-4px);
271
+ box-shadow: 0 12px 32px rgba(0, 0, 0, 0.4);
272
+ border-color: rgba(96, 165, 250, 0.3);
273
+ }
274
+
275
+ .element .modelName {
276
+ padding: 14px 16px;
277
+ background: rgba(15, 23, 42, 0.8);
278
+ font-weight: 600;
279
+ font-size: 14px;
280
+ text-overflow: ellipsis;
281
+ overflow: hidden;
282
+ white-space: nowrap;
283
+ border-bottom: 1px solid rgba(148, 163, 184, 0.1);
284
+ color: #e2e8f0;
285
+ }
286
+
287
+ .element .imageContainer {
288
+ position: relative;
289
+ width: 100%;
290
+ padding-top: 137.5%; /* 192/264 aspect ratio */
291
+ overflow: hidden;
292
+ background: rgba(15, 23, 42, 0.6);
293
+ }
294
+
295
+ .element .imageContainer img {
296
+ position: absolute;
297
+ top: 0;
298
+ left: 0;
299
+ width: 100%;
300
+ height: 100%;
301
+ object-fit: cover;
302
+ transition: transform 0.3s;
303
+ }
304
+
305
+ .element:hover .imageContainer img {
306
+ transform: scale(1.05);
307
+ }
308
+
309
+ .statsBox {
310
+ padding: 16px;
311
+ background: rgba(15, 23, 42, 0.8);
312
+ font-size: 13px;
313
+ line-height: 1.8;
314
+ flex: 1;
315
+ display: none; /* Hidden in main view */
316
+ }
317
+
318
+ .statsBox .stat-row {
319
+ display: flex;
320
+ align-items: center;
321
+ gap: 8px;
322
+ margin-bottom: 6px;
323
+ }
324
+
325
+ .statsBox .stat-row:last-child {
326
+ margin-bottom: 0;
327
+ }
328
+
329
+ .statsBox .stat-label {
330
+ font-weight: 600;
331
+ color: #94a3b8;
332
+ min-width: 75px;
333
+ }
334
+
335
+ .statsBox .stat-value {
336
+ display: flex;
337
+ align-items: center;
338
+ gap: 6px;
339
+ }
340
+
341
+ /* Modal Styles */
342
+ .modal-overlay {
343
+ display: none;
344
+ position: fixed;
345
+ top: 0;
346
+ left: 0;
347
+ right: 0;
348
+ bottom: 0;
349
+ background: rgba(0, 0, 0, 0.8);
350
+ backdrop-filter: blur(8px);
351
+ z-index: 1000;
352
+ align-items: center;
353
+ justify-content: center;
354
+ padding: 20px;
355
+ animation: fadeIn 0.2s ease-out;
356
+ }
357
+
358
+ .modal-overlay.active {
359
+ display: flex;
360
+ }
361
+
362
+ .modal-content {
363
+ background: linear-gradient(135deg, #1e293b 0%, #0f172a 100%);
364
+ border-radius: 20px;
365
+ max-width: 1100px;
366
+ width: 100%;
367
+ max-height: 90vh;
368
+ overflow: hidden;
369
+ position: relative;
370
+ border: 2px solid rgba(148, 163, 184, 0.2);
371
+ box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5);
372
+ animation: slideUp 0.3s ease-out;
373
+ display: flex;
374
+ flex-direction: column;
375
+ }
376
+
377
+ @keyframes slideUp {
378
+ from {
379
+ opacity: 0;
380
+ transform: translateY(30px);
381
+ }
382
+ to {
383
+ opacity: 1;
384
+ transform: translateY(0);
385
+ }
386
+ }
387
+
388
+ .modal-header {
389
+ padding: 24px;
390
+ border-bottom: 1px solid rgba(148, 163, 184, 0.1);
391
+ display: flex;
392
+ align-items: center;
393
+ justify-content: space-between;
394
+ position: sticky;
395
+ top: 0;
396
+ background: rgba(30, 41, 59, 0.95);
397
+ backdrop-filter: blur(10px);
398
+ z-index: 10;
399
+ }
400
+
401
+ .modal-title {
402
+ font-size: 24px;
403
+ font-weight: 700;
404
+ color: #f1f5f9;
405
+ margin: 0;
406
+ }
407
+
408
+ .modal-close {
409
+ width: 36px;
410
+ height: 36px;
411
+ border-radius: 10px;
412
+ background: rgba(239, 68, 68, 0.2);
413
+ border: 1px solid rgba(239, 68, 68, 0.3);
414
+ color: #fca5a5;
415
+ font-size: 20px;
416
+ cursor: pointer;
417
+ display: flex;
418
+ align-items: center;
419
+ justify-content: center;
420
+ transition: all 0.2s;
421
+ font-weight: 700;
422
+ }
423
+
424
+ .modal-close:hover {
425
+ background: rgba(239, 68, 68, 0.3);
426
+ color: #fecaca;
427
+ transform: rotate(90deg);
428
+ }
429
+
430
+ .modal-body {
431
+ display: flex;
432
+ flex: 1;
433
+ overflow: hidden;
434
+ gap: 0;
435
+ }
436
+
437
+ .modal-left {
438
+ flex: 0 0 45%;
439
+ display: flex;
440
+ align-items: center;
441
+ justify-content: center;
442
+ background: rgba(15, 23, 42, 0.6);
443
+ padding: 24px;
444
+ }
445
+
446
+ .modal-image-container {
447
+ width: 100%;
448
+ border-radius: 12px;
449
+ overflow: hidden;
450
+ box-shadow: 0 8px 24px rgba(0, 0, 0, 0.4);
451
+ max-height: 100%;
452
+ }
453
+
454
+ .modal-image-container img {
455
+ width: 100%;
456
+ height: auto;
457
+ display: block;
458
+ }
459
+
460
+ .modal-right {
461
+ flex: 1;
462
+ padding: 24px;
463
+ overflow-y: auto;
464
+ display: flex;
465
+ flex-direction: column;
466
+ gap: 16px;
467
+ }
468
+
469
+ .modal-stats {
470
+ display: grid;
471
+ gap: 12px;
472
+ }
473
+
474
+ .modal-stat-row {
475
+ display: flex;
476
+ align-items: center;
477
+ justify-content: space-between;
478
+ padding: 16px 20px;
479
+ background: rgba(15, 23, 42, 0.6);
480
+ border-radius: 12px;
481
+ border: 1px solid rgba(148, 163, 184, 0.1);
482
+ transition: all 0.2s;
483
+ }
484
+
485
+ .modal-stat-row:hover {
486
+ background: rgba(15, 23, 42, 0.8);
487
+ border-color: rgba(148, 163, 184, 0.2);
488
+ }
489
+
490
+ .modal-stat-label {
491
+ font-weight: 600;
492
+ font-size: 15px;
493
+ color: #cbd5e1;
494
+ }
495
+
496
+ .modal-stat-value {
497
+ display: flex;
498
+ align-items: center;
499
+ gap: 12px;
500
+ }
501
+
502
+ .modal-hf-link {
503
+ padding: 6px 12px;
504
+ background: rgba(255, 107, 0, 0.15);
505
+ border: 1px solid rgba(255, 107, 0, 0.3);
506
+ border-radius: 8px;
507
+ font-size: 12px;
508
+ font-weight: 600;
509
+ color: #ff9d5c;
510
+ transition: all 0.2s;
511
+ text-decoration: none;
512
+ display: inline-flex;
513
+ align-items: center;
514
+ gap: 6px;
515
+ }
516
+
517
+ .modal-hf-link:hover {
518
+ background: rgba(255, 107, 0, 0.25);
519
+ border-color: rgba(255, 107, 0, 0.5);
520
+ transform: translateY(-1px);
521
+ color: #ffb380;
522
+ }
523
+
524
+ .modal-hf-placeholder {
525
+ padding: 6px 12px;
526
+ background: rgba(100, 116, 139, 0.15);
527
+ border: 1px solid rgba(100, 116, 139, 0.3);
528
+ border-radius: 8px;
529
+ font-size: 12px;
530
+ font-weight: 600;
531
+ color: #94a3b8;
532
+ display: inline-flex;
533
+ align-items: center;
534
+ gap: 6px;
535
+ opacity: 0.5;
536
+ }
537
+
538
+ .hf-logo {
539
+ width: 16px;
540
+ height: 16px;
541
+ display: inline-block;
542
+ }
543
+
544
+ @media (max-width: 900px) {
545
+ .modal-body {
546
+ flex-direction: column;
547
+ }
548
+
549
+ .modal-left {
550
+ flex: 0 0 auto;
551
+ max-height: 400px;
552
+ }
553
+
554
+ .modal-right {
555
+ flex: 1;
556
+ }
557
+ }
558
+
559
+ .status-icon {
560
+ font-size: 16px;
561
+ display: inline-block;
562
+ }
563
+
564
+ .status-available {
565
+ color: #4ade80;
566
+ }
567
+
568
+ .status-unavailable {
569
+ color: #f87171;
570
+ }
571
+
572
+ .status-unknown {
573
+ color: #fbbf24;
574
+ }
575
+
576
+ .hf-link {
577
+ padding: 2px 8px;
578
+ background: rgba(96, 165, 250, 0.1);
579
+ border: 1px solid rgba(96, 165, 250, 0.2);
580
+ border-radius: 6px;
581
+ font-size: 11px;
582
+ font-weight: 600;
583
+ color: #60a5fa;
584
+ transition: all 0.2s;
585
+ display: inline-block;
586
+ }
587
+
588
+ .hf-link:hover {
589
+ background: rgba(96, 165, 250, 0.2);
590
+ border-color: rgba(96, 165, 250, 0.4);
591
+ }
592
+
593
+ @media (max-width: 768px) {
594
+ .controls {
595
+ flex-direction: column;
596
+ align-items: stretch;
597
+ }
598
+
599
+ .checkbox-group {
600
+ justify-content: center;
601
+ }
602
+
603
+ .mainContent {
604
+ grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
605
+ gap: 16px;
606
+ }
607
+ }
608
+
609
+ /* Loading animation */
610
+ @keyframes fadeIn {
611
+ from {
612
+ opacity: 0;
613
+ transform: translateY(20px);
614
+ }
615
+ to {
616
+ opacity: 1;
617
+ transform: translateY(0);
618
+ }
619
+ }
620
+
621
+ .element {
622
+ animation: fadeIn 0.4s ease-out;
623
+ }
624
+
625
+ /* Scrollbar styling */
626
+ ::-webkit-scrollbar {
627
+ width: 12px;
628
+ }
629
+
630
+ ::-webkit-scrollbar-track {
631
+ background: rgba(15, 23, 42, 0.5);
632
+ }
633
+
634
+ ::-webkit-scrollbar-thumb {
635
+ background: rgba(148, 163, 184, 0.3);
636
+ border-radius: 6px;
637
+ }
638
+
639
+ ::-webkit-scrollbar-thumb:hover {
640
+ background: rgba(148, 163, 184, 0.5);
641
+ }
642
+ </style>
643
+ </head>
644
+ <body>
645
+ <div class="header">
646
+ <h1>🎨 Mal's Models</h1>
647
+
648
+ <div class="controls">
649
+ <div class="search-container">
650
+ <input
651
+ id="search"
652
+ type="text"
653
+ onkeyup="javascript:searchModels(this.value);"
654
+ placeholder="Search models..."
655
+ />
656
+ <button
657
+ class="clear-btn"
658
+ onclick="javascript:clearCurrentSearchValue(); javascript:searchModels(getCurrentSearchValue())"
659
+ >
660
+ Clear
661
+ </button>
662
+ </div>
663
+
664
+ <select
665
+ class="mode-select"
666
+ id="searchMode"
667
+ onchange="javascript:searchModels(getCurrentSearchValue());"
668
+ >
669
+ <option value="available">Available</option>
670
+ <option value="missing">Missing</option>
671
+ </select>
672
+
673
+ <div class="stats-bar">
674
+ <label>Found:</label>
675
+ <input id="found" type="text" readonly />
676
+ </div>
677
+ </div>
678
+
679
+ <div class="controls" style="margin-top: 16px;">
680
+ <div class="checkbox-group">
681
+ <label class="checkbox-label">
682
+ <input
683
+ id="selectedLora"
684
+ type="checkbox"
685
+ onclick="javascript:searchModels(getCurrentSearchValue());"
686
+ />
687
+ <span>SD LoRA</span>
688
+ </label>
689
+
690
+ <label class="checkbox-label">
691
+ <input
692
+ id="selectedLocon"
693
+ type="checkbox"
694
+ onclick="javascript:searchModels(getCurrentSearchValue());"
695
+ />
696
+ <span>SD LoCon</span>
697
+ </label>
698
+
699
+ <label class="checkbox-label">
700
+ <input
701
+ id="selectedEmbedding"
702
+ type="checkbox"
703
+ onclick="javascript:searchModels(getCurrentSearchValue());"
704
+ />
705
+ <span>SD Embedding</span>
706
+ </label>
707
+
708
+ <label class="checkbox-label">
709
+ <input
710
+ id="selectedFlux"
711
+ type="checkbox"
712
+ onclick="javascript:searchModels(getCurrentSearchValue());"
713
+ />
714
+ <span>Flux</span>
715
+ </label>
716
+
717
+ <label class="checkbox-label">
718
+ <input
719
+ id="selectedWan"
720
+ type="checkbox"
721
+ onclick="javascript:searchModels(getCurrentSearchValue());"
722
+ />
723
+ <span>WAN</span>
724
+ </label>
725
+
726
+ <label class="checkbox-label">
727
+ <input
728
+ id="selectedSdxl"
729
+ type="checkbox"
730
+ onclick="javascript:searchModels(getCurrentSearchValue());"
731
+ />
732
+ <span>SDXL</span>
733
+ </label>
734
+
735
+ <label class="checkbox-label">
736
+ <input
737
+ id="selectedQwen"
738
+ type="checkbox"
739
+ onclick="javascript:searchModels(getCurrentSearchValue());"
740
+ />
741
+ <span>Qwen</span>
742
+ </label>
743
+ </div>
744
+ </div>
745
+ </div>
746
+
747
+ <div id="mainContent" class="mainContent"></div>
748
+
749
+ <!-- Modal -->
750
+ <div id="modalOverlay" class="modal-overlay" onclick="closeModalOnOverlay(event)">
751
+ <div class="modal-content" onclick="event.stopPropagation()">
752
+ <div class="modal-header">
753
+ <h2 id="modalTitle" class="modal-title"></h2>
754
+ <button class="modal-close" onclick="closeModal()" aria-label="Close">×</button>
755
+ </div>
756
+ <div class="modal-body">
757
+ <div class="modal-left">
758
+ <div class="modal-image-container">
759
+ <img id="modalImage" src="" alt="" />
760
+ </div>
761
+ </div>
762
+ <div class="modal-right">
763
+ <div id="modalStats" class="modal-stats"></div>
764
+ </div>
765
+ </div>
766
+ </div>
767
+ </div>
768
+
769
+ <script type="text/javascript">
770
+ function yesNo(value) {
771
+ if (value === undefined) {
772
+ return '<span class="status-icon status-unknown">❓</span>';
773
+ }
774
+ return value
775
+ ? '<span class="status-icon status-available">✅</span>'
776
+ : '<span class="status-icon status-unavailable">❌</span>';
777
+ }
778
+
779
+ function formatHFLink(link, hasHF) {
780
+ if (!link) return '';
781
+ return `<a href="${link}" target="_blank" class="hf-link">HF</a>`;
782
+ }
783
+
784
+ function formatModalHFLink(isAvailable, link) {
785
+ if (isAvailable && link) {
786
+ return `<a href="${link}" target="_blank" class="modal-hf-link">🤗 HuggingFace</a>`;
787
+ } else if (isAvailable) {
788
+ return `<span class="modal-hf-placeholder">🤗 HuggingFace</span>`;
789
+ }
790
+ return '';
791
+ }
792
+
793
+ function openModal(element) {
794
+ const modal = document.getElementById('modalOverlay');
795
+ const modalTitle = document.getElementById('modalTitle');
796
+ const modalImage = document.getElementById('modalImage');
797
+ const modalStats = document.getElementById('modalStats');
798
+
799
+ // Generate HuggingFace links
800
+ const personName = element.key;
801
+ const loconHFLink = element.locon
802
+ ? `https://huggingface.co/malcolmrey/lycoris/resolve/main/locon_${personName}_v1_from_v1_64_32.safetensors`
803
+ : undefined;
804
+ const loraHFLink = undefined; // We don't know the hyphenation pattern for loras
805
+ const embeddingHFLink = element.embedding
806
+ ? `https://huggingface.co/malcolmrey/embeddings/resolve/main/${personName}-ti.safetensors`
807
+ : undefined;
808
+ const fluxHFLink = element.flux
809
+ ? `https://huggingface.co/malcolmrey/flux/resolve/main/flux_${personName}_v1-step00000400.safetensors`
810
+ : undefined;
811
+ const wanHFLink = element.wan
812
+ ? `https://huggingface.co/malcolmrey/wan/resolve/main/wan2.1/wan_${personName}_v1.safetensors`
813
+ : undefined;
814
+
815
+ modalTitle.textContent = element.key;
816
+ modalImage.src = element.imageUrl ?? unknownImage;
817
+ modalImage.alt = element.key;
818
+
819
+ modalStats.innerHTML = `
820
+ <div class="modal-stat-row">
821
+ <span class="modal-stat-label">SD LoCon</span>
822
+ <span class="modal-stat-value">
823
+ ${yesNo(element.locon)}
824
+ ${formatModalHFLink(element.locon, loconHFLink)}
825
+ </span>
826
+ </div>
827
+ <div class="modal-stat-row">
828
+ <span class="modal-stat-label">SD LoRA</span>
829
+ <span class="modal-stat-value">
830
+ ${yesNo(element.lora)}
831
+ ${formatModalHFLink(element.lora, loraHFLink)}
832
+ </span>
833
+ </div>
834
+ <div class="modal-stat-row">
835
+ <span class="modal-stat-label">SD Embedding</span>
836
+ <span class="modal-stat-value">
837
+ ${yesNo(element.embedding)}
838
+ ${formatModalHFLink(element.embedding, embeddingHFLink)}
839
+ </span>
840
+ </div>
841
+ <div class="modal-stat-row">
842
+ <span class="modal-stat-label">Flux</span>
843
+ <span class="modal-stat-value">
844
+ ${yesNo(element.flux)}
845
+ ${formatModalHFLink(element.flux, fluxHFLink)}
846
+ </span>
847
+ </div>
848
+ <div class="modal-stat-row">
849
+ <span class="modal-stat-label">WAN</span>
850
+ <span class="modal-stat-value">
851
+ ${yesNo(element.wan)}
852
+ ${formatModalHFLink(element.wan, wanHFLink)}
853
+ </span>
854
+ </div>
855
+ <div class="modal-stat-row">
856
+ <span class="modal-stat-label">SDXL</span>
857
+ <span class="modal-stat-value">
858
+ ${yesNo(element.sdxl)}
859
+ ${formatModalHFLink(element.sdxl, element.sdxlHFLink)}
860
+ </span>
861
+ </div>
862
+ <div class="modal-stat-row">
863
+ <span class="modal-stat-label">Qwen</span>
864
+ <span class="modal-stat-value">
865
+ ${yesNo(element.qwen)}
866
+ ${formatModalHFLink(element.qwen, element.qwenHFLink)}
867
+ </span>
868
+ </div>
869
+ `;
870
+
871
+ modal.classList.add('active');
872
+ document.body.style.overflow = 'hidden';
873
+ }
874
+
875
+ function closeModal() {
876
+ const modal = document.getElementById('modalOverlay');
877
+ modal.classList.remove('active');
878
+ document.body.style.overflow = '';
879
+ }
880
+
881
+ function closeModalOnOverlay(event) {
882
+ if (event.target.id === 'modalOverlay') {
883
+ closeModal();
884
+ }
885
+ }
886
+
887
+ // Close modal on ESC key
888
+ document.addEventListener('keydown', function(event) {
889
+ if (event.key === 'Escape') {
890
+ closeModal();
891
+ }
892
+ });
893
+
894
+ const notMatched = {
895
+ lycoris: [],
896
+ lora: [],
897
+ embedding: [],
898
+ flux: [],
899
+ wan: [],
900
+ sdxl: [],
901
+ qwen: [],
902
+ };
903
+
904
+ models.lycorises.forEach((lycoris) => {
905
+ const key = prepareKey(lycoris.name);
906
+ if (presence[key] !== undefined) {
907
+ presence[key].loconCivitai = true;
908
+ setImageUrl(key, lycoris.imageUrl);
909
+ presence[key].loconCivitaiLink = lycoris.url;
910
+ } else if (!isKnownSkippableKey(key)) {
911
+ notMatched.lycoris.push(key);
912
+ }
913
+ });
914
+
915
+ models.loras.forEach((lora) => {
916
+ const key = prepareKey(lora.name);
917
+ if (presence[key] !== undefined) {
918
+ presence[key].loraCivitai = true;
919
+ setImageUrl(key, lora.imageUrl);
920
+ presence[key].loraCivitaiLink = lora.url;
921
+ } else if (!isKnownSkippableKey(key)) {
922
+ notMatched.lora.push(key);
923
+ }
924
+ });
925
+
926
+ models.embeddings.forEach((embedding) => {
927
+ const key = prepareKey(embedding.name);
928
+ if (presence[key] !== undefined) {
929
+ presence[key].embeddingCivitai = true;
930
+ setImageUrl(key, embedding.imageUrl);
931
+ presence[key].embeddingCivitaiLink = embedding.url;
932
+ } else if (!isKnownSkippableKey(key)) {
933
+ notMatched.embedding.push(key);
934
+ }
935
+ });
936
+
937
+ models.fluxes.forEach((flux) => {
938
+ const key = prepareKey(flux.name);
939
+ if (presence[key] !== undefined) {
940
+ presence[key].fluxCivitai = true;
941
+ setImageUrl(key, flux.imageUrl);
942
+ presence[key].fluxCivitaiLink = flux.url;
943
+ } else if (!isKnownSkippableKey(key)) {
944
+ notMatched.flux.push(key);
945
+ }
946
+ });
947
+
948
+ models.wans.forEach((wan) => {
949
+ const key = prepareKey(wan.name);
950
+ if (presence[key] !== undefined) {
951
+ presence[key].wanCivitai = true;
952
+ setImageUrl(key, wan.imageUrl);
953
+ presence[key].wanCivitaiLink = wan.url;
954
+ } else if (!isKnownSkippableKey(key)) {
955
+ notMatched.wan.push(key);
956
+ }
957
+ });
958
+
959
+ models.sdxls.forEach((sdxl) => {
960
+ const key = prepareKey(sdxl.name);
961
+ if (presence[key] !== undefined) {
962
+ presence[key].sdxlCivitai = true;
963
+ setImageUrl(key, sdxl.imageUrl);
964
+ presence[key].sdxlCivitaiLink = sdxl.url;
965
+ } else if (!isKnownSkippableKey(key)) {
966
+ notMatched.sdxl.push(key);
967
+ }
968
+ });
969
+
970
+ models.qwens.forEach((qwen) => {
971
+ const key = prepareKey(qwen.name);
972
+ if (presence[key] !== undefined) {
973
+ presence[key].qwenCivitai = true;
974
+ setImageUrl(key, qwen.imageUrl);
975
+ presence[key].qwenCivitaiLink = qwen.url;
976
+ } else if (!isKnownSkippableKey(key)) {
977
+ notMatched.qwen.push(key);
978
+ }
979
+ });
980
+
981
+ console.log(notMatched);
982
+
983
+ const presenceModels = [];
984
+ for (const property in presence) {
985
+ const element = {
986
+ key: property,
987
+ locon: presence[property].locon,
988
+ lora: presence[property].lora,
989
+ embedding: presence[property].embedding,
990
+ flux: presence[property].flux,
991
+ wan: presence[property].wan,
992
+ sdxl: presence[property].sdxl,
993
+ qwen: presence[property].qwen,
994
+ mega: undefined,
995
+ imageUrl: presence[property]?.imageUrl ?? undefined,
996
+ loconHFLink: presence[property]?.loconHFLink,
997
+ loraHFLink: presence[property]?.loraHFLink,
998
+ embeddingHFLink: presence[property]?.embeddingHFLink,
999
+ fluxHFLink: presence[property]?.fluxHFLink,
1000
+ wanHFLink: presence[property]?.wanHFLink,
1001
+ sdxlHFLink: presence[property]?.sdxlHFLink,
1002
+ qwenHFLink: presence[property]?.qwenHFLink,
1003
+ };
1004
+ presenceModels.push(element);
1005
+ }
1006
+
1007
+ function searchModelsModern(value) {
1008
+ const lowerCaseValue = value.toLowerCase();
1009
+
1010
+ const filtered = presenceModels.filter((element) => {
1011
+ return (
1012
+ (element.key.toLowerCase().includes(lowerCaseValue) || value === '*') &&
1013
+ filterByType(element)
1014
+ );
1015
+ });
1016
+
1017
+ document.getElementById('found').value = filtered.length;
1018
+
1019
+ const contentDiv = document.getElementById('mainContent');
1020
+ contentDiv.innerHTML = '';
1021
+
1022
+ filtered.forEach((element, index) => {
1023
+ const card = document.createElement('div');
1024
+ card.className = 'element';
1025
+ card.innerHTML = `
1026
+ <div class="modelName" title="${escapeHtml(element.key)}">${element.key}</div>
1027
+
1028
+ <div class="imageContainer">
1029
+ <img src="${element.imageUrl ?? unknownImage}" alt="${escapeHtml(element.key)}" />
1030
+ </div>
1031
+ `;
1032
+
1033
+ card.addEventListener('click', () => openModal(element));
1034
+ contentDiv.appendChild(card);
1035
+ });
1036
+ }
1037
+
1038
+ // Override the searchModels function
1039
+ window.searchModels = searchModelsModern;
1040
+
1041
+ searchModels(getCurrentSearchValue());
1042
+ </script>
1043
+ </body>
1044
+ </html>