Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="utf-8" /> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0" /> | |
| <title>Mal's Models</title> | |
| <script type="text/javascript" src="data-civitai.js"></script> | |
| <script type="text/javascript" src="data-huggingface.js"></script> | |
| <script type="text/javascript" src="comparator.js"></script> | |
| <link rel="preconnect" href="https://fonts.googleapis.com"> | |
| <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> | |
| <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet"> | |
| <style> | |
| * { | |
| margin: 0; | |
| padding: 0; | |
| box-sizing: border-box; | |
| } | |
| body { | |
| font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; | |
| background: linear-gradient(135deg, #0f172a 0%, #1e293b 100%); | |
| color: #f1f5f9; | |
| min-height: 100vh; | |
| padding: 20px; | |
| } | |
| a { | |
| text-decoration: none; | |
| color: #60a5fa; | |
| transition: color 0.2s; | |
| } | |
| a:hover { | |
| color: #93c5fd; | |
| } | |
| .header { | |
| max-width: 1400px; | |
| margin: 0 auto 30px; | |
| background: rgba(30, 41, 59, 0.7); | |
| backdrop-filter: blur(10px); | |
| border-radius: 16px; | |
| padding: 24px; | |
| box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3); | |
| border: 1px solid rgba(148, 163, 184, 0.1); | |
| } | |
| .header h1 { | |
| font-size: 28px; | |
| font-weight: 700; | |
| margin-bottom: 12px; | |
| background: linear-gradient(135deg, #60a5fa 0%, #a78bfa 100%); | |
| -webkit-background-clip: text; | |
| -webkit-text-fill-color: transparent; | |
| background-clip: text; | |
| text-align: center; | |
| } | |
| .profile-links { | |
| display: flex; | |
| flex-wrap: wrap; | |
| gap: 16px; | |
| justify-content: center; | |
| align-items: center; | |
| margin-bottom: 12px; | |
| padding: 12px; | |
| background: rgba(15, 23, 42, 0.4); | |
| border-radius: 8px; | |
| } | |
| .profile-links a { | |
| display: inline-flex; | |
| align-items: center; | |
| gap: 6px; | |
| padding: 6px 12px; | |
| background: rgba(96, 165, 250, 0.1); | |
| border-radius: 6px; | |
| font-size: 14px; | |
| transition: all 0.2s; | |
| } | |
| .profile-links a:hover { | |
| background: rgba(96, 165, 250, 0.2); | |
| transform: translateY(-1px); | |
| } | |
| .last-update { | |
| text-align: center; | |
| font-size: 13px; | |
| color: #94a3b8; | |
| margin-bottom: 16px; | |
| } | |
| .controls { | |
| display: flex; | |
| flex-wrap: wrap; | |
| gap: 12px; | |
| align-items: center; | |
| justify-content: center; | |
| } | |
| .search-container { | |
| flex: 1 1 auto; | |
| min-width: 300px; | |
| max-width: 600px; | |
| position: relative; | |
| } | |
| .search-container input[type="text"] { | |
| width: 100%; | |
| padding: 12px 16px; | |
| padding-right: 100px; | |
| background: rgba(15, 23, 42, 0.6); | |
| border: 2px solid rgba(148, 163, 184, 0.2); | |
| border-radius: 12px; | |
| color: #f1f5f9; | |
| font-size: 15px; | |
| font-family: inherit; | |
| transition: all 0.3s; | |
| } | |
| .search-container input[type="text"]:focus { | |
| outline: none; | |
| border-color: #60a5fa; | |
| background: rgba(15, 23, 42, 0.8); | |
| box-shadow: 0 0 0 4px rgba(96, 165, 250, 0.1); | |
| } | |
| .search-container input[type="text"]::placeholder { | |
| color: #64748b; | |
| } | |
| .clear-btn { | |
| position: absolute; | |
| right: 8px; | |
| top: 50%; | |
| transform: translateY(-50%); | |
| padding: 6px 14px; | |
| background: rgba(239, 68, 68, 0.2); | |
| border: 1px solid rgba(239, 68, 68, 0.3); | |
| border-radius: 8px; | |
| color: #fca5a5; | |
| font-size: 13px; | |
| font-weight: 500; | |
| cursor: pointer; | |
| transition: all 0.2s; | |
| } | |
| .clear-btn:hover { | |
| background: rgba(239, 68, 68, 0.3); | |
| color: #fecaca; | |
| } | |
| .mode-select { | |
| padding: 12px 40px 12px 16px; | |
| background: rgba(15, 23, 42, 0.6); | |
| border: 2px solid rgba(148, 163, 184, 0.2); | |
| border-radius: 12px; | |
| color: #f1f5f9; | |
| font-size: 15px; | |
| font-family: inherit; | |
| cursor: pointer; | |
| transition: all 0.3s; | |
| font-weight: 500; | |
| height: 48px; | |
| line-height: 20px; | |
| min-width: 150px; | |
| flex-shrink: 0; | |
| appearance: none; | |
| -webkit-appearance: none; | |
| -moz-appearance: none; | |
| 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"); | |
| background-repeat: no-repeat; | |
| background-position: right 12px center; | |
| background-size: 20px; | |
| box-shadow: none; | |
| outline: none; | |
| } | |
| .mode-select::-ms-expand { | |
| display: none; | |
| } | |
| .mode-select:focus { | |
| outline: none; | |
| border-color: #60a5fa; | |
| box-shadow: 0 0 0 4px rgba(96, 165, 250, 0.1); | |
| } | |
| .mode-select option { | |
| background: #0f172a; | |
| color: #e2e8f0; | |
| padding: 16px 20px; | |
| font-size: 15px; | |
| font-weight: 500; | |
| border: none; | |
| outline: none; | |
| } | |
| .mode-select option:hover { | |
| background: linear-gradient(90deg, rgba(96, 165, 250, 0.15) 0%, rgba(96, 165, 250, 0.05) 100%); | |
| color: #60a5fa; | |
| font-weight: 600; | |
| } | |
| .mode-select option:checked, | |
| .mode-select option:focus { | |
| background: linear-gradient(90deg, rgba(96, 165, 250, 0.25) 0%, rgba(96, 165, 250, 0.15) 100%); | |
| color: #93c5fd; | |
| font-weight: 600; | |
| box-shadow: none; | |
| outline: none; | |
| } | |
| .checkbox-group { | |
| display: flex; | |
| flex-wrap: wrap; | |
| gap: 8px; | |
| align-items: center; | |
| } | |
| .checkbox-label { | |
| display: flex; | |
| align-items: center; | |
| gap: 8px; | |
| padding: 10px 16px; | |
| background: rgba(15, 23, 42, 0.4); | |
| border: 2px solid rgba(148, 163, 184, 0.2); | |
| border-radius: 10px; | |
| cursor: pointer; | |
| transition: all 0.2s; | |
| font-size: 14px; | |
| font-weight: 500; | |
| user-select: none; | |
| } | |
| .checkbox-label:hover { | |
| background: rgba(15, 23, 42, 0.6); | |
| border-color: rgba(148, 163, 184, 0.3); | |
| } | |
| .checkbox-label input[type="checkbox"] { | |
| width: 18px; | |
| height: 18px; | |
| cursor: pointer; | |
| accent-color: #60a5fa; | |
| } | |
| .checkbox-label input[type="checkbox"]:checked + span { | |
| color: #60a5fa; | |
| } | |
| .stats-bar { | |
| display: flex; | |
| align-items: center; | |
| gap: 12px; | |
| padding: 12px 20px; | |
| background: rgba(15, 23, 42, 0.6); | |
| border-radius: 10px; | |
| font-size: 14px; | |
| font-weight: 600; | |
| flex-shrink: 0; | |
| } | |
| .stats-bar label { | |
| color: #94a3b8; | |
| } | |
| .stats-bar input { | |
| width: 70px; | |
| padding: 6px 10px; | |
| background: rgba(30, 41, 59, 0.8); | |
| border: 1px solid rgba(148, 163, 184, 0.2); | |
| border-radius: 6px; | |
| color: #60a5fa; | |
| text-align: center; | |
| font-weight: 700; | |
| font-size: 16px; | |
| } | |
| .mainContent { | |
| max-width: 1400px; | |
| margin: 0 auto; | |
| display: grid; | |
| grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); | |
| gap: 20px; | |
| padding: 0 4px; | |
| } | |
| .element { | |
| background: rgba(30, 41, 59, 0.6); | |
| backdrop-filter: blur(10px); | |
| border-radius: 16px; | |
| overflow: hidden; | |
| border: 1px solid rgba(148, 163, 184, 0.1); | |
| transition: all 0.3s; | |
| box-shadow: 0 4px 16px rgba(0, 0, 0, 0.2); | |
| display: flex; | |
| flex-direction: column; | |
| cursor: pointer; | |
| } | |
| .element:hover { | |
| transform: translateY(-4px); | |
| box-shadow: 0 12px 32px rgba(0, 0, 0, 0.4); | |
| border-color: rgba(96, 165, 250, 0.3); | |
| } | |
| .element .modelName { | |
| padding: 14px 16px; | |
| background: rgba(15, 23, 42, 0.8); | |
| font-weight: 600; | |
| font-size: 14px; | |
| text-overflow: ellipsis; | |
| overflow: hidden; | |
| white-space: nowrap; | |
| border-bottom: 1px solid rgba(148, 163, 184, 0.1); | |
| color: #e2e8f0; | |
| } | |
| .element .imageContainer { | |
| position: relative; | |
| width: 100%; | |
| padding-top: 137.5%; /* 192/264 aspect ratio */ | |
| overflow: hidden; | |
| background: rgba(15, 23, 42, 0.6); | |
| } | |
| .element .imageContainer img { | |
| position: absolute; | |
| top: 0; | |
| left: 0; | |
| width: 100%; | |
| height: 100%; | |
| object-fit: cover; | |
| transition: transform 0.3s; | |
| } | |
| .element:hover .imageContainer img { | |
| transform: scale(1.05); | |
| } | |
| .statsBox { | |
| padding: 16px; | |
| background: rgba(15, 23, 42, 0.8); | |
| font-size: 13px; | |
| line-height: 1.8; | |
| flex: 1; | |
| display: none; /* Hidden in main view */ | |
| } | |
| .statsBox .stat-row { | |
| display: flex; | |
| align-items: center; | |
| gap: 8px; | |
| margin-bottom: 6px; | |
| } | |
| .statsBox .stat-row:last-child { | |
| margin-bottom: 0; | |
| } | |
| .statsBox .stat-label { | |
| font-weight: 600; | |
| color: #94a3b8; | |
| min-width: 75px; | |
| } | |
| .statsBox .stat-value { | |
| display: flex; | |
| align-items: center; | |
| gap: 6px; | |
| } | |
| /* Modal Styles */ | |
| .modal-overlay { | |
| display: none; | |
| position: fixed; | |
| top: 0; | |
| left: 0; | |
| right: 0; | |
| bottom: 0; | |
| background: rgba(0, 0, 0, 0.8); | |
| backdrop-filter: blur(8px); | |
| z-index: 1000; | |
| align-items: center; | |
| justify-content: center; | |
| padding: 20px; | |
| animation: fadeIn 0.2s ease-out; | |
| } | |
| .modal-overlay.active { | |
| display: flex; | |
| } | |
| .modal-content { | |
| background: linear-gradient(135deg, #1e293b 0%, #0f172a 100%); | |
| border-radius: 20px; | |
| max-width: 1100px; | |
| width: 100%; | |
| max-height: 90vh; | |
| overflow: hidden; | |
| position: relative; | |
| border: 2px solid rgba(148, 163, 184, 0.2); | |
| box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5); | |
| animation: slideUp 0.3s ease-out; | |
| display: flex; | |
| flex-direction: column; | |
| } | |
| @keyframes slideUp { | |
| from { | |
| opacity: 0; | |
| transform: translateY(30px); | |
| } | |
| to { | |
| opacity: 1; | |
| transform: translateY(0); | |
| } | |
| } | |
| .modal-header { | |
| padding: 24px; | |
| border-bottom: 1px solid rgba(148, 163, 184, 0.1); | |
| display: flex; | |
| align-items: center; | |
| justify-content: space-between; | |
| position: sticky; | |
| top: 0; | |
| background: rgba(30, 41, 59, 0.95); | |
| backdrop-filter: blur(10px); | |
| z-index: 10; | |
| } | |
| .modal-title { | |
| font-size: 24px; | |
| font-weight: 700; | |
| color: #f1f5f9; | |
| margin: 0; | |
| } | |
| .modal-close { | |
| width: 36px; | |
| height: 36px; | |
| border-radius: 10px; | |
| background: rgba(239, 68, 68, 0.2); | |
| border: 1px solid rgba(239, 68, 68, 0.3); | |
| color: #fca5a5; | |
| font-size: 20px; | |
| cursor: pointer; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| transition: all 0.2s; | |
| font-weight: 700; | |
| } | |
| .modal-close:hover { | |
| background: rgba(239, 68, 68, 0.3); | |
| color: #fecaca; | |
| transform: rotate(90deg); | |
| } | |
| .modal-body { | |
| display: flex; | |
| flex: 1; | |
| overflow: hidden; | |
| gap: 0; | |
| } | |
| .modal-left { | |
| flex: 0 0 45%; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| background: rgba(15, 23, 42, 0.6); | |
| padding: 24px; | |
| } | |
| .modal-image-container { | |
| width: 100%; | |
| border-radius: 12px; | |
| overflow: hidden; | |
| box-shadow: 0 8px 24px rgba(0, 0, 0, 0.4); | |
| max-height: 100%; | |
| } | |
| .modal-image-container img { | |
| width: 100%; | |
| height: auto; | |
| display: block; | |
| } | |
| .modal-right { | |
| flex: 1; | |
| padding: 24px; | |
| overflow-y: auto; | |
| display: flex; | |
| flex-direction: column; | |
| gap: 16px; | |
| } | |
| .modal-stats { | |
| display: grid; | |
| gap: 12px; | |
| } | |
| .modal-stat-row { | |
| display: flex; | |
| align-items: center; | |
| justify-content: space-between; | |
| padding: 16px 20px; | |
| background: rgba(15, 23, 42, 0.6); | |
| border-radius: 12px; | |
| border: 1px solid rgba(148, 163, 184, 0.1); | |
| transition: all 0.2s; | |
| } | |
| .modal-stat-row:hover { | |
| background: rgba(15, 23, 42, 0.8); | |
| border-color: rgba(148, 163, 184, 0.2); | |
| } | |
| .modal-stat-label { | |
| font-weight: 600; | |
| font-size: 15px; | |
| color: #cbd5e1; | |
| } | |
| .modal-stat-value { | |
| display: flex; | |
| align-items: center; | |
| gap: 12px; | |
| } | |
| .modal-hf-link { | |
| padding: 6px 12px; | |
| background: rgba(255, 107, 0, 0.15); | |
| border: 1px solid rgba(255, 107, 0, 0.3); | |
| border-radius: 8px; | |
| font-size: 12px; | |
| font-weight: 600; | |
| color: #ff9d5c; | |
| transition: all 0.2s; | |
| text-decoration: none; | |
| display: inline-flex; | |
| align-items: center; | |
| gap: 6px; | |
| } | |
| .modal-hf-link:hover { | |
| background: rgba(255, 107, 0, 0.25); | |
| border-color: rgba(255, 107, 0, 0.5); | |
| transform: translateY(-1px); | |
| color: #ffb380; | |
| } | |
| .modal-hf-placeholder { | |
| padding: 6px 12px; | |
| background: rgba(100, 116, 139, 0.15); | |
| border: 1px solid rgba(100, 116, 139, 0.3); | |
| border-radius: 8px; | |
| font-size: 12px; | |
| font-weight: 600; | |
| color: #94a3b8; | |
| display: inline-flex; | |
| align-items: center; | |
| gap: 6px; | |
| opacity: 0.5; | |
| } | |
| .hf-logo { | |
| width: 16px; | |
| height: 16px; | |
| display: inline-block; | |
| } | |
| @media (max-width: 900px) { | |
| .modal-body { | |
| flex-direction: column; | |
| } | |
| .modal-left { | |
| flex: 0 0 auto; | |
| max-height: 400px; | |
| } | |
| .modal-right { | |
| flex: 1; | |
| } | |
| } | |
| .status-icon { | |
| font-size: 16px; | |
| display: inline-block; | |
| } | |
| .status-available { | |
| color: #4ade80; | |
| } | |
| .status-unavailable { | |
| color: #f87171; | |
| } | |
| .status-unknown { | |
| color: #fbbf24; | |
| } | |
| .hf-link { | |
| padding: 2px 8px; | |
| background: rgba(96, 165, 250, 0.1); | |
| border: 1px solid rgba(96, 165, 250, 0.2); | |
| border-radius: 6px; | |
| font-size: 11px; | |
| font-weight: 600; | |
| color: #60a5fa; | |
| transition: all 0.2s; | |
| display: inline-block; | |
| } | |
| .hf-link:hover { | |
| background: rgba(96, 165, 250, 0.2); | |
| border-color: rgba(96, 165, 250, 0.4); | |
| } | |
| @media (max-width: 768px) { | |
| .controls { | |
| flex-direction: column; | |
| align-items: stretch; | |
| } | |
| .checkbox-group { | |
| justify-content: center; | |
| } | |
| .mainContent { | |
| grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); | |
| gap: 16px; | |
| } | |
| } | |
| /* Loading animation */ | |
| @keyframes fadeIn { | |
| from { | |
| opacity: 0; | |
| transform: translateY(20px); | |
| } | |
| to { | |
| opacity: 1; | |
| transform: translateY(0); | |
| } | |
| } | |
| .element { | |
| animation: fadeIn 0.4s ease-out; | |
| } | |
| /* Scrollbar styling */ | |
| ::-webkit-scrollbar { | |
| width: 12px; | |
| } | |
| ::-webkit-scrollbar-track { | |
| background: rgba(15, 23, 42, 0.5); | |
| } | |
| ::-webkit-scrollbar-thumb { | |
| background: rgba(148, 163, 184, 0.3); | |
| border-radius: 6px; | |
| } | |
| ::-webkit-scrollbar-thumb:hover { | |
| background: rgba(148, 163, 184, 0.5); | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="header"> | |
| <h1>🎨 Mal's Models</h1> | |
| <div class="profile-links"> | |
| <a href="https://civitai.com/user/malcolmrey" target="_blank">🎨 CivitAI</a> | |
| <a href="https://huggingface.com/malcolmrey" target="_blank">🤗 HuggingFace</a> | |
| <a href="https://buymeacoffee.com/malcolmrey" target="_blank">☕ BuyMeACoffee</a> | |
| <a href="https://reddit.com/r/malcolmrey" target="_blank">💬 Reddit</a> | |
| </div> | |
| <div class="last-update">Last updated: 2025.11.02</div> | |
| <div class="controls"> | |
| <div class="search-container"> | |
| <input | |
| id="search" | |
| type="text" | |
| onkeyup="javascript:searchModels(this.value);" | |
| placeholder="Search models..." | |
| /> | |
| <button | |
| class="clear-btn" | |
| onclick="javascript:clearCurrentSearchValue(); javascript:searchModels(getCurrentSearchValue())" | |
| > | |
| Clear | |
| </button> | |
| </div> | |
| <select | |
| class="mode-select" | |
| id="searchMode" | |
| onchange="javascript:searchModels(getCurrentSearchValue());" | |
| > | |
| <option value="available">Include</option> | |
| <option value="missing">Exclude</option> | |
| </select> | |
| <div class="stats-bar"> | |
| <label>Found:</label> | |
| <input id="found" type="text" readonly /> | |
| </div> | |
| </div> | |
| <div class="controls" style="margin-top: 16px;"> | |
| <div class="checkbox-group"> | |
| <label class="checkbox-label"> | |
| <input | |
| id="selectedLora" | |
| type="checkbox" | |
| onclick="javascript:searchModels(getCurrentSearchValue());" | |
| /> | |
| <span>SD LoRA</span> | |
| </label> | |
| <label class="checkbox-label"> | |
| <input | |
| id="selectedLocon" | |
| type="checkbox" | |
| onclick="javascript:searchModels(getCurrentSearchValue());" | |
| /> | |
| <span>SD LoCon</span> | |
| </label> | |
| <label class="checkbox-label"> | |
| <input | |
| id="selectedEmbedding" | |
| type="checkbox" | |
| onclick="javascript:searchModels(getCurrentSearchValue());" | |
| /> | |
| <span>SD Embedding</span> | |
| </label> | |
| <label class="checkbox-label"> | |
| <input | |
| id="selectedFlux" | |
| type="checkbox" | |
| onclick="javascript:searchModels(getCurrentSearchValue());" | |
| /> | |
| <span>Flux</span> | |
| </label> | |
| <label class="checkbox-label"> | |
| <input | |
| id="selectedWan" | |
| type="checkbox" | |
| onclick="javascript:searchModels(getCurrentSearchValue());" | |
| /> | |
| <span>WAN</span> | |
| </label> | |
| <label class="checkbox-label"> | |
| <input | |
| id="selectedSdxl" | |
| type="checkbox" | |
| onclick="javascript:searchModels(getCurrentSearchValue());" | |
| /> | |
| <span>SDXL</span> | |
| </label> | |
| <label class="checkbox-label"> | |
| <input | |
| id="selectedQwen" | |
| type="checkbox" | |
| onclick="javascript:searchModels(getCurrentSearchValue());" | |
| /> | |
| <span>Qwen</span> | |
| </label> | |
| </div> | |
| </div> | |
| </div> | |
| <div id="mainContent" class="mainContent"></div> | |
| <!-- Modal --> | |
| <div id="modalOverlay" class="modal-overlay" onclick="closeModalOnOverlay(event)"> | |
| <div class="modal-content" onclick="event.stopPropagation()"> | |
| <div class="modal-header"> | |
| <h2 id="modalTitle" class="modal-title"></h2> | |
| <button class="modal-close" onclick="closeModal()" aria-label="Close">×</button> | |
| </div> | |
| <div class="modal-body"> | |
| <div class="modal-left"> | |
| <div class="modal-image-container"> | |
| <img id="modalImage" src="" alt="" /> | |
| </div> | |
| </div> | |
| <div class="modal-right"> | |
| <div id="modalStats" class="modal-stats"></div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <script type="text/javascript"> | |
| function yesNo(value) { | |
| if (value === undefined) { | |
| return '<span class="status-icon status-unknown">❓</span>'; | |
| } | |
| return value | |
| ? '<span class="status-icon status-available">✅</span>' | |
| : '<span class="status-icon status-unavailable">❌</span>'; | |
| } | |
| function formatHFLink(link, hasHF) { | |
| if (!link) return ''; | |
| return `<a href="${link}" target="_blank" class="hf-link">HF</a>`; | |
| } | |
| function formatModalHFLink(isAvailable, link) { | |
| if (isAvailable && link) { | |
| return `<a href="${link}" target="_blank" class="modal-hf-link">🤗 HuggingFace</a>`; | |
| } else if (isAvailable) { | |
| return `<span class="modal-hf-placeholder">🤗 HuggingFace</span>`; | |
| } | |
| return ''; | |
| } | |
| function openModal(element) { | |
| const modal = document.getElementById('modalOverlay'); | |
| const modalTitle = document.getElementById('modalTitle'); | |
| const modalImage = document.getElementById('modalImage'); | |
| const modalStats = document.getElementById('modalStats'); | |
| // Generate HuggingFace links | |
| const personName = element.key; | |
| const loconHFLink = element.locon | |
| ? `https://huggingface.co/malcolmrey/lycoris/resolve/main/locon_${personName}_v1_from_v1_64_32.safetensors` | |
| : undefined; | |
| const loraHFLink = undefined; // We don't know the hyphenation pattern for loras | |
| const embeddingHFLink = element.embedding | |
| ? `https://huggingface.co/malcolmrey/embeddings/resolve/main/${personName}-ti.safetensors` | |
| : undefined; | |
| const fluxHFLink = element.flux | |
| ? `https://huggingface.co/malcolmrey/flux/resolve/main/flux_${personName}_v1-step00000400.safetensors` | |
| : undefined; | |
| const wanHFLink = element.wan | |
| ? `https://huggingface.co/malcolmrey/wan/resolve/main/wan2.1/wan_${personName}_v1.safetensors` | |
| : undefined; | |
| modalTitle.textContent = element.key; | |
| modalImage.src = element.imageUrl ?? unknownImage; | |
| modalImage.alt = element.key; | |
| modalStats.innerHTML = ` | |
| <div class="modal-stat-row"> | |
| <span class="modal-stat-label">SD LoCon</span> | |
| <span class="modal-stat-value"> | |
| ${yesNo(element.locon)} | |
| ${formatModalHFLink(element.locon, loconHFLink)} | |
| </span> | |
| </div> | |
| <div class="modal-stat-row"> | |
| <span class="modal-stat-label">SD LoRA</span> | |
| <span class="modal-stat-value"> | |
| ${yesNo(element.lora)} | |
| ${formatModalHFLink(element.lora, loraHFLink)} | |
| </span> | |
| </div> | |
| <div class="modal-stat-row"> | |
| <span class="modal-stat-label">SD Embedding</span> | |
| <span class="modal-stat-value"> | |
| ${yesNo(element.embedding)} | |
| ${formatModalHFLink(element.embedding, embeddingHFLink)} | |
| </span> | |
| </div> | |
| <div class="modal-stat-row"> | |
| <span class="modal-stat-label">Flux</span> | |
| <span class="modal-stat-value"> | |
| ${yesNo(element.flux)} | |
| ${formatModalHFLink(element.flux, fluxHFLink)} | |
| </span> | |
| </div> | |
| <div class="modal-stat-row"> | |
| <span class="modal-stat-label">WAN</span> | |
| <span class="modal-stat-value"> | |
| ${yesNo(element.wan)} | |
| ${formatModalHFLink(element.wan, wanHFLink)} | |
| </span> | |
| </div> | |
| <div class="modal-stat-row"> | |
| <span class="modal-stat-label">SDXL</span> | |
| <span class="modal-stat-value"> | |
| ${yesNo(element.sdxl)} | |
| ${formatModalHFLink(element.sdxl, element.sdxlHFLink)} | |
| </span> | |
| </div> | |
| <div class="modal-stat-row"> | |
| <span class="modal-stat-label">Qwen</span> | |
| <span class="modal-stat-value"> | |
| ${yesNo(element.qwen)} | |
| ${formatModalHFLink(element.qwen, element.qwenHFLink)} | |
| </span> | |
| </div> | |
| `; | |
| modal.classList.add('active'); | |
| document.body.style.overflow = 'hidden'; | |
| } | |
| function closeModal() { | |
| const modal = document.getElementById('modalOverlay'); | |
| modal.classList.remove('active'); | |
| document.body.style.overflow = ''; | |
| } | |
| function closeModalOnOverlay(event) { | |
| if (event.target.id === 'modalOverlay') { | |
| closeModal(); | |
| } | |
| } | |
| // Close modal on ESC key | |
| document.addEventListener('keydown', function(event) { | |
| if (event.key === 'Escape') { | |
| closeModal(); | |
| } | |
| }); | |
| const notMatched = { | |
| lycoris: [], | |
| lora: [], | |
| embedding: [], | |
| flux: [], | |
| wan: [], | |
| sdxl: [], | |
| qwen: [], | |
| }; | |
| models.lycorises.forEach((lycoris) => { | |
| const key = prepareKey(lycoris.name); | |
| if (presence[key] !== undefined) { | |
| presence[key].loconCivitai = true; | |
| setImageUrl(key, lycoris.imageUrl); | |
| presence[key].loconCivitaiLink = lycoris.url; | |
| } else if (!isKnownSkippableKey(key)) { | |
| notMatched.lycoris.push(key); | |
| } | |
| }); | |
| models.loras.forEach((lora) => { | |
| const key = prepareKey(lora.name); | |
| if (presence[key] !== undefined) { | |
| presence[key].loraCivitai = true; | |
| setImageUrl(key, lora.imageUrl); | |
| presence[key].loraCivitaiLink = lora.url; | |
| } else if (!isKnownSkippableKey(key)) { | |
| notMatched.lora.push(key); | |
| } | |
| }); | |
| models.embeddings.forEach((embedding) => { | |
| const key = prepareKey(embedding.name); | |
| if (presence[key] !== undefined) { | |
| presence[key].embeddingCivitai = true; | |
| setImageUrl(key, embedding.imageUrl); | |
| presence[key].embeddingCivitaiLink = embedding.url; | |
| } else if (!isKnownSkippableKey(key)) { | |
| notMatched.embedding.push(key); | |
| } | |
| }); | |
| models.fluxes.forEach((flux) => { | |
| const key = prepareKey(flux.name); | |
| if (presence[key] !== undefined) { | |
| presence[key].fluxCivitai = true; | |
| setImageUrl(key, flux.imageUrl); | |
| presence[key].fluxCivitaiLink = flux.url; | |
| } else if (!isKnownSkippableKey(key)) { | |
| notMatched.flux.push(key); | |
| } | |
| }); | |
| models.wans.forEach((wan) => { | |
| const key = prepareKey(wan.name); | |
| if (presence[key] !== undefined) { | |
| presence[key].wanCivitai = true; | |
| setImageUrl(key, wan.imageUrl); | |
| presence[key].wanCivitaiLink = wan.url; | |
| } else if (!isKnownSkippableKey(key)) { | |
| notMatched.wan.push(key); | |
| } | |
| }); | |
| models.sdxls.forEach((sdxl) => { | |
| const key = prepareKey(sdxl.name); | |
| if (presence[key] !== undefined) { | |
| presence[key].sdxlCivitai = true; | |
| setImageUrl(key, sdxl.imageUrl); | |
| presence[key].sdxlCivitaiLink = sdxl.url; | |
| } else if (!isKnownSkippableKey(key)) { | |
| notMatched.sdxl.push(key); | |
| } | |
| }); | |
| models.qwens.forEach((qwen) => { | |
| const key = prepareKey(qwen.name); | |
| if (presence[key] !== undefined) { | |
| presence[key].qwenCivitai = true; | |
| setImageUrl(key, qwen.imageUrl); | |
| presence[key].qwenCivitaiLink = qwen.url; | |
| } else if (!isKnownSkippableKey(key)) { | |
| notMatched.qwen.push(key); | |
| } | |
| }); | |
| console.log(notMatched); | |
| const presenceModels = []; | |
| for (const property in presence) { | |
| const element = { | |
| key: property, | |
| locon: presence[property].locon, | |
| lora: presence[property].lora, | |
| embedding: presence[property].embedding, | |
| flux: presence[property].flux, | |
| wan: presence[property].wan, | |
| sdxl: presence[property].sdxl, | |
| qwen: presence[property].qwen, | |
| mega: undefined, | |
| imageUrl: presence[property]?.imageUrl ?? undefined, | |
| loconHFLink: presence[property]?.loconHFLink, | |
| loraHFLink: presence[property]?.loraHFLink, | |
| embeddingHFLink: presence[property]?.embeddingHFLink, | |
| fluxHFLink: presence[property]?.fluxHFLink, | |
| wanHFLink: presence[property]?.wanHFLink, | |
| sdxlHFLink: presence[property]?.sdxlHFLink, | |
| qwenHFLink: presence[property]?.qwenHFLink, | |
| }; | |
| presenceModels.push(element); | |
| } | |
| function searchModelsModern(value) { | |
| const lowerCaseValue = value.toLowerCase(); | |
| const filtered = presenceModels.filter((element) => { | |
| return ( | |
| (element.key.toLowerCase().includes(lowerCaseValue) || value === '*') && | |
| filterByType(element) | |
| ); | |
| }); | |
| document.getElementById('found').value = filtered.length; | |
| const contentDiv = document.getElementById('mainContent'); | |
| contentDiv.innerHTML = ''; | |
| filtered.forEach((element, index) => { | |
| const card = document.createElement('div'); | |
| card.className = 'element'; | |
| card.innerHTML = ` | |
| <div class="modelName" title="${escapeHtml(element.key)}">${element.key}</div> | |
| <div class="imageContainer"> | |
| <img src="${element.imageUrl ?? unknownImage}" alt="${escapeHtml(element.key)}" /> | |
| </div> | |
| `; | |
| card.addEventListener('click', () => openModal(element)); | |
| contentDiv.appendChild(card); | |
| }); | |
| } | |
| // Override the searchModels function | |
| window.searchModels = searchModelsModern; | |
| searchModels(getCurrentSearchValue()); | |
| </script> | |
| </body> | |
| </html> | |