anycoder-095dab4c / index.html
Keeg42069's picture
Upload folder using huggingface_hub
047cdde verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>RGB Spectrum Generator 500</title>
<!-- Importing FontAwesome for Icons -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
:root {
--bg-color: #0f172a;
--card-bg: #1e293b;
--text-primary: #f8fafc;
--text-secondary: #94a3b8;
--accent: #38bdf8;
--accent-hover: #0ea5e9;
--border: #334155;
--success: #22c55e;
--font-family: 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: var(--font-family);
background-color: var(--bg-color);
color: var(--text-primary);
line-height: 1.6;
min-height: 100vh;
display: flex;
flex-direction: column;
}
/* Header Styling */
header {
background: rgba(15, 23, 42, 0.95);
backdrop-filter: blur(10px);
border-bottom: 1px solid var(--border);
padding: 1rem 2rem;
position: sticky;
top: 0;
z-index: 100;
display: flex;
justify-content: space-between;
align-items: center;
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
}
.brand {
display: flex;
align-items: center;
gap: 0.75rem;
font-size: 1.25rem;
font-weight: 700;
color: var(--text-primary);
}
.brand i {
color: var(--accent);
}
.anycoder-link {
font-size: 0.875rem;
color: var(--text-secondary);
text-decoration: none;
transition: color 0.3s ease;
border: 1px solid var(--border);
padding: 0.4rem 0.8rem;
border-radius: 6px;
background: rgba(255,255,255,0.05);
}
.anycoder-link:hover {
color: var(--accent);
border-color: var(--accent);
background: rgba(56, 189, 248, 0.1);
}
/* Main Content */
main {
flex: 1;
padding: 2rem;
max-width: 1600px;
margin: 0 auto;
width: 100%;
}
/* Controls Section */
.controls {
display: flex;
flex-wrap: wrap;
gap: 1rem;
margin-bottom: 2rem;
align-items: center;
justify-content: space-between;
background: var(--card-bg);
padding: 1.5rem;
border-radius: 12px;
border: 1px solid var(--border);
}
.control-group {
display: flex;
gap: 1rem;
align-items: center;
}
h1 {
font-size: 1.5rem;
margin-bottom: 0.5rem;
background: linear-gradient(to right, var(--text-primary), var(--accent));
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
p.subtitle {
color: var(--text-secondary);
font-size: 0.9rem;
}
button {
background-color: var(--accent);
color: #0f172a;
border: none;
padding: 0.75rem 1.5rem;
border-radius: 8px;
font-weight: 600;
cursor: pointer;
transition: all 0.2s ease;
display: flex;
align-items: center;
gap: 0.5rem;
font-size: 0.95rem;
}
button:hover {
background-color: var(--accent-hover);
transform: translateY(-1px);
}
button.secondary {
background-color: transparent;
color: var(--text-primary);
border: 1px solid var(--border);
}
button.secondary:hover {
background-color: rgba(255,255,255,0.05);
border-color: var(--text-secondary);
}
button:disabled {
opacity: 0.5;
cursor: not-allowed;
transform: none;
}
/* Progress Bar */
.progress-container {
width: 100%;
height: 6px;
background-color: var(--border);
border-radius: 3px;
margin-top: 1rem;
overflow: hidden;
display: none; /* Hidden by default */
}
.progress-bar {
height: 100%;
background-color: var(--success);
width: 0%;
transition: width 0.1s linear;
}
/* Grid Layout */
.grid-container {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
gap: 1.5rem;
}
/* Color Card */
.card {
background-color: var(--card-bg);
border: 1px solid var(--border);
border-radius: 12px;
overflow: hidden;
transition: transform 0.2s ease, box-shadow 0.2s ease;
position: relative;
display: flex;
flex-direction: column;
animation: fadeIn 0.4s ease-out forwards;
opacity: 0;
}
@keyframes fadeIn {
to { opacity: 1; }
}
.card:hover {
transform: translateY(-4px);
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.3);
border-color: var(--accent);
}
.card-preview {
width: 100%;
aspect-ratio: 1 / 1;
position: relative;
background-color: #000; /* Fallback */
}
.card-preview img {
width: 100%;
height: 100%;
object-fit: cover;
display: block;
}
.card-info {
padding: 1rem;
flex: 1;
display: flex;
flex-direction: column;
gap: 0.25rem;
}
.color-name {
font-weight: 700;
font-size: 1rem;
color: var(--text-primary);
margin-bottom: 0.25rem;
}
.color-values {
font-family: 'Courier New', monospace;
font-size: 0.8rem;
color: var(--text-secondary);
display: flex;
justify-content: space-between;
}
.card-actions {
margin-top: auto;
padding-top: 0.75rem;
}
.btn-download-sm {
width: 100%;
padding: 0.5rem;
font-size: 0.85rem;
justify-content: center;
background: rgba(255,255,255,0.05);
color: var(--text-primary);
border: 1px solid var(--border);
}
.btn-download-sm:hover {
background: var(--accent);
color: #0f172a;
border-color: var(--accent);
}
/* Empty State */
.empty-state {
grid-column: 1 / -1;
text-align: center;
padding: 4rem 2rem;
color: var(--text-secondary);
border: 2px dashed var(--border);
border-radius: 12px;
}
.empty-state i {
font-size: 3rem;
margin-bottom: 1rem;
opacity: 0.5;
}
/* Toast Notification */
.toast {
position: fixed;
bottom: 2rem;
right: 2rem;
background: var(--card-bg);
border: 1px solid var(--accent);
color: var(--text-primary);
padding: 1rem 1.5rem;
border-radius: 8px;
box-shadow: 0 10px 25px rgba(0,0,0,0.5);
display: flex;
align-items: center;
gap: 0.75rem;
transform: translateY(100px);
opacity: 0;
transition: all 0.3s cubic-bezier(0.68, -0.55, 0.27, 1.55);
z-index: 1000;
}
.toast.show {
transform: translateY(0);
opacity: 1;
}
.toast i {
color: var(--success);
}
/* Footer */
footer {
text-align: center;
padding: 2rem;
color: var(--text-secondary);
font-size: 0.875rem;
border-top: 1px solid var(--border);
margin-top: auto;
}
/* Responsive */
@media (max-width: 768px) {
header {
flex-direction: column;
gap: 1rem;
}
.controls {
flex-direction: column;
align-items: stretch;
}
.control-group {
flex-direction: column;
}
.grid-container {
grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
}
}
</style>
</head>
<body>
<header>
<div class="brand">
<i class="fa-solid fa-palette"></i>
<span>SpectrumGen</span>
</div>
<a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank" class="anycoder-link">
Built with anycoder <i class="fa-solid fa-arrow-up-right-from-square" style="font-size: 0.7em;"></i>
</a>
</header>
<main>
<section class="controls">
<div class="info">
<h1>RGB Color Generator</h1>
<p class="subtitle">Generate 500 unique 256x256 images covering the full spectrum.</p>
</div>
<div class="control-group">
<button id="generateBtn" onclick="startGeneration()">
<i class="fa-solid fa-wand-magic-sparkles"></i> Generate Colors
</button>
<button id="downloadAllBtn" class="secondary" onclick="downloadAll()" disabled>
<i class="fa-solid fa-download"></i> Download All (ZIP-like)
</button>
<button id="clearBtn" class="secondary" onclick="clearGrid()">
<i class="fa-solid fa-trash"></i> Clear
</button>
</div>
<div class="progress-container" id="progressContainer">
<div class="progress-bar" id="progressBar"></div>
</div>
</section>
<section id="gallery" class="grid-container">
<div class="empty-state" id="emptyState">
<i class="fa-solid fa-image"></i>
<h3>No colors generated yet</h3>
<p>Click "Generate Colors" to create the spectrum.</p>
</div>
</section>
</main>
<footer>
<p>&copy; 2023 RGB Spectrum Generator. Pure HTML/CSS/JS.</p>
</footer>
<!-- Toast Notification Element -->
<div id="toast" class="toast">
<i class="fa-solid fa-circle-check"></i>
<span id="toastMsg">Operation Successful</span>
</div>
<script>
// --- Configuration & State ---
const TOTAL_COLORS = 500;
const IMAGE_SIZE = 256;
let generatedColors = []; // Stores objects {r, g, b, name, dataUrl}
// --- Color Name Database (Condensed Standard Web Colors) ---
const colorNames = [
{r:0,g:0,b:0,name:"Black"}, {r:255,g:255,b:255,name:"White"},
{r:128,g:128,b:128,name:"Gray"}, {r:192,g:192,b:192,name:"Silver"},
{r:255,g:0,b:0,name:"Red"}, {r:128,g:0,b:0,name:"Maroon"},
{r:0,g:255,b:0,name:"Lime"}, {r:0,g:128,b:0,name:"Green"},
{r:0,g:0,b:255,name:"Blue"}, {r:0,g:0,b:128,name:"Navy"},
{r:255,g:255,b:0,name:"Yellow"}, {r:128,g:128,b:0,name:"Olive"},
{r:0,g:255,b:255,name:"Aqua"}, {r:0,g:128,b:128,name:"Teal"},
{r:255,g:0,b:255,name:"Fuchsia"}, {r:128,g:0,b:128,name:"Purple"},
{r:255,g:165,b:0,name:"Orange"}, {r:255,g:192,b:203,name:"Pink"},
{r:165,g:42,b:42,name:"Brown"}, {r:244,g:164,b:96,name:"SandyBrown"},
{r:220,g:20,b:60,name:"Crimson"}, {r:75,g:0,b:130,name:"Indigo"},
{r:238,g:130,b:238,name:"Violet"}, {r:173,g:255,b:47,name:"GreenYellow"},
{r:255,g:215,b:0,name:"Gold"}, {r:250,g:128,b:114,name:"Salmon"},
{r:128,g:0,b:0,name:"DarkRed"}, {r:0,g:100,b:0,name:"DarkGreen"},
{r:0,g:0,b:139,name:"DarkBlue"}, {r:139,g:0,b:139,name:"DarkMagenta"},
{r:184,g:134,b:11,name:"DarkGoldenRod"}, {r:169,g:169,b:169,name:"DarkGray"},
{r:0,g:139,b:139,name:"DarkCyan"}, {r:255,g:140,b:0,name:"DarkOrange"},
{r:32,g:178,b:170,name:"LightSeaGreen"}, {r:135,g:206,b:250,name:"LightSkyBlue"},
{r:119,g:136,b:153,name:"LightSlateGray"}, {r:176,g:196,b:222,name:"LightSteelBlue"},
{r:255,g:228,b:225,name:"MistyRose"}, {r:255,g:240,b:245,name:"LavenderBlush"},
{r:255,g:250,b:250,name:"Snow"}, {r:240,g:255,b:240,name:"Honeydew"},
{r:245,g:255,b:250,name:"MintCream"}, {r:240,g:248,b:255,name:"AliceBlue"},
{r:255,g:235,b:205,name:"BlanchedAlmond"}, {r:255,g:228,b:196,name:"Bisque"},
{r:255,g:255,b:224,name:"LightYellow"}, {r:250,g:250,b:210,name:"LightGoldenRodYellow"},
{r:255,g:255,b:0,name:"Yellow"}, {r:154,g:205,b:50,name:"YellowGreen"},
{r:107,g:142,b:35,name:"OliveDrab"}, {r:85,g:107,b:47,name:"DarkOliveGreen"},
{r:143,g:188,b:143,name:"DarkSeaGreen"}, {r:46,g:139,b:87,name:"SeaGreen"},
{r:60,g:179,b:113,name:"MediumSeaGreen"}, {r:3,g:168,b:158,name:"MediumTurquoise"},
{r:64,g:224,b:208,name:"Turquoise"}, {r:72,g:209,b:204,name:"MediumTurquoise"},
{r:175,g:238,b:238,name:"PaleTurquoise"}, {r:224,g:255,b:255,name:"LightCyan"},
{r:0,g:255,b:255,name:"Cyan"}, {r:0,g:206,b:209,name:"DarkTurquoise"},
{r:70,g:130,b:180,name:"SteelBlue"}, {r:100,g:149,b:237,name:"CornflowerBlue"},
{r:30,g:144,b:255,name:"DodgerBlue"}, {r:0,g:0,b:205,name:"MediumBlue"},
{r:65,g:105,b:225,name:"RoyalBlue"}, {r:0,g:191,b:255,name:"DeepSkyBlue"},
{r:135,g:206,b:235,name:"SkyBlue"}, {r:176,g:224,b:230,name:"PowderBlue"},
{r:173,g:216,b:230,name:"LightBlue"}, {r:230,g:230,b:250,name:"Lavender"},
{r:216,g:191,b:216,name:"Thistle"}, {r:221,g:160,b:221,name:"Plum"},
{r:238,g:130,b:238,name:"Violet"}, {r:255,g:0,b:255,name:"Magenta"},
{r:199,g:21,b:133,name:"MediumVioletRed"}, {r:255,g:20,b:147,name:"DeepPink"},
{r:255,g:105,b:180,name:"HotPink"}, {r:255,g:182,b:193,name:"LightPink"},
{r:255,g:192,b:203,name:"Pink"}, {r:250,g:128,b:114,name:"Salmon"},
{r:233,g:150,b:122,name:"DarkSalmon"}, {r:255,g:160,b:122,name:"LightSalmon"},
{r:255,g:127,b:80,name:"Coral"}, {r:240,g:128,b:128,name:"LightCoral"},
{r:255,g:99,b:71,name:"Tomato"}, {r:255,g:69,b:0,name:"OrangeRed"},
{r:255,g:215,b:0,name:"Gold"}, {r:218,g:165,b:32,name:"GoldenRod"},
{r:248,g:248,b:255,name:"GhostWhite"}, {r:245,g:245,b:245,name:"WhiteSmoke"},
{r:47,g:79,b:79,name:"DarkSlateGray"}, {r:112,g:128,b:144,name:"SlateGray"},
{r:25,g:25,b:112,name:"MidnightBlue"}
];
// --- Helper Functions ---
// Calculate Euclidean distance between two colors
function colorDistance(r1, g1, b1, r2, g2, b2) {
return Math.sqrt(
Math.pow(r1 - r2, 2) +
Math.pow(g1 - g2, 2) +
Math.pow(b1 - b2, 2)
);
}
// Find the closest named color
function getClosestColorName(r, g, b) {
// Check for grayscale first (simple heuristic)
if (Math.abs(r - g) < 5 && Math.abs(g - b) < 5 && Math.abs(r - b) < 5) {
if (r < 10) return "Black";
if (r > 245) return "White";
if (r < 50) return "Very Dark Gray";
if (r > 200) return "Very Light Gray";
return "Gray";
}
let minDistance = Infinity;
let closestName = "Unknown Color";
for (const color of colorNames) {
const dist = colorDistance(r, g, b, color.r, color.g, color.b);
if (dist < minDistance) {
minDistance = dist;
closestName = color.name;
}
}
return closestName.replace(/\s+/g, ''); // Remove spaces for filename
}
// Create a 256x256 canvas and return Data URL
function generateColorImage(r, g, b) {
const canvas = document.createElement('canvas');
canvas.width = IMAGE_SIZE;
canvas.height = IMAGE_SIZE;
const ctx = canvas.getContext('2d');
// Fill background
ctx.fillStyle = `rgb(${r}, ${g}, ${b})`;
ctx.fillRect(0, 0, IMAGE_SIZE, IMAGE_SIZE);
// Optional: Add a subtle border to ensure visibility if white on white background
// though the request asked for individual colors, usually raw color is preferred.
// We will stick to pure color as requested.
return canvas.toDataURL('image/png');
}
function showToast(message) {
const toast = document.getElementById('toast');
const msg = document.getElementById('toastMsg');
msg.textContent = message;
toast.classList.add('show');
setTimeout(() => {
toast.classList.remove('show');
}, 3000);
}
function downloadImage(dataUrl, filename) {
const link = document.createElement('a');
link.href = dataUrl;
link.download = filename;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}
// --- Main Logic ---
function startGeneration() {
const gallery = document.getElementById('gallery');
const emptyState = document.getElementById('emptyState');
const progressBar = document.getElementById('progressBar');
const progressContainer = document.getElementById('progressContainer');
const btn = document.getElementById('generateBtn');
// Reset UI
if (emptyState) emptyState.style.display = 'none';
gallery.innerHTML = '';
generatedColors = [];
btn.disabled = true;
btn.innerHTML = '<i class="fa-solid fa-spinner fa-spin"></i> Generating...';
progressContainer.style.display = 'block';
progressBar.style.width = '0%';
// We'll use a stepped approach to ensure full spectrum coverage (0 to 255)
// 500 is approx 8^3 (512). We will step by 32, which gives 8 steps.
// 8 * 8 * 8 = 512 colors. We will take the first 500.
const step = 32;
let count = 0;
// Using a small timeout to allow UI to render the progress bar before heavy calculation
setTimeout(() => {
const fragment = document.createDocumentFragment();
for (let r = 0; r <= 255; r += step) {
for (let g = 0; g <= 255; g += step) {
for (let b = 0; b <= 255; b += step) {
if (count >= TOTAL_COLORS) break;
const name = getClosestColorName(r, g, b);
const rgbString = `${r}-${g}-${b}`;
const dataUrl = generateColorImage(r, g, b);
const hex = "#" + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1).toUpperCase();
const colorObj = {
r, g, b, name, dataUrl, hex, rgbString
};
generatedColors.push(colorObj);
// Create Card Element
const card = document.createElement('div');
card.className = 'card';
card.innerHTML = `
<div class="card-preview" style="background-color: rgb(${r},${g},${b})">
<!-- Using img tag for actual download source, but styling via bg for speed -->
</div>
<div class="card-info">
<div class="color-name">${name}</div>
<div class="color-values">
<span>RGB(${r},${g},${b})</span>
<span>${hex}</span>
</div>
<div class="card-actions">
<button class="btn-download-sm" onclick="triggerDownload(${count})">
<i class="fa-solid fa-download"></i> Download
</button>
</div>
</div>
`;
fragment.appendChild(card);
count++;
}
if (count >= TOTAL_COLORS) break;
}
if (count >= TOTAL_COLORS) break;
}
// Append all cards at once
gallery.appendChild(fragment);
// Update UI State
progressBar.style.width = '100%';
btn.disabled = false;
btn.innerHTML = '<i class="fa-solid fa-rotate-right"></i> Regenerate';
document.getElementById('downloadAllBtn').disabled = false;
setTimeout(() => {
progressContainer.style.display = 'none';
}, 500);
showToast(`Generated ${count} colors successfully!`);
}, 100);
}
// Exposed global function for the inline onclick handler
window.triggerDownload = function(index) {
const color = generatedColors[index];
if (!color) return;
const filename = `${color.rgbString}_${color.name}.png`;
downloadImage(color.dataUrl, filename);
};
function clearGrid() {
const gallery = document.getElementById('gallery');
gallery.innerHTML = `
<div class="empty-state" id="emptyState">
<i class="fa-solid fa-image"></i>
<h3>No colors generated yet</h3>
<p>Click "Generate Colors" to create the spectrum.</p>
</div>
`;
generatedColors = [];
document.getElementById('downloadAllBtn').disabled = true;
}
function downloadAll() {
if (generatedColors.length === 0) return;
const btn = document.getElementById('downloadAllBtn');
btn.disabled = true;
btn.innerHTML = '<i class="fa-solid fa-spinner fa-spin"></i> Downloading...';
let i = 0;
const total = generatedColors.length;
function processNext() {
if (i >= total) {
btn.disabled = false;
btn.innerHTML = '<i class="fa-solid fa-download"></i> Download All (ZIP-like)';
showToast("Batch download complete!");
return;
}
const color = generatedColors[i];
const filename = `${color.rgbString}_${color.name}.png`;
downloadImage(color.dataUrl, filename);
i++;
// Update progress bar for download
const pct = Math.round((i / total) * 100);
const progressBar = document.getElementById('progressBar');
const progressContainer = document.getElementById('progressContainer');
progressContainer.style.display = 'block';
progressBar.style.width = `${pct}%`;
// Delay to prevent browser blocking (download flood protection)
setTimeout(processNext, 200);
}
processNext();
}
</script>
</body>
</html>