cdn / index.html
vikarshana's picture
Create index.html
1e11516 verified
Raw History Blame Contribute Delete
18.8 kB
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>ClearCut</title>
<link href="https://fonts.googleapis.com/css2?family=Cormorant+Garamond:ital,wght@0,300;0,400;0,500;1,300;1,400&family=DM+Sans:wght@300;400;500&display=swap" rel="stylesheet"/>
<style>
:root {
--blush: #f9e8ec;
--petal: #f2cdd6;
--rose: #e8a5b8;
--deep-rose: #c0647e;
--mauve: #9b3a56;
--cream: #fdf6f8;
--ink: #3a1a24;
--soft-ink: #6b3a4a;
--divider: #efd4db;
--shadow: rgba(192, 100, 126, 0.12);
--glow: rgba(232, 165, 184, 0.35);
}
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
html { scroll-behavior: smooth; }
body {
background: var(--cream);
font-family: 'DM Sans', sans-serif;
color: var(--ink);
min-height: 100vh;
overflow-x: hidden;
}
/* ─── Background pattern ─── */
body::before {
content: '';
position: fixed;
inset: 0;
background:
radial-gradient(ellipse 60% 50% at 15% 20%, rgba(242,205,214,.45) 0%, transparent 60%),
radial-gradient(ellipse 50% 60% at 85% 75%, rgba(232,165,184,.30) 0%, transparent 60%),
radial-gradient(ellipse 30% 40% at 50% 50%, rgba(249,232,236,.6) 0%, transparent 70%);
pointer-events: none;
z-index: 0;
}
/* ─── Decorative petals ─── */
.petal-deco {
position: fixed;
border-radius: 50% 10% 50% 10%;
opacity: .06;
pointer-events: none;
z-index: 0;
}
.petal-deco:nth-child(1){ width:220px;height:220px;background:var(--deep-rose);top:-60px;right:80px;transform:rotate(30deg);}
.petal-deco:nth-child(2){ width:160px;height:160px;background:var(--rose);bottom:80px;left:-40px;transform:rotate(-20deg);}
.petal-deco:nth-child(3){ width:100px;height:100px;background:var(--mauve);bottom:200px;right:30px;transform:rotate(60deg);}
/* ─── Layout ─── */
.wrapper {
position: relative;
z-index: 1;
max-width: 860px;
margin: 0 auto;
padding: 0 24px 80px;
}
/* ─── Header ─── */
header {
text-align: center;
padding: 64px 0 40px;
animation: fadeDown .8s ease both;
}
.badge {
display: inline-flex;
align-items: center;
gap: 6px;
background: var(--petal);
color: var(--mauve);
font-size: .72rem;
font-weight: 500;
letter-spacing: .12em;
text-transform: uppercase;
padding: 5px 14px;
border-radius: 999px;
border: 1px solid var(--rose);
margin-bottom: 22px;
}
.badge::before { content:'✦'; font-size:.65rem; }
h1 {
font-family: 'Cormorant Garamond', serif;
font-size: clamp(2.6rem, 6vw, 4.2rem);
font-weight: 300;
line-height: 1.1;
color: var(--ink);
letter-spacing: -.01em;
}
h1 em {
font-style: italic;
color: var(--deep-rose);
}
.tagline {
margin-top: 14px;
font-size: .95rem;
color: var(--soft-ink);
font-weight: 300;
letter-spacing: .01em;
}
.credit {
margin-top: 10px;
font-size: .78rem;
color: var(--rose);
letter-spacing: .06em;
}
.credit span { color: var(--deep-rose); font-weight: 500; }
/* ─── Cards ─── */
.card {
background: rgba(255,255,255,.75);
backdrop-filter: blur(14px);
border: 1px solid var(--divider);
border-radius: 20px;
padding: 36px 40px;
box-shadow: 0 4px 30px var(--shadow);
animation: fadeUp .7s ease both;
}
.card + .card { margin-top: 20px; }
.card:nth-child(2){ animation-delay:.1s; }
.card:nth-child(3){ animation-delay:.2s; }
.card:nth-child(4){ animation-delay:.3s; }
.card-title {
font-family: 'Cormorant Garamond', serif;
font-size: 1.35rem;
font-weight: 400;
color: var(--mauve);
margin-bottom: 22px;
display: flex;
align-items: center;
gap: 10px;
}
.card-title .icon {
width: 34px; height: 34px;
background: var(--blush);
border: 1px solid var(--petal);
border-radius: 10px;
display: grid;
place-items: center;
font-size: 1rem;
}
/* ─── Drop zone ─── */
.dropzone {
border: 2px dashed var(--rose);
border-radius: 14px;
padding: 44px 20px;
text-align: center;
cursor: pointer;
transition: all .25s ease;
background: var(--blush);
position: relative;
}
.dropzone:hover, .dropzone.drag { border-color: var(--deep-rose); background: var(--petal); }
.dropzone input { position:absolute;inset:0;opacity:0;cursor:pointer; }
.drop-icon {
font-size: 2.2rem;
margin-bottom: 10px;
display: block;
}
.drop-label {
font-size: .92rem;
color: var(--soft-ink);
}
.drop-label strong { color: var(--deep-rose); }
.drop-hint {
font-size: .75rem;
color: var(--rose);
margin-top: 6px;
}
/* ─── URL input ─── */
.input-row {
display: flex;
gap: 10px;
}
input[type="text"], input[type="url"] {
flex: 1;
padding: 13px 18px;
border: 1.5px solid var(--divider);
border-radius: 12px;
font-family: 'DM Sans', sans-serif;
font-size: .9rem;
color: var(--ink);
background: var(--cream);
outline: none;
transition: border-color .2s;
}
input[type="text"]:focus, input[type="url"]:focus {
border-color: var(--rose);
box-shadow: 0 0 0 3px var(--glow);
}
input::placeholder { color: var(--rose); opacity: .7; }
/* ─── Buttons ─── */
.btn {
display: inline-flex;
align-items: center;
gap: 8px;
padding: 13px 26px;
border: none;
border-radius: 12px;
font-family: 'DM Sans', sans-serif;
font-size: .88rem;
font-weight: 500;
cursor: pointer;
transition: all .22s ease;
letter-spacing: .01em;
}
.btn-primary {
background: var(--deep-rose);
color: #fff;
box-shadow: 0 4px 16px rgba(192,100,126,.3);
}
.btn-primary:hover { background: var(--mauve); transform: translateY(-1px); box-shadow: 0 6px 20px rgba(192,100,126,.4); }
.btn-primary:active { transform: translateY(0); }
.btn-primary:disabled { opacity:.55; cursor:not-allowed; transform:none; }
.btn-soft {
background: var(--blush);
color: var(--deep-rose);
border: 1px solid var(--petal);
}
.btn-soft:hover { background: var(--petal); transform: translateY(-1px); }
/* ─── Result ─── */
#result-section { display: none; margin-top: 20px; }
.result-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 16px;
margin-bottom: 22px;
}
@media(max-width:560px){ .result-grid{ grid-template-columns:1fr; } }
.img-wrap {
border-radius: 12px;
overflow: hidden;
background: repeating-conic-gradient(#f0d8de 0% 25%, #fff 0% 50%) 0 0 / 16px 16px;
border: 1px solid var(--divider);
position: relative;
}
.img-wrap img {
width: 100%;
height: 200px;
object-fit: contain;
display: block;
}
.img-label {
position: absolute;
bottom: 0; left: 0; right: 0;
background: rgba(249,232,236,.88);
backdrop-filter: blur(6px);
text-align: center;
padding: 6px;
font-size: .72rem;
color: var(--soft-ink);
letter-spacing: .05em;
text-transform: uppercase;
}
.result-url-box {
display: flex;
align-items: center;
gap: 10px;
background: var(--blush);
border: 1px solid var(--petal);
border-radius: 12px;
padding: 12px 16px;
margin-bottom: 14px;
}
.result-url-box input {
flex: 1;
background: transparent;
border: none;
box-shadow: none;
font-size: .83rem;
color: var(--soft-ink);
padding: 0;
}
.result-url-box input:focus { box-shadow: none; }
/* ─── Status / loader ─── */
.status {
display: none;
align-items: center;
gap: 12px;
padding: 14px 18px;
background: var(--blush);
border: 1px solid var(--petal);
border-radius: 12px;
font-size: .88rem;
color: var(--soft-ink);
margin-top: 14px;
}
.status.show { display: flex; }
.status.error { background:#fff1f3; border-color:#f5b8c4; color:#8b2a3a; }
.status.success { background:#f0faf4; border-color:#a8dfc0; color:#1a6640; }
.spinner {
width: 18px; height: 18px;
border: 2px solid var(--petal);
border-top-color: var(--deep-rose);
border-radius: 50%;
animation: spin .7s linear infinite;
flex-shrink: 0;
}
/* ─── Endpoints reference ─── */
.endpoint-list {
display: flex;
flex-direction: column;
gap: 10px;
}
.ep {
display: flex;
align-items: flex-start;
gap: 12px;
padding: 14px 16px;
background: var(--blush);
border: 1px solid var(--divider);
border-radius: 12px;
transition: border-color .2s;
}
.ep:hover { border-color: var(--rose); }
.ep-method {
font-size: .7rem;
font-weight: 600;
letter-spacing: .08em;
padding: 3px 9px;
border-radius: 6px;
flex-shrink: 0;
margin-top: 1px;
}
.get { background: #e0f4ea; color: #1a6640; }
.post { background: #fde8f0; color: var(--mauve); }
.ep-path {
font-size: .83rem;
font-family: 'DM Mono', 'Courier New', monospace;
color: var(--deep-rose);
font-weight: 500;
}
.ep-desc {
font-size: .78rem;
color: var(--soft-ink);
margin-top: 2px;
}
/* ─── Footer ─── */
footer {
text-align: center;
margin-top: 56px;
padding-bottom: 20px;
font-size: .78rem;
color: var(--rose);
animation: fadeUp .8s ease .4s both;
}
footer a { color: var(--deep-rose); text-decoration: none; }
footer .heart { color: var(--deep-rose); }
/* ─── Animations ─── */
@keyframes fadeDown {
from { opacity:0; transform:translateY(-18px); }
to { opacity:1; transform:translateY(0); }
}
@keyframes fadeUp {
from { opacity:0; transform:translateY(18px); }
to { opacity:1; transform:translateY(0); }
}
@keyframes spin {
to { transform: rotate(360deg); }
}
/* ─── Scrollbar ─── */
::-webkit-scrollbar { width: 6px; }
::-webkit-scrollbar-track { background: var(--blush); }
::-webkit-scrollbar-thumb { background: var(--rose); border-radius: 4px; }
</style>
</head>
<body>
<div class="petal-deco"></div>
<div class="petal-deco"></div>
<div class="petal-deco"></div>
<div class="wrapper">
<!-- Header -->
<header>
<div class="badge">Ultra Fast Β· Precise</div>
<h1><em>ClearCut</em></h1>
<p class="tagline">Instant image processing β€” clean, crisp, flawless results.</p>
<p class="credit">Crafted with love by <span>Ayodya Vichakshana</span></p>
</header>
<!-- Upload Card -->
<div class="card" style="animation-delay:.05s">
<div class="card-title">
<span class="icon">🌸</span>
Upload an Image
</div>
<div class="dropzone" id="dropzone">
<input type="file" id="fileInput" accept="image/*"/>
<span class="drop-icon">πŸ–ΌοΈ</span>
<p class="drop-label"><strong>Click to browse</strong> or drag & drop here</p>
<p class="drop-hint">PNG, JPG, WEBP β€” up to 10 MB</p>
</div>
<div class="status" id="upload-status">
<div class="spinner"></div>
<span id="upload-msg">Processing image…</span>
</div>
</div>
<!-- URL Card -->
<div class="card">
<div class="card-title">
<span class="icon">πŸ”—</span>
Image from URL
</div>
<div class="input-row">
<input type="url" id="urlInput" placeholder="https://example.com/photo.jpg"/>
<button class="btn btn-primary" id="urlBtn">
✦ Process
</button>
</div>
<div class="status" id="url-status">
<div class="spinner"></div>
<span id="url-msg">Fetching & processing image…</span>
</div>
</div>
<!-- Result -->
<div class="card" id="result-section">
<div class="card-title">
<span class="icon">✨</span>
Result
</div>
<div class="result-grid">
<div class="img-wrap">
<img id="originalPreview" src="" alt="Original"/>
<div class="img-label">Original</div>
</div>
<div class="img-wrap">
<img id="resultPreview" src="" alt="Processed"/>
<div class="img-label">Processed</div>
</div>
</div>
<div class="result-url-box">
<input type="text" id="resultUrl" readonly placeholder="Result URL will appear here…"/>
<button class="btn btn-soft" onclick="copyUrl()">Copy</button>
</div>
<button class="btn btn-primary" id="downloadBtn">
⬇ Download PNG
</button>
</div>
<!-- API Reference -->
<div class="card">
<div class="card-title">
<span class="icon">πŸ“‘</span>
API Endpoints
</div>
<div class="endpoint-list">
<div class="ep">
<span class="ep-method get">GET</span>
<div>
<div class="ep-path">/</div>
<div class="ep-desc">Health check Β· lists available endpoints and creator info</div>
</div>
</div>
<div class="ep">
<span class="ep-method post">POST</span>
<div>
<div class="ep-path">/upload</div>
<div class="ep-desc">Upload an image file (multipart/form-data) β†’ returns JSON with result URL</div>
</div>
</div>
<div class="ep">
<span class="ep-method get">GET</span>
<div>
<div class="ep-path">/json?url=&lt;imageURL&gt;</div>
<div class="ep-desc">Fetch a remote image by URL, process it, and return the result</div>
</div>
</div>
<div class="ep">
<span class="ep-method get">GET</span>
<div>
<div class="ep-path">/file/{id}</div>
<div class="ep-desc">Retrieve a processed PNG by its temporary file ID</div>
</div>
</div>
</div>
</div>
</div><!-- /wrapper -->
<footer>
Made with <span class="heart">β™₯</span> by <strong>Ayodya Vichakshana</strong> Β·
Powered by <a href="https://jerrycoder-rembg-as.hf.space" target="_blank">HF UltraFast API</a>
</footer>
<script>
const API = 'https://jerrycoder-rembg-as.hf.space';
// ── Helpers ──────────────────────────────────────────
function setStatus(el, msgEl, type, text) {
el.className = 'status show' + (type ? ' ' + type : '');
msgEl.textContent = text;
}
function hideStatus(el) { el.className = 'status'; }
function showResult(originalSrc, resultUrl) {
document.getElementById('originalPreview').src = originalSrc;
document.getElementById('resultPreview').src = resultUrl;
document.getElementById('resultUrl').value = resultUrl;
document.getElementById('downloadBtn').onclick = () => {
const a = document.createElement('a');
a.href = resultUrl; a.download = 'clearcut-result.png'; a.click();
};
const sec = document.getElementById('result-section');
sec.style.display = 'block';
sec.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
}
function copyUrl() {
const v = document.getElementById('resultUrl').value;
if (!v) return;
navigator.clipboard.writeText(v).then(() => {
const b = event.target;
b.textContent = 'Copied!';
setTimeout(() => b.textContent = 'Copy', 1800);
});
}
// ── File Upload ───────────────────────────────────────
const dz = document.getElementById('dropzone');
const fi = document.getElementById('fileInput');
const upSt = document.getElementById('upload-status');
const upMsg = document.getElementById('upload-msg');
dz.addEventListener('dragover', e => { e.preventDefault(); dz.classList.add('drag'); });
dz.addEventListener('dragleave', () => dz.classList.remove('drag'));
dz.addEventListener('drop', e => {
e.preventDefault(); dz.classList.remove('drag');
if (e.dataTransfer.files[0]) handleFile(e.dataTransfer.files[0]);
});
fi.addEventListener('change', () => { if (fi.files[0]) handleFile(fi.files[0]); });
async function handleFile(file) {
const originalSrc = URL.createObjectURL(file);
setStatus(upSt, upMsg, '', 'Processing image…');
const fd = new FormData();
fd.append('file', file);
try {
const res = await fetch(`${API}/upload`, { method: 'POST', body: fd });
const data = await res.json();
if (data.status === 'success') {
setStatus(upSt, upMsg, 'success', 'βœ“ Image processed successfully!');
showResult(originalSrc, data.url);
} else {
setStatus(upSt, upMsg, 'error', 'βœ— ' + (data.message || 'Something went wrong.'));
}
} catch(e) {
setStatus(upSt, upMsg, 'error', 'βœ— Network error: ' + e.message);
}
}
// ── URL Processing ────────────────────────────────────
const urlInput = document.getElementById('urlInput');
const urlBtn = document.getElementById('urlBtn');
const urlSt = document.getElementById('url-status');
const urlMsg = document.getElementById('url-msg');
urlBtn.addEventListener('click', async () => {
const url = urlInput.value.trim();
if (!url) return;
urlBtn.disabled = true;
setStatus(urlSt, urlMsg, '', 'Fetching & processing image…');
try {
const res = await fetch(`${API}/json?url=${encodeURIComponent(url)}`);
const data = await res.json();
if (data.status === 'success') {
setStatus(urlSt, urlMsg, 'success', 'βœ“ Done! Image processed.');
showResult(url, data.url);
} else {
setStatus(urlSt, urlMsg, 'error', 'βœ— ' + (data.message || 'Something went wrong.'));
}
} catch(e) {
setStatus(urlSt, urlMsg, 'error', 'βœ— Network error: ' + e.message);
}
urlBtn.disabled = false;
});
urlInput.addEventListener('keydown', e => { if (e.key === 'Enter') urlBtn.click(); });
</script>
</body>
</html>