Update app_enhanced.py
Browse files- app_enhanced.py +82 -39
app_enhanced.py
CHANGED
|
@@ -496,7 +496,7 @@ class EnhancedComicGenerator:
|
|
| 496 |
<script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js"></script>
|
| 497 |
<link rel="preconnect" href="https://fonts.googleapis.com">
|
| 498 |
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
| 499 |
-
<link href="https://fonts.googleapis.com/css2?family=Bangers&family=Comic+Neue:wght@700&family=Gloria+Hallelujah&family=Lato&display=swap" rel="stylesheet">
|
| 500 |
<style>
|
| 501 |
body { margin: 0; padding: 20px; background: #f0f0f0; font-family: 'Lato', sans-serif; }
|
| 502 |
.comic-container { max-width: 1200px; margin: 0 auto; }
|
|
@@ -509,27 +509,83 @@ class EnhancedComicGenerator:
|
|
| 509 |
.panel img { width: 100%; height: 100%; object-fit: cover; object-position: center; transition: transform 0.1s ease-out; }
|
| 510 |
.panel img.pannable { cursor: grab; }
|
| 511 |
.panel img.panning { cursor: grabbing; }
|
| 512 |
-
.speech-bubble {
|
| 513 |
.bubble-text { padding: 2px; word-wrap: break-word; }
|
| 514 |
.speech-bubble.selected { outline: 2px dashed #4CAF50; }
|
| 515 |
.speech-bubble textarea { position: absolute; top: 0; left: 0; width: 100%; height: 100%; box-sizing: border-box; border: 1px solid #4CAF50; background: rgba(255,255,255,0.95); font: inherit; text-align: center; resize: none; padding: 8px; z-index: 102; }
|
| 516 |
-
|
| 517 |
-
|
| 518 |
-
.speech-bubble.
|
| 519 |
-
|
| 520 |
-
|
| 521 |
-
|
| 522 |
-
|
| 523 |
-
|
| 524 |
-
|
| 525 |
-
|
| 526 |
-
.
|
| 527 |
-
|
| 528 |
-
|
| 529 |
-
|
| 530 |
-
|
| 531 |
-
|
| 532 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 533 |
.resize-handle { position: absolute; width: 10px; height: 10px; background: #2196F3; border: 1px solid white; border-radius: 50%; display: none; z-index: 11; }
|
| 534 |
.speech-bubble.selected .resize-handle { display: block; }
|
| 535 |
.resize-handle.se { bottom: -5px; right: -5px; cursor: se-resize; }
|
|
@@ -567,6 +623,7 @@ class EnhancedComicGenerator:
|
|
| 567 |
<option value="speech">Speech</option><option value="thought">Thought</option><option value="reaction">Reaction</option><option value="narration">Narration</option><option value="idea">Idea</option>
|
| 568 |
</select>
|
| 569 |
<select id="font-select" onchange="changeFont(this.value)" disabled>
|
|
|
|
| 570 |
<option value="'Comic Neue', cursive">Comic Neue</option>
|
| 571 |
<option value="'Bangers', cursive">Bangers</option>
|
| 572 |
<option value="'Gloria Hallelujah', cursive">Gloria</option>
|
|
@@ -582,7 +639,7 @@ class EnhancedComicGenerator:
|
|
| 582 |
<input type="color" id="bubble-fill-color" value="#FFFFFF" disabled>
|
| 583 |
</div>
|
| 584 |
</div>
|
| 585 |
-
<button onclick="rotateBubbleTail()" class="secondary-button">🔄
|
| 586 |
<button onclick="addBubbleToPanel()" class="action-button">💬 Add Bubble</button>
|
| 587 |
<button onclick="deleteBubble()" class="reset-button">🗑️ Delete Bubble</button>
|
| 588 |
</div>
|
|
@@ -717,21 +774,12 @@ class EnhancedComicGenerator:
|
|
| 717 |
}
|
| 718 |
|
| 719 |
function applyBubbleType(bubble, type) {
|
| 720 |
-
|
| 721 |
-
|
| 722 |
-
if (bubble.classList.contains('
|
| 723 |
-
|
| 724 |
-
if (bubble.classList.contains('flipped-vertical')) classesToKeep += ' flipped-vertical';
|
| 725 |
-
bubble.className = classesToKeep;
|
| 726 |
bubble.classList.add(type);
|
| 727 |
bubble.dataset.type = type;
|
| 728 |
-
if (type === 'thought') {
|
| 729 |
-
for (let i = 1; i <= 2; i++) {
|
| 730 |
-
const dot = document.createElement('div');
|
| 731 |
-
dot.className = `thought-dot thought-dot-${i}`;
|
| 732 |
-
bubble.appendChild(dot);
|
| 733 |
-
}
|
| 734 |
-
}
|
| 735 |
}
|
| 736 |
|
| 737 |
function changeBubbleType(type) {
|
|
@@ -746,12 +794,7 @@ class EnhancedComicGenerator:
|
|
| 746 |
|
| 747 |
function rotateBubbleTail() {
|
| 748 |
if (!currentlySelectedBubble) { alert("Please select a bubble first."); return; }
|
| 749 |
-
|
| 750 |
-
const isFlippedV = currentlySelectedBubble.classList.contains('flipped-vertical');
|
| 751 |
-
if (!isFlippedH && !isFlippedV) { currentlySelectedBubble.classList.add('flipped'); }
|
| 752 |
-
else if (isFlippedH && !isFlippedV) { currentlySelectedBubble.classList.add('flipped-vertical'); }
|
| 753 |
-
else if (isFlippedH && isFlippedV) { currentlySelectedBubble.classList.remove('flipped'); }
|
| 754 |
-
else { currentlySelectedBubble.classList.remove('flipped-vertical'); }
|
| 755 |
}
|
| 756 |
|
| 757 |
function selectPanel(panel) {
|
|
|
|
| 496 |
<script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js"></script>
|
| 497 |
<link rel="preconnect" href="https://fonts.googleapis.com">
|
| 498 |
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
| 499 |
+
<link href="https://fonts.googleapis.com/css2?family=Bangers&family=Comic+Neue:wght@700&family=Gloria+Hallelujah&family=Lato&family=Permanent+Marker&display=swap" rel="stylesheet">
|
| 500 |
<style>
|
| 501 |
body { margin: 0; padding: 20px; background: #f0f0f0; font-family: 'Lato', sans-serif; }
|
| 502 |
.comic-container { max-width: 1200px; margin: 0 auto; }
|
|
|
|
| 509 |
.panel img { width: 100%; height: 100%; object-fit: cover; object-position: center; transition: transform 0.1s ease-out; }
|
| 510 |
.panel img.pannable { cursor: grab; }
|
| 511 |
.panel img.panning { cursor: grabbing; }
|
| 512 |
+
.speech-bubble { position: absolute; display: flex; justify-content: center; align-items: center; width: 150px; height: 80px; min-width: 50px; min-height: 30px; box-sizing: border-box; padding: 8px; box-shadow: 2px 2px 5px rgba(0,0,0,0.3); z-index: 10; cursor: move; overflow: visible; font-size: 13px; font-weight: bold; text-align: center; }
|
| 513 |
.bubble-text { padding: 2px; word-wrap: break-word; }
|
| 514 |
.speech-bubble.selected { outline: 2px dashed #4CAF50; }
|
| 515 |
.speech-bubble textarea { position: absolute; top: 0; left: 0; width: 100%; height: 100%; box-sizing: border-box; border: 1px solid #4CAF50; background: rgba(255,255,255,0.95); font: inherit; text-align: center; resize: none; padding: 8px; z-index: 102; }
|
| 516 |
+
|
| 517 |
+
/* --- NEW BUBBLE STYLES --- */
|
| 518 |
+
.speech-bubble.speech {
|
| 519 |
+
padding: 20px 30px;
|
| 520 |
+
border-radius: 10px;
|
| 521 |
+
border: 3px solid black;
|
| 522 |
+
background: white;
|
| 523 |
+
color: black;
|
| 524 |
+
font-family: "Permanent Marker", cursive;
|
| 525 |
+
}
|
| 526 |
+
.speech-bubble.speech:before {
|
| 527 |
+
content: '';
|
| 528 |
+
position: absolute;
|
| 529 |
+
bottom: -50px;
|
| 530 |
+
height: 50px;
|
| 531 |
+
width: 90px;
|
| 532 |
+
border-radius: 0 0 100%;
|
| 533 |
+
box-shadow: -2px -2px 0 0 #000 inset, -23px 0 0 0 #fff inset, -25px -2px 0 0 #000 inset;
|
| 534 |
+
left: 0;
|
| 535 |
+
}
|
| 536 |
+
.speech-bubble.speech.flipped:before {
|
| 537 |
+
border-radius: 0 0 0 100%;
|
| 538 |
+
box-shadow: 2px -2px 0 0 #000 inset, 23px 0 0 0 #fff inset, 25px -2px 0 0 #000 inset;
|
| 539 |
+
right: 0;
|
| 540 |
+
left: auto;
|
| 541 |
+
}
|
| 542 |
+
|
| 543 |
+
.speech-bubble.thought {
|
| 544 |
+
border-radius: 50%;
|
| 545 |
+
border: 3px solid black;
|
| 546 |
+
background: white;
|
| 547 |
+
color: black;
|
| 548 |
+
}
|
| 549 |
+
.speech-bubble.thought:before {
|
| 550 |
+
content: '';
|
| 551 |
+
position: absolute;
|
| 552 |
+
height: 3px;
|
| 553 |
+
width: 3px;
|
| 554 |
+
bottom: -20px;
|
| 555 |
+
border-radius: 100%;
|
| 556 |
+
background: #fff;
|
| 557 |
+
left: 20px;
|
| 558 |
+
box-shadow: 0 0 0 7px white, 0 0 0 10px black, -20px 15px 0 5px white, -20px 15px 0 8px black, -40px 20px 0 2px white, -40px 20px 0 5px black;
|
| 559 |
+
}
|
| 560 |
+
.speech-bubble.thought.flipped:before {
|
| 561 |
+
right: 20px;
|
| 562 |
+
left: auto;
|
| 563 |
+
box-shadow: 0 0 0 7px white, 0 0 0 10px black, 20px 15px 0 5px white, 20px 15px 0 8px black, 40px 20px 0 2px white, 40px 20px 0 5px black;
|
| 564 |
+
}
|
| 565 |
+
|
| 566 |
+
.speech-bubble.reaction {
|
| 567 |
+
background: #FFD700;
|
| 568 |
+
border: 3px solid #E53935;
|
| 569 |
+
color: #D32F2F;
|
| 570 |
+
font-weight: 900;
|
| 571 |
+
border-radius: 50%;
|
| 572 |
+
position: relative;
|
| 573 |
+
}
|
| 574 |
+
.speech-bubble.reaction:before, .speech-bubble.reaction:after {
|
| 575 |
+
content: '';
|
| 576 |
+
position: absolute;
|
| 577 |
+
top: -3px; left: -3px; right: -3px; bottom: -3px;
|
| 578 |
+
background: inherit;
|
| 579 |
+
clip-path: polygon(50% 0%, 60% 40%, 100% 40%, 70% 65%, 85% 100%, 50% 75%, 15% 100%, 30% 65%, 0% 40%, 40% 40%);
|
| 580 |
+
z-index: -1;
|
| 581 |
+
}
|
| 582 |
+
.speech-bubble.reaction:after {
|
| 583 |
+
transform: rotate(20deg);
|
| 584 |
+
}
|
| 585 |
+
|
| 586 |
+
.speech-bubble.narration { background: #FAFAFA; border: 2px solid #BDBDBD; color: #424242; border-radius: 3px; }
|
| 587 |
+
.speech-bubble.idea { background: linear-gradient(180deg,#FFFDD0 0%, #FFF8B5 100%); border: 2px solid #FFA500; color: #6a4b00; border-radius: 40% 60% 40% 60% / 60% 40% 60% 40%; }
|
| 588 |
+
|
| 589 |
.resize-handle { position: absolute; width: 10px; height: 10px; background: #2196F3; border: 1px solid white; border-radius: 50%; display: none; z-index: 11; }
|
| 590 |
.speech-bubble.selected .resize-handle { display: block; }
|
| 591 |
.resize-handle.se { bottom: -5px; right: -5px; cursor: se-resize; }
|
|
|
|
| 623 |
<option value="speech">Speech</option><option value="thought">Thought</option><option value="reaction">Reaction</option><option value="narration">Narration</option><option value="idea">Idea</option>
|
| 624 |
</select>
|
| 625 |
<select id="font-select" onchange="changeFont(this.value)" disabled>
|
| 626 |
+
<option value="'Permanent Marker', cursive">Permanent Marker</option>
|
| 627 |
<option value="'Comic Neue', cursive">Comic Neue</option>
|
| 628 |
<option value="'Bangers', cursive">Bangers</option>
|
| 629 |
<option value="'Gloria Hallelujah', cursive">Gloria</option>
|
|
|
|
| 639 |
<input type="color" id="bubble-fill-color" value="#FFFFFF" disabled>
|
| 640 |
</div>
|
| 641 |
</div>
|
| 642 |
+
<button onclick="rotateBubbleTail()" class="secondary-button">🔄 Flip Tail</button>
|
| 643 |
<button onclick="addBubbleToPanel()" class="action-button">💬 Add Bubble</button>
|
| 644 |
<button onclick="deleteBubble()" class="reset-button">🗑️ Delete Bubble</button>
|
| 645 |
</div>
|
|
|
|
| 774 |
}
|
| 775 |
|
| 776 |
function applyBubbleType(bubble, type) {
|
| 777 |
+
let classesToKeep = ['speech-bubble'];
|
| 778 |
+
if (bubble.classList.contains('selected')) classesToKeep.push('selected');
|
| 779 |
+
if (bubble.classList.contains('flipped')) classesToKeep.push('flipped');
|
| 780 |
+
bubble.className = classesToKeep.join(' ');
|
|
|
|
|
|
|
| 781 |
bubble.classList.add(type);
|
| 782 |
bubble.dataset.type = type;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 783 |
}
|
| 784 |
|
| 785 |
function changeBubbleType(type) {
|
|
|
|
| 794 |
|
| 795 |
function rotateBubbleTail() {
|
| 796 |
if (!currentlySelectedBubble) { alert("Please select a bubble first."); return; }
|
| 797 |
+
currentlySelectedBubble.classList.toggle('flipped');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 798 |
}
|
| 799 |
|
| 800 |
function selectPanel(panel) {
|