Spaces:
Running
Running
A deeply integrated CivitAI client that accepts user supplied CivitAI keys and quick check-box age verification to unlock the vast and diverse world of different base modes
Browse files- README.md +8 -5
- components/footer.js +94 -0
- components/header.js +106 -0
- index.html +57 -19
- script.js +99 -0
- style.css +40 -19
README.md
CHANGED
|
@@ -1,10 +1,13 @@
|
|
| 1 |
---
|
| 2 |
-
title:
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
|
| 6 |
sdk: static
|
| 7 |
pinned: false
|
|
|
|
|
|
|
| 8 |
---
|
| 9 |
|
| 10 |
-
|
|
|
|
|
|
| 1 |
---
|
| 2 |
+
title: CivitAI Explorer π
|
| 3 |
+
colorFrom: purple
|
| 4 |
+
colorTo: pink
|
| 5 |
+
emoji: π³
|
| 6 |
sdk: static
|
| 7 |
pinned: false
|
| 8 |
+
tags:
|
| 9 |
+
- deepsite-v3
|
| 10 |
---
|
| 11 |
|
| 12 |
+
# Welcome to your new DeepSite project!
|
| 13 |
+
This project was created with [DeepSite](https://huggingface.co/deepsite).
|
components/footer.js
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
class CustomFooter extends HTMLElement {
|
| 2 |
+
connectedCallback() {
|
| 3 |
+
this.attachShadow({ mode: 'open' });
|
| 4 |
+
this.shadowRoot.innerHTML = `
|
| 5 |
+
<style>
|
| 6 |
+
:host {
|
| 7 |
+
display: block;
|
| 8 |
+
margin-top: 4rem;
|
| 9 |
+
}
|
| 10 |
+
|
| 11 |
+
footer {
|
| 12 |
+
background-color: #111827;
|
| 13 |
+
border-top: 1px solid #374151;
|
| 14 |
+
padding: 2rem 0;
|
| 15 |
+
}
|
| 16 |
+
|
| 17 |
+
.container {
|
| 18 |
+
max-width: 1200px;
|
| 19 |
+
margin: 0 auto;
|
| 20 |
+
padding: 0 1rem;
|
| 21 |
+
}
|
| 22 |
+
|
| 23 |
+
.footer-content {
|
| 24 |
+
display: flex;
|
| 25 |
+
flex-direction: column;
|
| 26 |
+
align-items: center;
|
| 27 |
+
text-align: center;
|
| 28 |
+
}
|
| 29 |
+
|
| 30 |
+
.social-links {
|
| 31 |
+
display: flex;
|
| 32 |
+
margin: 1rem 0;
|
| 33 |
+
}
|
| 34 |
+
|
| 35 |
+
.social-link {
|
| 36 |
+
margin: 0 0.5rem;
|
| 37 |
+
color: #d1d5db;
|
| 38 |
+
transition: color 0.3s;
|
| 39 |
+
}
|
| 40 |
+
|
| 41 |
+
.social-link:hover {
|
| 42 |
+
color: #10b981;
|
| 43 |
+
}
|
| 44 |
+
|
| 45 |
+
.copyright {
|
| 46 |
+
color: #9ca3af;
|
| 47 |
+
font-size: 0.875rem;
|
| 48 |
+
margin-top: 1rem;
|
| 49 |
+
}
|
| 50 |
+
</style>
|
| 51 |
+
|
| 52 |
+
<footer>
|
| 53 |
+
<div class="container">
|
| 54 |
+
<div class="footer-content">
|
| 55 |
+
<div class="logo">
|
| 56 |
+
<i data-feather="box" style="color: #10b981;"></i>
|
| 57 |
+
<span style="margin-left: 0.5rem; font-weight: bold;">CivitAI Explorer</span>
|
| 58 |
+
</div>
|
| 59 |
+
|
| 60 |
+
<div class="social-links">
|
| 61 |
+
<a href="#" class="social-link">
|
| 62 |
+
<i data-feather="github"></i>
|
| 63 |
+
</a>
|
| 64 |
+
<a href="#" class="social-link">
|
| 65 |
+
<i data-feather="twitter"></i>
|
| 66 |
+
</a>
|
| 67 |
+
<a href="#" class="social-link">
|
| 68 |
+
<i data-feather="discord"></i>
|
| 69 |
+
</a>
|
| 70 |
+
</div>
|
| 71 |
+
|
| 72 |
+
<p class="copyright">
|
| 73 |
+
© ${new Date().getFullYear()} CivitAI Explorer. This is an unofficial client. All models belong to their respective creators.
|
| 74 |
+
</p>
|
| 75 |
+
</div>
|
| 76 |
+
</div>
|
| 77 |
+
</footer>
|
| 78 |
+
`;
|
| 79 |
+
|
| 80 |
+
// Initialize feather icons
|
| 81 |
+
setTimeout(() => {
|
| 82 |
+
const featherScript = document.createElement('script');
|
| 83 |
+
featherScript.src = "https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js";
|
| 84 |
+
featherScript.onload = () => {
|
| 85 |
+
if (this.shadowRoot) {
|
| 86 |
+
feather.replace({}, this.shadowRoot);
|
| 87 |
+
}
|
| 88 |
+
};
|
| 89 |
+
this.shadowRoot.appendChild(featherScript);
|
| 90 |
+
}, 0);
|
| 91 |
+
}
|
| 92 |
+
}
|
| 93 |
+
|
| 94 |
+
customElements.define('custom-footer', CustomFooter);
|
components/header.js
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
class CustomHeader extends HTMLElement {
|
| 2 |
+
connectedCallback() {
|
| 3 |
+
this.attachShadow({ mode: 'open' });
|
| 4 |
+
this.shadowRoot.innerHTML = `
|
| 5 |
+
<style>
|
| 6 |
+
:host {
|
| 7 |
+
display: block;
|
| 8 |
+
}
|
| 9 |
+
|
| 10 |
+
header {
|
| 11 |
+
background-color: #111827;
|
| 12 |
+
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
| 13 |
+
}
|
| 14 |
+
|
| 15 |
+
.container {
|
| 16 |
+
max-width: 1200px;
|
| 17 |
+
margin: 0 auto;
|
| 18 |
+
padding: 0 1rem;
|
| 19 |
+
}
|
| 20 |
+
|
| 21 |
+
nav {
|
| 22 |
+
display: flex;
|
| 23 |
+
justify-content: space-between;
|
| 24 |
+
align-items: center;
|
| 25 |
+
padding: 1rem 0;
|
| 26 |
+
}
|
| 27 |
+
|
| 28 |
+
.logo {
|
| 29 |
+
display: flex;
|
| 30 |
+
align-items: center;
|
| 31 |
+
font-weight: bold;
|
| 32 |
+
font-size: 1.5rem;
|
| 33 |
+
color: #10b981;
|
| 34 |
+
}
|
| 35 |
+
|
| 36 |
+
.logo i {
|
| 37 |
+
margin-right: 0.5rem;
|
| 38 |
+
}
|
| 39 |
+
|
| 40 |
+
ul {
|
| 41 |
+
display: flex;
|
| 42 |
+
list-style: none;
|
| 43 |
+
}
|
| 44 |
+
|
| 45 |
+
li {
|
| 46 |
+
margin-left: 2rem;
|
| 47 |
+
}
|
| 48 |
+
|
| 49 |
+
a {
|
| 50 |
+
color: #d1d5db;
|
| 51 |
+
text-decoration: none;
|
| 52 |
+
transition: color 0.3s;
|
| 53 |
+
}
|
| 54 |
+
|
| 55 |
+
a:hover {
|
| 56 |
+
color: #10b981;
|
| 57 |
+
}
|
| 58 |
+
|
| 59 |
+
@media (max-width: 768px) {
|
| 60 |
+
nav {
|
| 61 |
+
flex-direction: column;
|
| 62 |
+
}
|
| 63 |
+
|
| 64 |
+
ul {
|
| 65 |
+
margin-top: 1rem;
|
| 66 |
+
}
|
| 67 |
+
|
| 68 |
+
li {
|
| 69 |
+
margin: 0 1rem;
|
| 70 |
+
}
|
| 71 |
+
}
|
| 72 |
+
</style>
|
| 73 |
+
|
| 74 |
+
<header>
|
| 75 |
+
<div class="container">
|
| 76 |
+
<nav>
|
| 77 |
+
<a href="/" class="logo">
|
| 78 |
+
<i data-feather="box"></i>
|
| 79 |
+
<span>CivitAI Explorer</span>
|
| 80 |
+
</a>
|
| 81 |
+
<ul>
|
| 82 |
+
<li><a href="/">Home</a></li>
|
| 83 |
+
<li><a href="#">Models</a></li>
|
| 84 |
+
<li><a href="#">Collections</a></li>
|
| 85 |
+
<li><a href="#">About</a></li>
|
| 86 |
+
</ul>
|
| 87 |
+
</nav>
|
| 88 |
+
</div>
|
| 89 |
+
</header>
|
| 90 |
+
`;
|
| 91 |
+
|
| 92 |
+
// Initialize feather icons after a short delay to ensure DOM is ready
|
| 93 |
+
setTimeout(() => {
|
| 94 |
+
const featherScript = document.createElement('script');
|
| 95 |
+
featherScript.src = "https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js";
|
| 96 |
+
featherScript.onload = () => {
|
| 97 |
+
if (this.shadowRoot) {
|
| 98 |
+
feather.replace({}, this.shadowRoot);
|
| 99 |
+
}
|
| 100 |
+
};
|
| 101 |
+
this.shadowRoot.appendChild(featherScript);
|
| 102 |
+
}, 0);
|
| 103 |
+
}
|
| 104 |
+
}
|
| 105 |
+
|
| 106 |
+
customElements.define('custom-header', CustomHeader);
|
index.html
CHANGED
|
@@ -1,19 +1,57 @@
|
|
| 1 |
-
<!
|
| 2 |
-
<html>
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
<
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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>CivitAI Explorer</title>
|
| 7 |
+
<link rel="icon" type="image/x-icon" href="/static/favicon.ico">
|
| 8 |
+
<link rel="stylesheet" href="style.css">
|
| 9 |
+
<script src="https://cdn.tailwindcss.com"></script>
|
| 10 |
+
<script src="https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js"></script>
|
| 11 |
+
<script src="https://unpkg.com/feather-icons"></script>
|
| 12 |
+
</head>
|
| 13 |
+
<body class="bg-gray-900 text-white">
|
| 14 |
+
<custom-header></custom-header>
|
| 15 |
+
|
| 16 |
+
<main class="container mx-auto px-4 py-8">
|
| 17 |
+
<section class="mb-12">
|
| 18 |
+
<h1 class="text-4xl font-bold mb-6 text-center">Unlock the World of AI Models</h1>
|
| 19 |
+
<p class="text-lg text-center max-w-2xl mx-auto mb-8">
|
| 20 |
+
Access thousands of AI models from CivitAI with our secure explorer.
|
| 21 |
+
Simply provide your API key and verify your age to get started.
|
| 22 |
+
</p>
|
| 23 |
+
|
| 24 |
+
<div class="max-w-md mx-auto bg-gray-800 rounded-xl shadow-md overflow-hidden p-6">
|
| 25 |
+
<div class="mb-4">
|
| 26 |
+
<label for="apiKey" class="block text-sm font-medium mb-2">CivitAI API Key</label>
|
| 27 |
+
<input type="password" id="apiKey" class="w-full px-3 py-2 bg-gray-700 border border-gray-600 rounded-md focus:outline-none focus:ring-2 focus:ring-green-500" placeholder="Enter your API key">
|
| 28 |
+
</div>
|
| 29 |
+
|
| 30 |
+
<div class="flex items-center mb-6">
|
| 31 |
+
<input id="ageVerification" type="checkbox" class="w-4 h-4 text-green-500 bg-gray-700 border-gray-600 rounded focus:ring-green-500">
|
| 32 |
+
<label for="ageVerification" class="ml-2 text-sm">I am 18+ years old</label>
|
| 33 |
+
</div>
|
| 34 |
+
|
| 35 |
+
<button id="unlockBtn" class="w-full bg-green-500 hover:bg-green-600 text-white font-bold py-2 px-4 rounded transition duration-300">
|
| 36 |
+
Unlock Models
|
| 37 |
+
</button>
|
| 38 |
+
</div>
|
| 39 |
+
</section>
|
| 40 |
+
|
| 41 |
+
<section id="modelsSection" class="hidden">
|
| 42 |
+
<h2 class="text-3xl font-bold mb-6">Featured Models</h2>
|
| 43 |
+
<div id="modelsContainer" class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
| 44 |
+
<!-- Model cards will be populated here -->
|
| 45 |
+
</div>
|
| 46 |
+
</section>
|
| 47 |
+
</main>
|
| 48 |
+
|
| 49 |
+
<custom-footer></custom-footer>
|
| 50 |
+
|
| 51 |
+
<script src="components/header.js"></script>
|
| 52 |
+
<script src="components/footer.js"></script>
|
| 53 |
+
<script src="script.js"></script>
|
| 54 |
+
<script>feather.replace();</script>
|
| 55 |
+
<script src="https://huggingface.co/deepsite/deepsite-badge.js"></script>
|
| 56 |
+
</body>
|
| 57 |
+
</html>
|
script.js
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
document.addEventListener('DOMContentLoaded', function() {
|
| 2 |
+
const apiKeyInput = document.getElementById('apiKey');
|
| 3 |
+
const ageVerification = document.getElementById('ageVerification');
|
| 4 |
+
const unlockBtn = document.getElementById('unlockBtn');
|
| 5 |
+
const modelsSection = document.getElementById('modelsSection');
|
| 6 |
+
const modelsContainer = document.getElementById('modelsContainer');
|
| 7 |
+
|
| 8 |
+
// Check if API key is stored in localStorage
|
| 9 |
+
const storedApiKey = localStorage.getItem('civitaiApiKey');
|
| 10 |
+
if (storedApiKey) {
|
| 11 |
+
apiKeyInput.value = storedApiKey;
|
| 12 |
+
}
|
| 13 |
+
|
| 14 |
+
unlockBtn.addEventListener('click', async function() {
|
| 15 |
+
const apiKey = apiKeyInput.value.trim();
|
| 16 |
+
const isVerified = ageVerification.checked;
|
| 17 |
+
|
| 18 |
+
if (!apiKey) {
|
| 19 |
+
alert('Please enter your CivitAI API key');
|
| 20 |
+
return;
|
| 21 |
+
}
|
| 22 |
+
|
| 23 |
+
if (!isVerified) {
|
| 24 |
+
alert('Please verify your age to proceed');
|
| 25 |
+
return;
|
| 26 |
+
}
|
| 27 |
+
|
| 28 |
+
// Store API key in localStorage
|
| 29 |
+
localStorage.setItem('civitaiApiKey', apiKey);
|
| 30 |
+
|
| 31 |
+
// Show loading state
|
| 32 |
+
unlockBtn.innerHTML = '<svg class="animate-spin -ml-1 mr-2 h-4 w-4 text-white inline" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle><path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path></svg> Loading...';
|
| 33 |
+
unlockBtn.disabled = true;
|
| 34 |
+
|
| 35 |
+
try {
|
| 36 |
+
// Fetch models from CivitAI API
|
| 37 |
+
const response = await fetch('https://civitai.com/api/v1/models?limit=6', {
|
| 38 |
+
headers: {
|
| 39 |
+
'Authorization': `Bearer ${apiKey}`
|
| 40 |
+
}
|
| 41 |
+
});
|
| 42 |
+
|
| 43 |
+
if (!response.ok) {
|
| 44 |
+
throw new Error('Invalid API key or network error');
|
| 45 |
+
}
|
| 46 |
+
|
| 47 |
+
const data = await response.json();
|
| 48 |
+
displayModels(data.items);
|
| 49 |
+
|
| 50 |
+
// Show models section
|
| 51 |
+
modelsSection.classList.remove('hidden');
|
| 52 |
+
|
| 53 |
+
// Scroll to models section
|
| 54 |
+
modelsSection.scrollIntoView({ behavior: 'smooth' });
|
| 55 |
+
} catch (error) {
|
| 56 |
+
alert('Error: ' + error.message);
|
| 57 |
+
} finally {
|
| 58 |
+
// Reset button
|
| 59 |
+
unlockBtn.textContent = 'Unlock Models';
|
| 60 |
+
unlockBtn.disabled = false;
|
| 61 |
+
}
|
| 62 |
+
});
|
| 63 |
+
|
| 64 |
+
function displayModels(models) {
|
| 65 |
+
modelsContainer.innerHTML = '';
|
| 66 |
+
|
| 67 |
+
models.forEach(model => {
|
| 68 |
+
const modelCard = document.createElement('div');
|
| 69 |
+
modelCard.className = 'model-card';
|
| 70 |
+
|
| 71 |
+
// Get first image or use placeholder
|
| 72 |
+
const imageUrl = model.images && model.images.length > 0 ?
|
| 73 |
+
model.images[0].url :
|
| 74 |
+
`http://static.photos/technology/320x240/${model.id}`;
|
| 75 |
+
|
| 76 |
+
modelCard.innerHTML = `
|
| 77 |
+
<img src="${imageUrl}" alt="${model.name}" class="model-image">
|
| 78 |
+
<div class="p-4">
|
| 79 |
+
<h3 class="font-bold text-lg mb-2">${model.name}</h3>
|
| 80 |
+
<p class="text-gray-400 text-sm mb-3 line-clamp-2">${model.description || 'No description available'}</p>
|
| 81 |
+
<div class="model-stats">
|
| 82 |
+
<i data-feather="download"></i>
|
| 83 |
+
<span>${model.downloads}</span>
|
| 84 |
+
<i data-feather="heart" class="ml-3"></i>
|
| 85 |
+
<span>${model.favorites}</span>
|
| 86 |
+
</div>
|
| 87 |
+
<div class="mt-3">
|
| 88 |
+
<span class="inline-block bg-green-500 text-xs px-2 py-1 rounded">${model.type}</span>
|
| 89 |
+
</div>
|
| 90 |
+
</div>
|
| 91 |
+
`;
|
| 92 |
+
|
| 93 |
+
modelsContainer.appendChild(modelCard);
|
| 94 |
+
});
|
| 95 |
+
|
| 96 |
+
// Reinitialize feather icons
|
| 97 |
+
feather.replace();
|
| 98 |
+
}
|
| 99 |
+
});
|
style.css
CHANGED
|
@@ -1,28 +1,49 @@
|
|
| 1 |
-
|
| 2 |
-
|
| 3 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4 |
}
|
| 5 |
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
margin-top: 0;
|
| 9 |
}
|
| 10 |
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 16 |
}
|
| 17 |
|
| 18 |
-
.
|
| 19 |
-
|
| 20 |
-
margin: 0 auto;
|
| 21 |
-
padding: 16px;
|
| 22 |
-
border: 1px solid lightgray;
|
| 23 |
-
border-radius: 16px;
|
| 24 |
}
|
| 25 |
|
| 26 |
-
|
| 27 |
-
|
|
|
|
| 28 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
@tailwind base;
|
| 2 |
+
@tailwind components;
|
| 3 |
+
@tailwind utilities;
|
| 4 |
+
|
| 5 |
+
/* Custom styles for model cards */
|
| 6 |
+
.model-card {
|
| 7 |
+
@apply bg-gray-800 rounded-xl shadow-lg overflow-hidden transition-transform duration-300 hover:scale-105;
|
| 8 |
+
}
|
| 9 |
+
|
| 10 |
+
.model-image {
|
| 11 |
+
@apply w-full h-48 object-cover;
|
| 12 |
+
}
|
| 13 |
+
|
| 14 |
+
.model-stats {
|
| 15 |
+
@apply flex items-center text-sm text-gray-400 mt-2;
|
| 16 |
}
|
| 17 |
|
| 18 |
+
.model-stats svg {
|
| 19 |
+
@apply mr-1;
|
|
|
|
| 20 |
}
|
| 21 |
|
| 22 |
+
/* Animation for loading spinner */
|
| 23 |
+
@keyframes spin {
|
| 24 |
+
to {
|
| 25 |
+
transform: rotate(360deg);
|
| 26 |
+
}
|
| 27 |
}
|
| 28 |
|
| 29 |
+
.animate-spin {
|
| 30 |
+
animation: spin 1s linear infinite;
|
|
|
|
|
|
|
|
|
|
|
|
|
| 31 |
}
|
| 32 |
|
| 33 |
+
/* Custom scrollbar */
|
| 34 |
+
::-webkit-scrollbar {
|
| 35 |
+
width: 8px;
|
| 36 |
}
|
| 37 |
+
|
| 38 |
+
::-webkit-scrollbar-track {
|
| 39 |
+
background: #1f2937;
|
| 40 |
+
}
|
| 41 |
+
|
| 42 |
+
::-webkit-scrollbar-thumb {
|
| 43 |
+
background: #4b5563;
|
| 44 |
+
border-radius: 4px;
|
| 45 |
+
}
|
| 46 |
+
|
| 47 |
+
::-webkit-scrollbar-thumb:hover {
|
| 48 |
+
background: #6b7280;
|
| 49 |
+
}
|