Spaces:
Running
Running
| <html lang="zh-CN"> | |
| <head> | |
| <meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1.0"> | |
| <title>图提词</title> | |
| <style> | |
| *{margin:0;padding:0;box-sizing:border-box} | |
| body{font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;background:#0f172a;color:#e2e8f0;display:flex;justify-content:center;padding:2rem;min-height:100vh} | |
| .c{max-width:800px;width:100%} | |
| h1{font-size:1.8rem;text-align:center;margin-bottom:.3rem} | |
| .st{text-align:center;color:#94a3b8;margin-bottom:1.2rem;font-size:.9rem} | |
| .quota{text-align:center;color:#64748b;font-size:.75rem;margin-bottom:1rem} | |
| .quota b{color:#60a5fa} | |
| textarea{width:100%;padding:.75rem;border-radius:10px;border:1px solid #334155;background:#1e293b;color:#e2e8f0;font-size:.95rem;min-height:200px;resize:vertical;font-family:inherit;margin-bottom:.3rem} | |
| textarea:focus{outline:none;border-color:#60a5fa} | |
| .note{color:#64748b;font-size:.75rem;margin-bottom:1rem;text-align:right} | |
| .btn{background:linear-gradient(135deg,#3b82f6,#8b5cf6);color:#fff;padding:.85rem 2rem;border:none;border-radius:10px;font-size:1rem;cursor:pointer;width:100%;font-weight:600} | |
| .btn:disabled{opacity:.4;cursor:not-allowed} | |
| .result{background:#1e293b;border-radius:12px;padding:1.5rem;margin-top:1.5rem;display:none} | |
| .result.show{display:block} | |
| .result pre{white-space:pre-wrap;font-family:inherit;font-size:.9rem;line-height:1.7;overflow-x:auto} | |
| .actions{margin-top:1rem;display:flex;gap:.5rem} | |
| .btn-sm{padding:.5rem 1rem;border-radius:6px;border:1px solid #475569;background:#334155;color:#e2e8f0;cursor:pointer;font-size:.85rem} | |
| .loading{text-align:center;padding:2rem;display:none} | |
| .loading.show{display:block} | |
| .spinner{width:36px;height:36px;border:3px solid #334155;border-top-color:#60a5fa;border-radius:50%;animation:spin .8s linear infinite;margin:0 auto 1rem} | |
| @keyframes spin{to{transform:rotate(360deg)}} | |
| .error{background:#7f1d1d;border:1px solid #ef4444;border-radius:10px;padding:1rem;margin-top:1rem;display:none} | |
| .error.show{display:block} | |
| </style> | |
| </head> | |
| <body> | |
| <div class="c"> | |
| <h1>图提词</h1> | |
| <p class="st">粘贴文案,自动生成图片提示词</p> | |
| <p class="quota" id="quota">加载中...</p> | |
| <textarea id="inputText" placeholder="在此粘贴纯文字文案..."></textarea> | |
| <p class="note" id="stats"></p> | |
| <button class="btn" id="submitBtn" onclick="go()">生成图片提示词</button> | |
| <div class="loading" id="loading"><div class="spinner"></div><p>正在分析文案并生成提示词...</p></div> | |
| <div class="error" id="error"></div> | |
| <div class="result" id="result"><pre id="rc"></pre> | |
| <div class="actions"><button class="btn-sm" onclick="navigator.clipboard.writeText(document.getElementById('rc').textContent).then(()=>alert('已复制'))">复制</button><button class="btn-sm" onclick="dl()">下载 .md</button></div></div> | |
| </div> | |
| <script> | |
| var API_KEY = ''; | |
| var API_URL = 'https://image-prompt-proxy.willian166.workers.dev'; | |
| var MODEL = 'THUDM/GLM-Z1-9B-0414'; | |
| var LIMIT = 10; | |
| var SYS="你是「图提词」,从纯文字文案生成图片提示词。\n\n流程:\n1.提取纯文本,中文<10字回复[文案过短]\n2.并行提取:主题、核心信息、场景(季节/时间/室内外)、人物/物品/氛围。情绪词优先\n3.图片数量=ceil(中文字数/50),最小1\n4.场景人物按顺序排布,只按场景类型合并(室内/室外×白天/夜晚),不用主题/动作/情感合并\n5.兜底:不熟悉术语先理解再决定,敏感内容终止\n6.逐张生成五段式:主体+动作+环境+风格+技术参数。风格独立判断:情绪>环境>季节特性\n7.自检:对应段落、五段式完整、风格独立、可生图\n8.输出格式:\n# 图片提示词 — [主题]\n## 原文\n[原文]\n---\n## 图片N\n- 画面描述:[描述]\n- 风格:[风格及判断]\n- 提示词:[完整提示词]\n\n规则:不生图、不改原文、不处理非文字、敏感停止"; | |
| function getUsage(){try{var d=JSON.parse(localStorage.getItem('ttc_usage')||'{}');var h=new Date().getHours();var k=new Date().toDateString();if(d._date!==k){return{hits:0,_date:k}}if(d._hour!==h){return{hits:0,_date:k,_hour:h}}return d}catch(e){return{hits:0}}} | |
| function saveUsage(u){u._hour=new Date().getHours();u._date=new Date().toDateString();localStorage.setItem('ttc_usage',JSON.stringify(u))} | |
| function updateQuota(){var u=getUsage();var left=LIMIT-u.hits;var el=document.getElementById('quota');if(left<=0){el.innerHTML='今日已用完 <b>'+LIMIT+'</b> 次额度,请下个小时再来';document.getElementById('submitBtn').disabled=true}else{el.innerHTML='本小时剩余 <b>'+left+'</b> / '+LIMIT+' 次';document.getElementById('submitBtn').disabled=false}} | |
| document.getElementById('inputText').addEventListener('input',function(){var c=(this.value.match(/[\u4e00-\u9fff]/g)||[]).length;document.getElementById('stats').innerHTML='中文: <b>'+c+'</b> 字 → 约 <b>'+Math.max(1,Math.ceil(c/50))+'</b> 张图'}); | |
| async function go(){ | |
| var t=document.getElementById('inputText').value.trim(); | |
| if(!t)return alert('请输入文案'); | |
| var u=getUsage(); | |
| if(u.hits>=LIMIT)return alert('本小时额度已用完,请稍后再试'); | |
| var l=document.getElementById('loading'),e=document.getElementById('error'),r=document.getElementById('result'),b=document.getElementById('submitBtn'); | |
| l.classList.add('show');e.classList.remove('show');r.classList.remove('show');b.disabled=true; | |
| try{ | |
| var resp=await fetch(API_URL+'/chat/completions?v=2',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({model:MODEL,messages:[{role:'system',content:SYS},{role:'user',content:'请处理以下文案,生成图片提示词:\n\n'+t}],temperature:.7,max_tokens:4096})}); | |
| if(!resp.ok){var er=await resp.json().catch(()=>({}));var msg=er.error?.message||er.error||'HTTP '+resp.status;if(resp.status==503)msg+=' (模型加载中,请等30秒重试)';throw new Error(msg)} | |
| var d=await resp.json(); | |
| document.getElementById('rc').textContent=d.choices[0].message.content; | |
| r.classList.add('show'); | |
| u.hits++;saveUsage(u);updateQuota(); | |
| }catch(ex){e.textContent='失败: '+ex.message;e.classList.add('show')} | |
| l.classList.remove('show');b.disabled=false | |
| } | |
| function dl(){var t=document.getElementById('rc').textContent,n=new Date().toISOString().replace('T',' ').slice(0,19),a=document.createElement('a');a.href=URL.createObjectURL(new Blob([t],{type:'text/markdown'}));a.download=n+'.md';a.click()} | |
| updateQuota(); | |
| </script> | |
| </body> | |
| </html> |