ShadowfangV1 / script.js
Dalladrain's picture
Update script.js
4fe2976 verified
let characters=[];
// Tab switching
document.querySelectorAll(".tab-btn").forEach(btn=>{
btn.addEventListener("click",()=>{
document.querySelectorAll(".tab-btn").forEach(b=>b.classList.remove("active"));
document.querySelectorAll(".tab-content").forEach(c=>c.classList.add("hidden"));
btn.classList.add("active");
document.getElementById(btn.dataset.tab).classList.remove("hidden");
});
});
// Add character
document.getElementById("add-character").addEventListener("click",()=>{
const container=document.createElement("div");
container.className="border p-4 rounded";
container.innerHTML=`
<input class="char-name w-full p-2 border rounded mb-1" placeholder="Name">
<textarea class="char-desc w-full p-2 border rounded mb-1" placeholder="Character description (appearance, outfit, build, etc.)"></textarea>
<input type="file" class="char-img mb-1" accept="image/png, image/jpeg">
`;
document.getElementById("character-list").appendChild(container);
characters.push({name:"",desc:"",img:null});
});
// Convert file to Base64
function imageFileToBase64(file){
return new Promise((resolve,reject)=>{
const reader=new FileReader();
reader.onload=()=>resolve(reader.result);
reader.onerror=err=>reject(err);
reader.readAsDataURL(file);
});
}
// Make speech bubbles draggable
function makeDraggable(el){
let offsetX,offsetY;
el.onmousedown=e=>{
offsetX=e.clientX-el.offsetLeft; offsetY=e.clientY-el.offsetTop;
document.onmousemove=e=>{el.style.left=(e.clientX-offsetX)+"px"; el.style.top=(e.clientY-offsetY)+"px";}
document.onmouseup=()=>document.onmousemove=null;
};
}
// Comic sound effects
const comicEffects=["BAM!","KAPOW!","KA-BOOM!","WHAM!","ZAP!","BOOM!"];
// Generate panels
document.getElementById("generate-btn").addEventListener("click", async()=>{
const story=document.getElementById("story-input").value.trim();
const panels=parseInt(document.getElementById("panel-count").value);
const apiKey=document.getElementById("api-key").value.trim();
const artStyle=document.getElementById("art-style").value;
const explicit=document.getElementById("explicit-toggle").checked;
const model=document.getElementById("custom-model").value.trim() || "stabilityai/stable-diffusion-2";
const bgDesc=document.getElementById("bg-desc").value.trim();
const preview=document.getElementById("comic-preview");
preview.innerHTML="";
// Collect characters
const charPrompts=[];
const charImages=[];
const charDivs=document.querySelectorAll("#character-list .border");
for(const div of charDivs){
const name=div.querySelector(".char-name").value;
const desc=div.querySelector(".char-desc").value;
const file=div.querySelector(".char-img").files[0];
if(name && desc) charPrompts.push(`${name}: ${desc}`);
if(file) charImages.push(await imageFileToBase64(file));
}
for(let i=0;i<panels;i++){
const panelDiv=document.createElement("div");
panelDiv.className="comic-panel";
panelDiv.innerText="⏳ Generating...";
preview.appendChild(panelDiv);
try{
const prompt=`Panel ${i+1} of story: ${story}. Style: ${artStyle}${explicit ? ", mature, graphic" : ""}. Background: ${bgDesc}. Characters: ${charPrompts.join(", ")}`;
// Generate image with ControlNet reference
const imgResp=await fetch(`https://api-inference.huggingface.co/models/${model}`,{
method:"POST",
headers:{Authorization:`Bearer ${apiKey}`, "Content-Type":"application/json"},
body: JSON.stringify({
inputs: prompt,
parameters: {
guidance_scale: 7.5,
num_inference_steps: 30,
height:512,
width:512,
controlnet_reference_images: charImages
}
})
});
const imgBlob=await imgResp.blob();
const imgUrl=URL.createObjectURL(imgBlob);
panelDiv.innerHTML=`<img src="${imgUrl}" class="comic-img">`;
// Generate short dialogue for speech bubble
const textPrompt=`Generate short comic dialogue for panel ${i+1} with characters: ${charPrompts.join(", ")} including sound effects like BAM, POW, KAPOW.`;
const textResp=await fetch(`https://api-inference.huggingface.co/models/gpt2`,{
method:"POST",
headers:{Authorization:`Bearer ${apiKey}`, "Content-Type":"application/json"},
body:JSON.stringify({inputs:textPrompt,max_new_tokens:40})
}).then(r=>r.json());
// Add draggable speech bubble
const bubble=document.createElement("div");
bubble.className="speech-bubble";
bubble.contentEditable=true;
bubble.innerText=textResp[0]?.generated_text?.split(".")[0] || comicEffects[Math.floor(Math.random()*comicEffects.length)];
makeDraggable(bubble);
panelDiv.appendChild(bubble);
// Add editable caption
const caption=document.createElement("div");
caption.className="caption";
caption.contentEditable=true;
caption.innerText=textResp[0]?.generated_text || "";
panelDiv.appendChild(caption);
}catch(err){
console.error(err);
panelDiv.innerText="❌ Error generating panel";
}
}
});
// Export PDF
document.getElementById("download-btn").addEventListener("click",()=>{
const {jsPDF}=window.jspdf;
const pdf=new jsPDF();
const panels=document.querySelectorAll(".comic-panel img");
panels.forEach((img,i)=>{
if(i>0) pdf.addPage();
pdf.addImage(img.src,"JPEG",10,10,180,160);
});
pdf.save("comic-book.pdf");
});