anycoder-32b7fde1 / index.html
Keeg42069's picture
Upload folder using huggingface_hub
21e6f8e verified
Raw
History Blame Contribute Delete
19 kB
<!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</title>
<!-- Import 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: #3b82f6;
--accent-hover: #2563eb;
--border: #334155;
--success: #10b981;
--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 */
header {
background-color: rgba(30, 41, 59, 0.8);
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;
}
.brand {
display: flex;
align-items: center;
gap: 12px;
font-size: 1.25rem;
font-weight: 700;
}
.brand i {
color: var(--accent);
font-size: 1.5rem;
}
.anycoder-link {
font-size: 0.9rem;
color: var(--text-secondary);
text-decoration: none;
padding: 0.5rem 1rem;
border: 1px solid var(--border);
border-radius: 6px;
transition: all 0.3s ease;
display: flex;
align-items: center;
gap: 8px;
}
.anycoder-link:hover {
color: var(--accent);
border-color: var(--accent);
background-color: rgba(59, 130, 246, 0.1);
}
/* Main Content */
main {
flex: 1;
padding: 2rem;
max-width: 1600px;
margin: 0 auto;
width: 100%;
}
.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);
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
}
.control-group {
display: flex;
gap: 1rem;
align-items: center;
}
.status-text {
color: var(--text-secondary);
font-size: 0.95rem;
}
/* Buttons */
.btn {
padding: 0.75rem 1.5rem;
border-radius: 8px;
font-weight: 600;
cursor: pointer;
border: none;
display: inline-flex;
align-items: center;
gap: 8px;
transition: all 0.2s ease;
font-size: 0.95rem;
}
.btn-primary {
background-color: var(--accent);
color: white;
}
.btn-primary:hover {
background-color: var(--accent-hover);
transform: translateY(-1px);
}
.btn-secondary {
background-color: transparent;
border: 1px solid var(--border);
color: var(--text-primary);
}
.btn-secondary:hover {
background-color: rgba(255, 255, 255, 0.05);
border-color: var(--text-secondary);
}
.btn:disabled {
opacity: 0.6;
cursor: not-allowed;
transform: none;
}
/* Progress Bar */
.progress-container {
width: 100%;
height: 6px;
background-color: var(--bg-color);
border-radius: 3px;
overflow: hidden;
margin-top: 1rem;
display: none; /* Hidden by default */
}
.progress-bar {
height: 100%;
background-color: var(--success);
width: 0%;
transition: width 0.1s linear;
}
/* Grid Layout */
.gallery {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
gap: 1.5rem;
}
/* Card Design */
.color-card {
background-color: var(--card-bg);
border-radius: 12px;
overflow: hidden;
border: 1px solid var(--border);
transition: transform 0.2s ease, box-shadow 0.2s ease;
display: flex;
flex-direction: column;
animation: fadeIn 0.5s ease forwards;
opacity: 0;
}
@keyframes fadeIn {
to { opacity: 1; }
}
.color-card:hover {
transform: translateY(-4px);
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.3);
border-color: var(--accent);
}
.color-preview {
height: 180px;
width: 100%;
position: relative;
background-image:
linear-gradient(45deg, #ccc 25%, transparent 25%),
linear-gradient(-45deg, #ccc 25%, transparent 25%),
linear-gradient(45deg, transparent 75%, #ccc 75%),
linear-gradient(-45deg, transparent 75%, #ccc 75%);
background-size: 20px 20px;
background-position: 0 0, 0 10px, 10px -10px, -10px 0px;
}
.color-swatch {
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
}
.color-info {
padding: 1rem;
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.color-values {
font-family: 'Courier New', monospace;
font-size: 0.85rem;
color: var(--text-secondary);
}
.filename-label {
font-weight: 600;
color: var(--text-primary);
margin-bottom: 0.5rem;
font-size: 0.9rem;
}
.card-actions {
margin-top: 0.5rem;
}
.btn-sm {
padding: 0.4rem 0.8rem;
font-size: 0.8rem;
width: 100%;
justify-content: center;
}
/* Toast Notification */
.toast {
position: fixed;
bottom: 2rem;
right: 2rem;
background-color: var(--card-bg);
border: 1px solid var(--accent);
color: var(--text-primary);
padding: 1rem 1.5rem;
border-radius: 8px;
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.5);
transform: translateY(150%);
transition: transform 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
z-index: 1000;
display: flex;
align-items: center;
gap: 10px;
}
.toast.show {
transform: translateY(0);
}
.toast i {
color: var(--success);
}
/* Responsive */
@media (max-width: 768px) {
.controls {
flex-direction: column;
align-items: stretch;
}
.control-group {
flex-direction: column;
width: 100%;
}
.btn {
width: 100%;
justify-content: center;
}
.gallery {
grid-template-columns: repeat(auto-fill, minmax(100%, 1fr));
}
}
</style>
</head>
<body>
<header>
<div class="brand">
<i class="fa-solid fa-palette"></i>
<span>RGB Spectrum Gen</span>
</div>
<a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank" class="anycoder-link">
<i class="fa-solid fa-code"></i> Built with anycoder
</a>
</header>
<main>
<section class="controls">
<div class="control-group">
<div>
<h2 style="margin-bottom: 0.5rem;">Spectrum Generator</h2>
<p class="status-text" id="statusText">Ready to generate 500 unique shades.</p>
</div>
</div>
<div class="control-group">
<button id="generateBtn" class="btn btn-primary">
<i class="fa-solid fa-wand-magic-sparkles"></i> Generate Images
</button>
<button id="downloadAllBtn" class="btn btn-secondary" disabled>
<i class="fa-solid fa-download"></i> Download All
</button>
</div>
<div class="progress-container" id="progressContainer">
<div class="progress-bar" id="progressBar"></div>
</div>
</section>
<section id="gallery" class="gallery">
<!-- Images will be injected here -->
</section>
</main>
<div id="toast" class="toast">
<i class="fa-solid fa-circle-check"></i>
<span id="toastMessage">Operation successful</span>
</div>
<script>
/**
* RGB Spectrum Generator
* Uses HTML5 Canvas to generate 256x256 PNGs
*/
const CONFIG = {
count: 500,
width: 256,
height: 256,
delayBetweenDownloads: 150, // ms to prevent browser blocking
};
const elements = {
gallery: document.getElementById('gallery'),
generateBtn: document.getElementById('generateBtn'),
downloadAllBtn: document.getElementById('downloadAllBtn'),
statusText: document.getElementById('statusText'),
progressBar: document.getElementById('progressBar'),
progressContainer: document.getElementById('progressContainer'),
toast: document.getElementById('toast'),
toastMessage: document.getElementById('toastMessage')
};
// Store generated data to avoid re-rendering canvas
let generatedColors = [];
/**
* Convert HSL to RGB object
* h, s, l are in [0, 1]
* returns {r, g, b} in [0, 255]
*/
function hslToRgb(h, s, l) {
let r, g, b;
if (s === 0) {
r = g = b = l; // achromatic
} else {
const hue2rgb = (p, q, t) => {
if (t < 0) t += 1;
if (t > 1) t -= 1;
if (t < 1/6) return p + (q - p) * 6 * t;
if (t < 1/2) return q;
if (t < 2/3) return p + (q - p) * (2/3 - t) * 6;
return p;
};
const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
const p = 2 * l - q;
r = hue2rgb(p, q, h + 1/3);
g = hue2rgb(p, q, h);
b = hue2rgb(p, q, h - 1/3);
}
return {
r: Math.round(r * 255),
g: Math.round(g * 255),
b: Math.round(b * 255)
};
}
/**
* Generate Color Palette
* Creates 500 colors distributed across the spectrum with varying lightness (shades)
*/
function generateColorPalette() {
const colors = [];
for (let i = 0; i < CONFIG.count; i++) {
// Distribute Hue evenly 0 -> 1
const h = i / CONFIG.count;
// Oscillate Saturation between 0.6 and 1.0 for vibrancy
const s = 0.6 + (Math.sin(i * 0.1) * 0.2 + 0.2);
// Oscillate Lightness between 0.2 and 0.8 to get shades
// Using cosine to ensure we start and end reasonably visible
const l = 0.5 + (Math.cos(i * 0.05) * 0.3);
const rgb = hslToRgb(h, s, l);
// Create CSS string
const cssString = `rgb(${rgb.r}, ${rgb.g}, ${rgb.b})`;
// Create Filename: rgb(r,g,b).png
const filename = `rgb(${rgb.r},${rgb.g},${rgb.b}).png`;
colors.push({
rgb: rgb,
css: cssString,
filename: filename,
id: i
});
}
return colors;
}
/**
* Create a 256x256 PNG Data URL from a color string
*/
function createImageDataURL(colorCss) {
const canvas = document.createElement('canvas');
canvas.width = CONFIG.width;
canvas.height = CONFIG.height;
const ctx = canvas.getContext('2d');
// Fill background
ctx.fillStyle = colorCss;
ctx.fillRect(0, 0, CONFIG.width, CONFIG.height);
return canvas.toDataURL('image/png');
}
/**
* Render the Grid
*/
async function renderGallery() {
elements.gallery.innerHTML = '';
elements.statusText.textContent = `Generating ${CONFIG.count} images...`;
elements.progressContainer.style.display = 'block';
elements.generateBtn.disabled = true;
elements.downloadAllBtn.disabled = true;
generatedColors = generateColorPalette();
// Process in batches to keep UI responsive
const batchSize = 20;
let processed = 0;
for (let i = 0; i < generatedColors.length; i += batchSize) {
const batch = generatedColors.slice(i, i + batchSize);
const fragment = document.createDocumentFragment();
batch.forEach(color => {
const card = document.createElement('article');
card.className = 'color-card';
// Generate DataURL
const dataUrl = createImageDataURL(color.css);
card.innerHTML = `
<div class="color-preview">
<div class="color-swatch" style="background-color: ${color.css};"></div>
</div>
<div class="color-info">
<div class="filename-label">${color.filename}</div>
<div class="color-values">
R: ${color.rgb.r} &nbsp; G: ${color.rgb.g} &nbsp; B: ${color.rgb.b}
</div>
<div class="card-actions">
<button class="btn btn-secondary btn-sm" onclick="downloadSingle('${dataUrl}', '${color.filename}')">
<i class="fa-solid fa-download"></i> Download
</button>
</div>
</div>
`;
fragment.appendChild(card);
});
elements.gallery.appendChild(fragment);
// Update Progress
processed += batch.length;
const percent = (processed / CONFIG.count) * 100;
elements.progressBar.style.width = `${percent}%`;
// Allow UI to breathe
await new Promise(r => setTimeout(r, 10));
}
elements.statusText.textContent = `Generated ${CONFIG.count} images successfully.`;
elements.generateBtn.disabled = false;
elements.generateBtn.innerHTML = '<i class="fa-solid fa-rotate-right"></i> Regenerate';
elements.downloadAllBtn.disabled = false;
showToast('Images generated successfully!');
}
/**
* Trigger a single file download
*/
window.downloadSingle = function(dataUrl, filename) {
const a = document.createElement('a');
a.href = dataUrl;
a.download = filename;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
};
/**
* Download all images with a delay
*/
async function downloadAll() {
if (generatedColors.length === 0) return;
elements.downloadAllBtn.disabled = true;
elements.generateBtn.disabled = true;
elements.statusText.textContent = 'Downloading images... Please wait.';
elements.progressContainer.style.display = 'block';
elements.progressBar.style.width = '0%';
// We need to regenerate DataURLs or store them.
// For efficiency, let's just grab them from the DOM or regenerate on fly.
// Regenerating is safer to ensure we have the raw data.
for (let i = 0; i < generatedColors.length; i++) {
const color = generatedColors[i];
const dataUrl = createImageDataURL(color.css);
downloadSingle(dataUrl, color.filename);
// Update Progress
const percent = ((i + 1) / generatedColors.length) * 100;
elements.progressBar.style.width = `${percent}%`;
// Delay to prevent browser crash/blocking
await new Promise(r => setTimeout(r, CONFIG.delayBetweenDownloads));
}
elements.statusText.textContent = 'Download sequence complete.';
elements.downloadAllBtn.disabled = false;
elements.generateBtn.disabled = false;
showToast('Download sequence complete!');
};
/**
* Show Toast Notification
*/
function showToast(msg) {
elements.toastMessage.textContent = msg;
elements.toast.classList.add('show');
setTimeout(() => {
elements.toast.classList.remove('show');
}, 3000);
}
// Event Listeners
elements.generateBtn.addEventListener('click', renderGallery);
elements.downloadAllBtn.addEventListener('click', downloadAll);
</script>
</body>
</html>