Update app_enhanced.py
Browse files- app_enhanced.py +34 -22
app_enhanced.py
CHANGED
|
@@ -509,7 +509,7 @@ 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 { font-family: 'Comic Neue', cursive; position: absolute; display: flex; justify-content: center; align-items: center; width: 150px; height: 80px; min-width: 50px;
|
| 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; }
|
|
@@ -623,8 +623,7 @@ class EnhancedComicGenerator:
|
|
| 623 |
let currentlySelectedBubble = null;
|
| 624 |
let currentlySelectedPanel = null;
|
| 625 |
let isPanning = false, panStartX, panStartY, panStartTranslateX, panStartTranslateY;
|
| 626 |
-
let isResizing = false;
|
| 627 |
-
let resizeHandle, originalWidth, originalHeight, originalX, originalY, originalMouseX, originalMouseY;
|
| 628 |
|
| 629 |
function renderComic(data) {
|
| 630 |
const container = document.getElementById('comic-pages');
|
|
@@ -646,14 +645,12 @@ class EnhancedComicGenerator:
|
|
| 646 |
const img = document.createElement('img');
|
| 647 |
img.src = '/frames/final/' + panelData.image;
|
| 648 |
panelDiv.appendChild(img);
|
| 649 |
-
|
| 650 |
-
if (pageData.bubbles && pageData.bubbles[panelIndex]) {
|
| 651 |
-
const bubbleData = pageData.bubbles[panelIndex];
|
| 652 |
const bubbleDiv = createBubbleElement({
|
| 653 |
id: `initial-${pageIndex}-${panelIndex}`,
|
| 654 |
-
text:
|
| 655 |
-
left: `${
|
| 656 |
-
top: `${
|
| 657 |
});
|
| 658 |
panelDiv.appendChild(bubbleDiv);
|
| 659 |
}
|
|
@@ -672,14 +669,12 @@ class EnhancedComicGenerator:
|
|
| 672 |
});
|
| 673 |
document.querySelectorAll('.speech-bubble').forEach(initializeBubbleEvents);
|
| 674 |
document.getElementById('zoom-slider').addEventListener('input', handleZoom);
|
| 675 |
-
|
| 676 |
document.getElementById('bubble-text-color').addEventListener('input', (e) => {
|
| 677 |
if(currentlySelectedBubble) currentlySelectedBubble.style.color = e.target.value;
|
| 678 |
});
|
| 679 |
document.getElementById('bubble-fill-color').addEventListener('input', (e) => {
|
| 680 |
if(currentlySelectedBubble) currentlySelectedBubble.style.backgroundColor = e.target.value;
|
| 681 |
});
|
| 682 |
-
|
| 683 |
document.addEventListener('mousemove', e => { if (isPanning) panImage(e); if (draggedBubble) drag(e); if(isResizing) resizeBubble(e); });
|
| 684 |
document.addEventListener('mouseup', e => { if (isPanning) stopPan(e); if (draggedBubble) stopDrag(e); if(isResizing) stopResize(e);});
|
| 685 |
document.addEventListener('mouseleave', e => { if (isPanning) stopPan(e); if (draggedBubble) stopDrag(e); if(isResizing) stopResize(e);});
|
|
@@ -689,7 +684,6 @@ class EnhancedComicGenerator:
|
|
| 689 |
bubble.addEventListener('dblclick', e => { e.stopPropagation(); editBubbleText(bubble); });
|
| 690 |
bubble.addEventListener('mousedown', e => { e.stopPropagation(); startDrag(e); });
|
| 691 |
bubble.addEventListener('click', e => { e.stopPropagation(); selectBubble(bubble); });
|
| 692 |
-
|
| 693 |
['nw', 'ne', 'sw', 'se'].forEach(dir => {
|
| 694 |
const handle = document.createElement('div');
|
| 695 |
handle.className = `resize-handle ${dir}`;
|
|
@@ -765,28 +759,29 @@ class EnhancedComicGenerator:
|
|
| 765 |
panel.classList.add('selected');
|
| 766 |
currentlySelectedPanel = panel;
|
| 767 |
selectBubble(null);
|
| 768 |
-
const img = currentlySelectedPanel.querySelector('img');
|
| 769 |
-
document.getElementById('zoom-slider').value = img.dataset.zoom || 100;
|
| 770 |
-
document.getElementById('zoom-slider').disabled = false;
|
| 771 |
}
|
| 772 |
|
| 773 |
function selectBubble(bubble) {
|
| 774 |
if (currentlySelectedBubble) currentlySelectedBubble.classList.remove('selected');
|
| 775 |
currentlySelectedBubble = bubble;
|
| 776 |
const bubbleControls = ['bubble-text-color', 'bubble-fill-color', 'bubble-type-select', 'font-select'];
|
|
|
|
| 777 |
if (currentlySelectedBubble) {
|
| 778 |
currentlySelectedBubble.classList.add('selected');
|
| 779 |
if (currentlySelectedPanel) currentlySelectedPanel.classList.remove('selected');
|
| 780 |
currentlySelectedPanel = null;
|
|
|
|
| 781 |
const styles = window.getComputedStyle(currentlySelectedBubble);
|
| 782 |
document.getElementById('bubble-text-color').value = rgbToHex(styles.color);
|
| 783 |
document.getElementById('bubble-fill-color').value = rgbToHex(styles.backgroundColor);
|
| 784 |
document.getElementById('bubble-type-select').value = currentlySelectedBubble.dataset.type || 'speech';
|
| 785 |
document.getElementById('font-select').value = styles.fontFamily.split(',')[0].replace(/"/g, "").replace(/'/g, "").trim();
|
|
|
|
| 786 |
document.getElementById('zoom-slider').disabled = true;
|
| 787 |
bubbleControls.forEach(id => document.getElementById(id).disabled = false);
|
| 788 |
} else {
|
| 789 |
bubbleControls.forEach(id => document.getElementById(id).disabled = true);
|
|
|
|
| 790 |
}
|
| 791 |
}
|
| 792 |
|
|
@@ -797,6 +792,7 @@ class EnhancedComicGenerator:
|
|
| 797 |
const textarea = document.createElement('textarea');
|
| 798 |
const originalHeight = bubble.offsetHeight;
|
| 799 |
bubble.style.height = `${originalHeight}px`;
|
|
|
|
| 800 |
textarea.value = textSpan.textContent;
|
| 801 |
bubble.appendChild(textarea);
|
| 802 |
textSpan.style.display = 'none';
|
|
@@ -806,7 +802,6 @@ class EnhancedComicGenerator:
|
|
| 806 |
bubble.removeChild(textarea);
|
| 807 |
textSpan.style.display = '';
|
| 808 |
currentlyEditing = null;
|
| 809 |
-
bubble.style.height = 'auto'; // Let it reflow naturally
|
| 810 |
};
|
| 811 |
textarea.addEventListener('blur', finishEditing, { once: true });
|
| 812 |
textarea.addEventListener('keydown', e => { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); textarea.blur(); }});
|
|
@@ -841,7 +836,7 @@ class EnhancedComicGenerator:
|
|
| 841 |
selectBubble(null);
|
| 842 |
}
|
| 843 |
}
|
| 844 |
-
|
| 845 |
function startResize(e, dir) {
|
| 846 |
e.preventDefault();
|
| 847 |
e.stopPropagation();
|
|
@@ -863,10 +858,16 @@ class EnhancedComicGenerator:
|
|
| 863 |
const dy = e.clientY - originalMouseY;
|
| 864 |
const bubble = currentlySelectedBubble;
|
| 865 |
|
| 866 |
-
if (resizeHandle.includes('e'))
|
| 867 |
-
if (resizeHandle.includes('w')) {
|
| 868 |
-
|
| 869 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 870 |
}
|
| 871 |
|
| 872 |
function stopResize() { isResizing = false; }
|
|
@@ -879,7 +880,18 @@ class EnhancedComicGenerator:
|
|
| 879 |
}
|
| 880 |
|
| 881 |
async function exportPagesToPNG() {
|
| 882 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 883 |
}
|
| 884 |
|
| 885 |
function replacePanelImage() {
|
|
|
|
| 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 { font-family: 'Comic Neue', cursive; 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; }
|
|
|
|
| 623 |
let currentlySelectedBubble = null;
|
| 624 |
let currentlySelectedPanel = null;
|
| 625 |
let isPanning = false, panStartX, panStartY, panStartTranslateX, panStartTranslateY;
|
| 626 |
+
let isResizing = false, resizeHandle, originalWidth, originalHeight, originalX, originalY, originalMouseX, originalMouseY;
|
|
|
|
| 627 |
|
| 628 |
function renderComic(data) {
|
| 629 |
const container = document.getElementById('comic-pages');
|
|
|
|
| 645 |
const img = document.createElement('img');
|
| 646 |
img.src = '/frames/final/' + panelData.image;
|
| 647 |
panelDiv.appendChild(img);
|
| 648 |
+
if (pageData.bubbles && pageData.bubbles[panelIndex] && pageData.bubbles[panelIndex].dialog) {
|
|
|
|
|
|
|
| 649 |
const bubbleDiv = createBubbleElement({
|
| 650 |
id: `initial-${pageIndex}-${panelIndex}`,
|
| 651 |
+
text: pageData.bubbles[panelIndex].dialog || '',
|
| 652 |
+
left: `${pageData.bubbles[panelIndex].bubble_offset_x ?? 50}px`,
|
| 653 |
+
top: `${pageData.bubbles[panelIndex].bubble_offset_y ?? 20}px`,
|
| 654 |
});
|
| 655 |
panelDiv.appendChild(bubbleDiv);
|
| 656 |
}
|
|
|
|
| 669 |
});
|
| 670 |
document.querySelectorAll('.speech-bubble').forEach(initializeBubbleEvents);
|
| 671 |
document.getElementById('zoom-slider').addEventListener('input', handleZoom);
|
|
|
|
| 672 |
document.getElementById('bubble-text-color').addEventListener('input', (e) => {
|
| 673 |
if(currentlySelectedBubble) currentlySelectedBubble.style.color = e.target.value;
|
| 674 |
});
|
| 675 |
document.getElementById('bubble-fill-color').addEventListener('input', (e) => {
|
| 676 |
if(currentlySelectedBubble) currentlySelectedBubble.style.backgroundColor = e.target.value;
|
| 677 |
});
|
|
|
|
| 678 |
document.addEventListener('mousemove', e => { if (isPanning) panImage(e); if (draggedBubble) drag(e); if(isResizing) resizeBubble(e); });
|
| 679 |
document.addEventListener('mouseup', e => { if (isPanning) stopPan(e); if (draggedBubble) stopDrag(e); if(isResizing) stopResize(e);});
|
| 680 |
document.addEventListener('mouseleave', e => { if (isPanning) stopPan(e); if (draggedBubble) stopDrag(e); if(isResizing) stopResize(e);});
|
|
|
|
| 684 |
bubble.addEventListener('dblclick', e => { e.stopPropagation(); editBubbleText(bubble); });
|
| 685 |
bubble.addEventListener('mousedown', e => { e.stopPropagation(); startDrag(e); });
|
| 686 |
bubble.addEventListener('click', e => { e.stopPropagation(); selectBubble(bubble); });
|
|
|
|
| 687 |
['nw', 'ne', 'sw', 'se'].forEach(dir => {
|
| 688 |
const handle = document.createElement('div');
|
| 689 |
handle.className = `resize-handle ${dir}`;
|
|
|
|
| 759 |
panel.classList.add('selected');
|
| 760 |
currentlySelectedPanel = panel;
|
| 761 |
selectBubble(null);
|
|
|
|
|
|
|
|
|
|
| 762 |
}
|
| 763 |
|
| 764 |
function selectBubble(bubble) {
|
| 765 |
if (currentlySelectedBubble) currentlySelectedBubble.classList.remove('selected');
|
| 766 |
currentlySelectedBubble = bubble;
|
| 767 |
const bubbleControls = ['bubble-text-color', 'bubble-fill-color', 'bubble-type-select', 'font-select'];
|
| 768 |
+
|
| 769 |
if (currentlySelectedBubble) {
|
| 770 |
currentlySelectedBubble.classList.add('selected');
|
| 771 |
if (currentlySelectedPanel) currentlySelectedPanel.classList.remove('selected');
|
| 772 |
currentlySelectedPanel = null;
|
| 773 |
+
|
| 774 |
const styles = window.getComputedStyle(currentlySelectedBubble);
|
| 775 |
document.getElementById('bubble-text-color').value = rgbToHex(styles.color);
|
| 776 |
document.getElementById('bubble-fill-color').value = rgbToHex(styles.backgroundColor);
|
| 777 |
document.getElementById('bubble-type-select').value = currentlySelectedBubble.dataset.type || 'speech';
|
| 778 |
document.getElementById('font-select').value = styles.fontFamily.split(',')[0].replace(/"/g, "").replace(/'/g, "").trim();
|
| 779 |
+
|
| 780 |
document.getElementById('zoom-slider').disabled = true;
|
| 781 |
bubbleControls.forEach(id => document.getElementById(id).disabled = false);
|
| 782 |
} else {
|
| 783 |
bubbleControls.forEach(id => document.getElementById(id).disabled = true);
|
| 784 |
+
if(currentlySelectedPanel) document.getElementById('zoom-slider').disabled = false;
|
| 785 |
}
|
| 786 |
}
|
| 787 |
|
|
|
|
| 792 |
const textarea = document.createElement('textarea');
|
| 793 |
const originalHeight = bubble.offsetHeight;
|
| 794 |
bubble.style.height = `${originalHeight}px`;
|
| 795 |
+
|
| 796 |
textarea.value = textSpan.textContent;
|
| 797 |
bubble.appendChild(textarea);
|
| 798 |
textSpan.style.display = 'none';
|
|
|
|
| 802 |
bubble.removeChild(textarea);
|
| 803 |
textSpan.style.display = '';
|
| 804 |
currentlyEditing = null;
|
|
|
|
| 805 |
};
|
| 806 |
textarea.addEventListener('blur', finishEditing, { once: true });
|
| 807 |
textarea.addEventListener('keydown', e => { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); textarea.blur(); }});
|
|
|
|
| 836 |
selectBubble(null);
|
| 837 |
}
|
| 838 |
}
|
| 839 |
+
|
| 840 |
function startResize(e, dir) {
|
| 841 |
e.preventDefault();
|
| 842 |
e.stopPropagation();
|
|
|
|
| 858 |
const dy = e.clientY - originalMouseY;
|
| 859 |
const bubble = currentlySelectedBubble;
|
| 860 |
|
| 861 |
+
if (resizeHandle.includes('e')) bubble.style.width = `${originalWidth + dx}px`;
|
| 862 |
+
if (resizeHandle.includes('w')) {
|
| 863 |
+
bubble.style.width = `${originalWidth - dx}px`;
|
| 864 |
+
bubble.style.left = `${originalX + dx}px`;
|
| 865 |
+
}
|
| 866 |
+
if (resizeHandle.includes('s')) bubble.style.height = `${originalHeight + dy}px`;
|
| 867 |
+
if (resizeHandle.includes('n')) {
|
| 868 |
+
bubble.style.height = `${originalHeight - dy}px`;
|
| 869 |
+
bubble.style.top = `${originalY + dy}px`;
|
| 870 |
+
}
|
| 871 |
}
|
| 872 |
|
| 873 |
function stopResize() { isResizing = false; }
|
|
|
|
| 880 |
}
|
| 881 |
|
| 882 |
async function exportPagesToPNG() {
|
| 883 |
+
const pages = document.querySelectorAll('.comic-page');
|
| 884 |
+
if (pages.length === 0) return alert("No pages found.");
|
| 885 |
+
alert(`Starting export of ${pages.length} page(s).`);
|
| 886 |
+
for (let i = 0; i < pages.length; i++) {
|
| 887 |
+
try {
|
| 888 |
+
const canvas = await html2canvas(pages[i], { scale: 2 });
|
| 889 |
+
const link = document.createElement('a');
|
| 890 |
+
link.download = `comic-page-${i + 1}.png`;
|
| 891 |
+
link.href = canvas.toDataURL('image/png');
|
| 892 |
+
link.click();
|
| 893 |
+
} catch (err) { alert(`Failed to export page ${i + 1}.`); }
|
| 894 |
+
}
|
| 895 |
}
|
| 896 |
|
| 897 |
function replacePanelImage() {
|