Update app_enhanced.py
Browse files- app_enhanced.py +38 -17
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; 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,7 +623,6 @@ class EnhancedComicGenerator:
|
|
| 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');
|
|
@@ -686,6 +685,8 @@ class EnhancedComicGenerator:
|
|
| 686 |
bubble.addEventListener('dblclick', e => { e.stopPropagation(); editBubbleText(bubble); });
|
| 687 |
bubble.addEventListener('mousedown', e => { e.stopPropagation(); startDrag(e); });
|
| 688 |
bubble.addEventListener('click', e => { e.stopPropagation(); selectBubble(bubble); });
|
|
|
|
|
|
|
| 689 |
['nw', 'ne', 'sw', 'se'].forEach(dir => {
|
| 690 |
const handle = document.createElement('div');
|
| 691 |
handle.className = `resize-handle ${dir}`;
|
|
@@ -761,12 +762,20 @@ class EnhancedComicGenerator:
|
|
| 761 |
panel.classList.add('selected');
|
| 762 |
currentlySelectedPanel = panel;
|
| 763 |
selectBubble(null);
|
|
|
|
|
|
|
|
|
|
|
|
|
| 764 |
}
|
| 765 |
|
| 766 |
function selectBubble(bubble) {
|
| 767 |
if (currentlySelectedBubble) currentlySelectedBubble.classList.remove('selected');
|
| 768 |
currentlySelectedBubble = bubble;
|
| 769 |
-
const
|
|
|
|
|
|
|
|
|
|
|
|
|
| 770 |
|
| 771 |
if (currentlySelectedBubble) {
|
| 772 |
currentlySelectedBubble.classList.add('selected');
|
|
@@ -774,16 +783,15 @@ class EnhancedComicGenerator:
|
|
| 774 |
currentlySelectedPanel = null;
|
| 775 |
|
| 776 |
const styles = window.getComputedStyle(currentlySelectedBubble);
|
| 777 |
-
|
| 778 |
-
|
| 779 |
-
|
| 780 |
-
|
| 781 |
|
| 782 |
document.getElementById('zoom-slider').disabled = true;
|
| 783 |
-
bubbleControls.forEach(
|
| 784 |
} else {
|
| 785 |
-
bubbleControls.forEach(
|
| 786 |
-
if(currentlySelectedPanel) document.getElementById('zoom-slider').disabled = false;
|
| 787 |
}
|
| 788 |
}
|
| 789 |
|
|
@@ -792,8 +800,9 @@ class EnhancedComicGenerator:
|
|
| 792 |
currentlyEditing = bubble;
|
| 793 |
const textSpan = bubble.querySelector('.bubble-text');
|
| 794 |
const textarea = document.createElement('textarea');
|
| 795 |
-
|
| 796 |
-
bubble.style.height
|
|
|
|
| 797 |
|
| 798 |
textarea.value = textSpan.textContent;
|
| 799 |
bubble.appendChild(textarea);
|
|
@@ -803,6 +812,9 @@ class EnhancedComicGenerator:
|
|
| 803 |
textSpan.textContent = textarea.value;
|
| 804 |
bubble.removeChild(textarea);
|
| 805 |
textSpan.style.display = '';
|
|
|
|
|
|
|
|
|
|
| 806 |
currentlyEditing = null;
|
| 807 |
};
|
| 808 |
textarea.addEventListener('blur', finishEditing, { once: true });
|
|
@@ -839,6 +851,9 @@ class EnhancedComicGenerator:
|
|
| 839 |
}
|
| 840 |
}
|
| 841 |
|
|
|
|
|
|
|
|
|
|
| 842 |
function startResize(e, dir) {
|
| 843 |
e.preventDefault();
|
| 844 |
e.stopPropagation();
|
|
@@ -860,19 +875,25 @@ class EnhancedComicGenerator:
|
|
| 860 |
const dy = e.clientY - originalMouseY;
|
| 861 |
const bubble = currentlySelectedBubble;
|
| 862 |
|
| 863 |
-
if (resizeHandle.includes('e'))
|
|
|
|
|
|
|
| 864 |
if (resizeHandle.includes('w')) {
|
| 865 |
bubble.style.width = `${originalWidth - dx}px`;
|
| 866 |
bubble.style.left = `${originalX + dx}px`;
|
| 867 |
}
|
| 868 |
-
if (resizeHandle.includes('s'))
|
|
|
|
|
|
|
| 869 |
if (resizeHandle.includes('n')) {
|
| 870 |
bubble.style.height = `${originalHeight - dy}px`;
|
| 871 |
bubble.style.top = `${originalY + dy}px`;
|
| 872 |
}
|
| 873 |
}
|
| 874 |
|
| 875 |
-
function stopResize() {
|
|
|
|
|
|
|
| 876 |
|
| 877 |
function clearSavedState() {
|
| 878 |
if (confirm("Reset all edits?")) {
|
|
@@ -900,8 +921,8 @@ class EnhancedComicGenerator:
|
|
| 900 |
.then(data => {
|
| 901 |
if (data.success) {
|
| 902 |
img.src = `/frames/final/${data.new_filename}?t=${new Date().getTime()}`;
|
| 903 |
-
img.style.objectFit = 'contain';
|
| 904 |
-
resetPanelTransform();
|
| 905 |
} else { alert('Error replacing image: ' + data.error); }
|
| 906 |
img.style.opacity = '1';
|
| 907 |
})
|
|
|
|
| 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; max-width: 220px; 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 |
|
| 627 |
function renderComic(data) {
|
| 628 |
const container = document.getElementById('comic-pages');
|
|
|
|
| 685 |
bubble.addEventListener('dblclick', e => { e.stopPropagation(); editBubbleText(bubble); });
|
| 686 |
bubble.addEventListener('mousedown', e => { e.stopPropagation(); startDrag(e); });
|
| 687 |
bubble.addEventListener('click', e => { e.stopPropagation(); selectBubble(bubble); });
|
| 688 |
+
|
| 689 |
+
// Add resize handles
|
| 690 |
['nw', 'ne', 'sw', 'se'].forEach(dir => {
|
| 691 |
const handle = document.createElement('div');
|
| 692 |
handle.className = `resize-handle ${dir}`;
|
|
|
|
| 762 |
panel.classList.add('selected');
|
| 763 |
currentlySelectedPanel = panel;
|
| 764 |
selectBubble(null);
|
| 765 |
+
|
| 766 |
+
const img = currentlySelectedPanel.querySelector('img');
|
| 767 |
+
document.getElementById('zoom-slider').value = img.dataset.zoom || 100;
|
| 768 |
+
document.getElementById('zoom-slider').disabled = false;
|
| 769 |
}
|
| 770 |
|
| 771 |
function selectBubble(bubble) {
|
| 772 |
if (currentlySelectedBubble) currentlySelectedBubble.classList.remove('selected');
|
| 773 |
currentlySelectedBubble = bubble;
|
| 774 |
+
const textColorPicker = document.getElementById('bubble-text-color');
|
| 775 |
+
const fillColorPicker = document.getElementById('bubble-fill-color');
|
| 776 |
+
const typeSelect = document.getElementById('bubble-type-select');
|
| 777 |
+
const fontSelect = document.getElementById('font-select');
|
| 778 |
+
const bubbleControls = [textColorPicker, fillColorPicker, typeSelect, fontSelect];
|
| 779 |
|
| 780 |
if (currentlySelectedBubble) {
|
| 781 |
currentlySelectedBubble.classList.add('selected');
|
|
|
|
| 783 |
currentlySelectedPanel = null;
|
| 784 |
|
| 785 |
const styles = window.getComputedStyle(currentlySelectedBubble);
|
| 786 |
+
textColorPicker.value = rgbToHex(styles.color);
|
| 787 |
+
fillColorPicker.value = rgbToHex(styles.backgroundColor);
|
| 788 |
+
typeSelect.value = currentlySelectedBubble.dataset.type || 'speech';
|
| 789 |
+
fontSelect.value = styles.fontFamily.split(',')[0].replace(/"/g, "").replace(/'/g, "").trim();
|
| 790 |
|
| 791 |
document.getElementById('zoom-slider').disabled = true;
|
| 792 |
+
bubbleControls.forEach(c => c.disabled = false);
|
| 793 |
} else {
|
| 794 |
+
bubbleControls.forEach(c => c.disabled = true);
|
|
|
|
| 795 |
}
|
| 796 |
}
|
| 797 |
|
|
|
|
| 800 |
currentlyEditing = bubble;
|
| 801 |
const textSpan = bubble.querySelector('.bubble-text');
|
| 802 |
const textarea = document.createElement('textarea');
|
| 803 |
+
// FIX: Preserve height before editing
|
| 804 |
+
const originalHeight = bubble.style.height || `${bubble.offsetHeight}px`;
|
| 805 |
+
bubble.style.height = originalHeight;
|
| 806 |
|
| 807 |
textarea.value = textSpan.textContent;
|
| 808 |
bubble.appendChild(textarea);
|
|
|
|
| 812 |
textSpan.textContent = textarea.value;
|
| 813 |
bubble.removeChild(textarea);
|
| 814 |
textSpan.style.display = '';
|
| 815 |
+
// FIX: Re-apply height, then set to auto to allow natural flow
|
| 816 |
+
bubble.style.height = originalHeight;
|
| 817 |
+
setTimeout(() => { bubble.style.height = 'auto'; }, 0);
|
| 818 |
currentlyEditing = null;
|
| 819 |
};
|
| 820 |
textarea.addEventListener('blur', finishEditing, { once: true });
|
|
|
|
| 851 |
}
|
| 852 |
}
|
| 853 |
|
| 854 |
+
let isResizing = false;
|
| 855 |
+
let resizeHandle, originalWidth, originalHeight, originalX, originalY, originalMouseX, originalMouseY;
|
| 856 |
+
|
| 857 |
function startResize(e, dir) {
|
| 858 |
e.preventDefault();
|
| 859 |
e.stopPropagation();
|
|
|
|
| 875 |
const dy = e.clientY - originalMouseY;
|
| 876 |
const bubble = currentlySelectedBubble;
|
| 877 |
|
| 878 |
+
if (resizeHandle.includes('e')) {
|
| 879 |
+
bubble.style.width = `${originalWidth + dx}px`;
|
| 880 |
+
}
|
| 881 |
if (resizeHandle.includes('w')) {
|
| 882 |
bubble.style.width = `${originalWidth - dx}px`;
|
| 883 |
bubble.style.left = `${originalX + dx}px`;
|
| 884 |
}
|
| 885 |
+
if (resizeHandle.includes('s')) {
|
| 886 |
+
bubble.style.height = `${originalHeight + dy}px`;
|
| 887 |
+
}
|
| 888 |
if (resizeHandle.includes('n')) {
|
| 889 |
bubble.style.height = `${originalHeight - dy}px`;
|
| 890 |
bubble.style.top = `${originalY + dy}px`;
|
| 891 |
}
|
| 892 |
}
|
| 893 |
|
| 894 |
+
function stopResize() {
|
| 895 |
+
isResizing = false;
|
| 896 |
+
}
|
| 897 |
|
| 898 |
function clearSavedState() {
|
| 899 |
if (confirm("Reset all edits?")) {
|
|
|
|
| 921 |
.then(data => {
|
| 922 |
if (data.success) {
|
| 923 |
img.src = `/frames/final/${data.new_filename}?t=${new Date().getTime()}`;
|
| 924 |
+
img.style.objectFit = 'contain'; // FIX: Show full image
|
| 925 |
+
resetPanelTransform(); // FIX: Reset any old zoom/pan
|
| 926 |
} else { alert('Error replacing image: ' + data.error); }
|
| 927 |
img.style.opacity = '1';
|
| 928 |
})
|