File size: 5,535 Bytes
877b8f2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4fe2976
877b8f2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4fe2976
877b8f2
 
 
 
 
 
 
 
 
4fe2976
 
 
877b8f2
 
 
 
 
 
 
4fe2976
 
877b8f2
 
 
4fe2976
877b8f2
 
 
 
 
 
 
4fe2976
877b8f2
 
 
 
 
 
 
 
 
 
4fe2976
877b8f2
4fe2976
 
877b8f2
4fe2976
 
 
 
 
 
 
 
 
 
 
 
877b8f2
4fe2976
877b8f2
 
 
4fe2976
 
 
 
 
 
 
 
 
877b8f2
4fe2976
 
 
877b8f2
 
 
4fe2976
 
 
 
 
877b8f2
 
4fe2976
 
 
 
877b8f2
 
 
 
 
 
 
 
 
 
 
 
 
 
4fe2976
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
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");
});