POCKET-Zimage-CPU / index.html
SeaWolf-AI's picture
429 auto-retry: wait in queue instead of erroring
5a4d3cf verified
Raw History Blame Contribute Delete
13.4 kB
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>POCKET-Zimage-CPU</title>
<style>
:root{
--bg:#0c0e14; --panel:#141824; --line:#232838; --ink:#e9edf5; --dim:#8a93a8;
--hot:#ff6b35; --ok:#1baf7a; --chip:#1b2030;
}
*{box-sizing:border-box}
body{margin:0;background:var(--bg);color:var(--ink);
font:15px/1.6 ui-sans-serif,system-ui,-apple-system,"Segoe UI",Roboto,"Noto Sans KR","Noto Sans SC",sans-serif}
.wrap{max-width:1180px;margin:0 auto;padding:28px 20px 64px}
header{display:flex;align-items:center;justify-content:space-between;gap:16px;flex-wrap:wrap;margin-bottom:26px}
.brand{display:flex;align-items:center;gap:12px}
.dot{width:38px;height:38px;border-radius:11px;background:linear-gradient(135deg,var(--hot),#ffb703);
display:grid;place-items:center;font-size:20px}
h1{font-size:21px;margin:0;letter-spacing:-.2px}
.sub{color:var(--dim);font-size:13px;margin-top:2px}
.langs{display:flex;gap:6px}
.langs button{background:var(--chip);color:var(--dim);border:1px solid var(--line);
padding:7px 13px;border-radius:9px;cursor:pointer;font-size:13px;font-weight:600}
.langs button.on{background:var(--hot);color:#12141c;border-color:var(--hot)}
.hero{background:linear-gradient(180deg,#161b2b,#11141f);border:1px solid var(--line);
border-radius:18px;padding:26px 24px;margin-bottom:22px}
.hero h2{margin:0 0 8px;font-size:27px;letter-spacing:-.5px;line-height:1.25}
.hero h2 b{color:var(--hot)}
.hero p{margin:0;color:var(--dim)}
.stats{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:12px;margin-top:20px}
.stat{background:#0f1320;border:1px solid var(--line);border-radius:12px;padding:13px 15px}
.stat .v{font-size:22px;font-weight:700;color:var(--hot)}
.stat .k{font-size:12px;color:var(--dim);margin-top:2px}
.grid{display:grid;grid-template-columns:1fr 1fr;gap:20px}
@media(max-width:900px){.grid{grid-template-columns:1fr}}
.card{background:var(--panel);border:1px solid var(--line);border-radius:16px;padding:20px}
label{display:block;font-size:12px;color:var(--dim);margin:0 0 7px;font-weight:600;letter-spacing:.3px}
textarea{width:100%;min-height:104px;background:#0f1320;border:1px solid var(--line);color:var(--ink);
border-radius:11px;padding:13px;font:inherit;resize:vertical}
textarea:focus{outline:none;border-color:var(--hot)}
.row{display:flex;gap:11px;flex-wrap:wrap;margin-top:14px}
.row>div{flex:1;min-width:110px}
select{width:100%;background:#0f1320;border:1px solid var(--line);color:var(--ink);
border-radius:9px;padding:10px;font:inherit}
.go{width:100%;margin-top:16px;background:var(--hot);color:#12141c;border:0;border-radius:11px;
padding:14px;font-size:15px;font-weight:800;cursor:pointer;letter-spacing:.2px}
.go:disabled{opacity:.45;cursor:not-allowed}
.ex{display:flex;gap:8px;flex-wrap:wrap;margin-top:13px}
.ex button{background:var(--chip);border:1px solid var(--line);color:var(--dim);
padding:7px 11px;border-radius:8px;cursor:pointer;font-size:12.5px}
.ex button:hover{border-color:var(--hot);color:var(--ink)}
.stage{aspect-ratio:1;background:#0b0e16;border:1px solid var(--line);border-radius:12px;
display:grid;place-items:center;overflow:hidden;position:relative}
.stage img{width:100%;height:100%;object-fit:cover;display:block}
.ph{color:#4b5468;text-align:center;padding:26px;font-size:13.5px}
.bar{position:absolute;left:0;bottom:0;height:3px;background:var(--hot);width:0;transition:width .4s}
.meta{display:flex;gap:9px;flex-wrap:wrap;margin-top:12px;font-size:12px;color:var(--dim)}
.meta span{background:var(--chip);border:1px solid var(--line);padding:5px 10px;border-radius:7px}
.note{margin-top:20px;background:#17120f;border:1px solid #3a2417;border-radius:12px;padding:15px 17px;font-size:13.5px}
.note b{color:#ffab76}
.note ul{margin:9px 0 0;padding-left:19px;color:var(--dim)}
footer{margin-top:30px;color:var(--dim);font-size:12.5px;text-align:center;line-height:1.9}
footer a{color:var(--hot);text-decoration:none}
.badge{display:inline-block;background:var(--chip);border:1px solid var(--line);
border-radius:7px;padding:4px 9px;margin:2px;font-size:12px;color:var(--dim)}
</style>
</head>
<body>
<div class="wrap">
<header>
<div class="brand">
<div class="dot">🖼️</div>
<div><h1>POCKET-Zimage-CPU</h1><div class="sub" data-i="tag"></div></div>
</div>
<div class="langs">
<button data-l="en">EN</button><button data-l="ko">한국어</button><button data-l="zh">中文</button>
</div>
</header>
<div class="hero">
<h2 data-i="h1"></h2>
<p data-i="h2"></p>
<div class="stats">
<div class="stat"><div class="v">46.4s</div><div class="k" data-i="s1"></div></div>
<div class="stat"><div class="v">6.4 GB</div><div class="k" data-i="s2"></div></div>
<div class="stat"><div class="v">0</div><div class="k" data-i="s3"></div></div>
<div class="stat"><div class="v">5.3×</div><div class="k" data-i="s4"></div></div>
</div>
</div>
<div class="grid">
<div class="card">
<label data-i="lp"></label>
<textarea id="p"></textarea>
<div class="ex" id="ex"></div>
<div class="row">
<div><label data-i="ls"></label>
<select id="st"><option value="3" selected>3</option><option value="4">4</option><option value="6">6</option></select></div>
<div><label data-i="lz"></label>
<select id="sz"><option value="384">384</option><option value="512" selected>512</option><option value="768">768</option></select></div>
</div>
<button class="go" id="go" data-i="go"></button>
<div class="meta"><span id="stt">…</span></div>
</div>
<div class="card">
<label data-i="lo"></label>
<div class="stage">
<img id="img" style="display:none" alt="">
<div class="ph" id="ph" data-i="ph"></div>
<div class="bar" id="bar"></div>
</div>
<div class="meta" id="mt"></div>
</div>
</div>
<div class="note">
<b data-i="nt"></b>
<ul><li data-i="n1"></li><li data-i="n2"></li><li data-i="n3"></li></ul>
</div>
<footer>
<div>
<a href="https://huggingface.co/FINAL-Bench/POCKET-Zimage-CPU">Model</a> ·
<a href="https://huggingface.co/collections/FINAL-Bench/pocket-models-6a618ee5d23eafb7e185a5c6">POCKET Collection</a> ·
<a href="https://huggingface.co/Tongyi-MAI/Z-Image-Turbo">Z-Image-Turbo</a> ·
<a href="https://github.com/leejet/stable-diffusion.cpp">stable-diffusion.cpp</a>
</div>
<div style="margin-top:8px">
<span class="badge">Z-Image-Turbo · Apache-2.0</span>
<span class="badge">Qwen3-4B · Apache-2.0</span>
<span class="badge">sd.cpp · MIT</span>
</div>
<div style="margin-top:8px" data-i="ft"></div>
</footer>
</div>
<script>
const T={
en:{tag:"Images on a CPU. No GPU, no CUDA, no Python.",
h1:"Photoreal images in <b>46 seconds</b> on a <b>CPU only</b>.",
h2:"Z-Image-Turbo packaged so an ordinary office PC can generate. Zero graphics cards used in every number below.",
s1:"512×512, measured",s2:"peak RAM",s3:"GPUs used",s4:"faster than defaults",
lp:"PROMPT",ls:"STEPS",lz:"SIZE",lo:"RESULT",go:"Generate",
ph:"Your image appears here.<br>CPU generation takes about a minute.",
nt:"Honest limits",
n1:"It cannot render text — words inside the image come out garbled, in every language.",
n2:"Korean prompts lose count: “a red apple” gives one in English, five or six in Korean.",
n3:"This Space runs on shared CPU hardware, so it is slower than the numbers above.",
ft:"We did not train this model. Ours is the CPU packaging, the 3-step setting, the re-quantization, and the measurements.",
wait:"Someone else is generating. Waiting for your turn…",
ex:["a red apple on a wooden table, photorealistic","a misty pine forest at sunrise, cinematic","a calico cat asleep on a windowsill, soft light","a ceramic coffee cup, studio product shot"]},
ko:{tag:"CPU만으로 이미지 생성. GPU도 CUDA도 파이썬도 필요 없습니다.",
h1:"<b>그래픽카드 없이</b> 사진급 이미지를 <b>46초</b>에.",
h2:"평범한 사무용 PC에서 돌도록 Z-Image-Turbo를 다시 묶었습니다. 아래 모든 수치는 GPU 0장으로 측정했습니다.",
s1:"512×512 실측",s2:"최대 메모리",s3:"사용한 GPU",s4:"기본 설정 대비",
lp:"프롬프트",ls:"스텝",lz:"크기",lo:"결과",go:"생성하기",
ph:"여기에 이미지가 나타납니다.<br>CPU 생성은 1분 정도 걸립니다.",
nt:"못 하는 것도 적어 둡니다",
n1:"글자를 못 그립니다 — 이미지 안의 문자는 한글이든 영어든 깨집니다.",
n2:"한국어에서 개수를 흘립니다. “빨간 사과”가 영어로는 1개, 한국어로는 대여섯 개가 나옵니다.",
n3:"이 데모는 공용 CPU에서 돌아 위 수치보다 느립니다.",
ft:"저희가 모델을 학습시킨 게 아닙니다. 저희 것은 CPU 패키징, 3스텝 설정, 재양자화, 그리고 측정값입니다.",
wait:"다른 분이 생성 중입니다. 차례를 기다립니다…",
ex:["나무 탁자 위의 빨간 사과, 사실적인 사진","해 뜰 무렵 안개 낀 소나무 숲, 영화적인","창가에서 자는 삼색 고양이, 부드러운 빛","도자기 커피잔, 스튜디오 제품 사진"]},
zh:{tag:"仅用 CPU 生成图像。无需 GPU、CUDA 或 Python。",
h1:"<b>无需显卡</b>,<b>46 秒</b>生成照片级图像。",
h2:"重新封装 Z-Image-Turbo,让普通办公电脑也能生成。下列所有数据均在 0 张 GPU 上实测。",
s1:"512×512 实测",s2:"峰值内存",s3:"使用的 GPU",s4:"比默认设置快",
lp:"提示词",ls:"步数",lz:"尺寸",lo:"结果",go:"生成",
ph:"图像将显示在这里。<br>CPU 生成大约需要一分钟。",
nt:"诚实说明局限",
n1:"无法渲染文字 — 图像内的文字在任何语言下都会乱码。",
n2:"中文/韩文提示词会丢失数量信息。",
n3:"本演示运行在共享 CPU 上,比上述数据慢。",
ft:"我们没有训练该模型。我们做的是 CPU 封装、3 步设置、重新量化以及这些实测数据。",
wait:"其他人正在生成。正在等待您的回合…",
ex:["木桌上的红苹果,照片级真实","日出时分雾气弥漫的松林,电影感","窗台上熟睡的三花猫,柔和光线","陶瓷咖啡杯,棚拍产品图"]}
};
let L="en";
function pickLang(){
const n=(navigator.languages||[navigator.language||"en"]).map(s=>s.toLowerCase());
for(const s of n){ if(s.startsWith("ko"))return"ko"; if(s.startsWith("zh"))return"zh"; if(s.startsWith("en"))return"en"; }
return "en";
}
function apply(l){
L=l; const t=T[l];
document.documentElement.lang=l;
document.querySelectorAll("[data-i]").forEach(el=>{el.innerHTML=t[el.dataset.i]||"";});
document.querySelectorAll(".langs button").forEach(b=>b.classList.toggle("on",b.dataset.l===l));
const ex=document.getElementById("ex"); ex.innerHTML="";
t.ex.forEach(s=>{const b=document.createElement("button");b.textContent=s.length>28?s.slice(0,28)+"…":s;
b.title=s;b.onclick=()=>{document.getElementById("p").value=s;};ex.appendChild(b);});
if(!document.getElementById("p").value) document.getElementById("p").value=t.ex[0];
try{localStorage.setItem("pz_lang",l)}catch(e){}
}
document.querySelectorAll(".langs button").forEach(b=>b.onclick=()=>apply(b.dataset.l));
let saved=null; try{saved=localStorage.getItem("pz_lang")}catch(e){}
apply(saved||pickLang());
async function poll(){
try{const r=await fetch("/api/status");const s=await r.json();
document.getElementById("stt").textContent=(s.ready?"● ready":"○ "+s.msg)+" · "+s.threads+" threads · CPU only";
if(!s.ready)setTimeout(poll,2500);
}catch(e){setTimeout(poll,4000);}
}
poll();
document.getElementById("go").onclick=async()=>{
const go=document.getElementById("go"),bar=document.getElementById("bar"),
img=document.getElementById("img"),ph=document.getElementById("ph"),mt=document.getElementById("mt");
const body={prompt:document.getElementById("p").value.trim(),
steps:+document.getElementById("st").value,size:+document.getElementById("sz").value,seed:-1};
if(!body.prompt)return;
go.disabled=true; mt.innerHTML=""; img.style.display="none"; ph.style.display="block";
let w=2; bar.style.width="2%";
const tk=setInterval(()=>{w=Math.min(93,w+1.6);bar.style.width=w+"%";},900);
try{
let r,tries=0;
while(true){
r=await fetch("/api/generate",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(body)});
if(r.status!==429) break;
tries++;
if(tries>40) throw new Error("timeout waiting for a free slot");
mt.innerHTML=`<span>⏳ ${T[L].wait} (${tries})</span>`;
await new Promise(s=>setTimeout(s,8000));
}
if(!r.ok){throw new Error((await r.text()).slice(0,180));}
const d=await r.json();
img.src=d.image; img.style.display="block"; ph.style.display="none";
mt.innerHTML=`<span>${d.seconds}s</span><span>${d.size}×${d.size}</span><span>${d.steps} steps</span><span>seed ${d.seed}</span><span>CPU only</span>`;
}catch(e){ mt.innerHTML=`<span style="color:#ff8a65">${e.message}</span>`; }
finally{ clearInterval(tk); bar.style.width="100%"; setTimeout(()=>bar.style.width="0",900); go.disabled=false; }
};
</script>
</body>
</html>