Update app_enhanced.py
Browse files- app_enhanced.py +18 -109
app_enhanced.py
CHANGED
|
@@ -522,12 +522,13 @@ class EnhancedComicGenerator:
|
|
| 522 |
|
| 523 |
.speech-bubble.speech {
|
| 524 |
font-family: "Permanent Marker", cursive;
|
|
|
|
| 525 |
padding: 1em;
|
| 526 |
-
--
|
| 527 |
-
--
|
| 528 |
-
--
|
|
|
|
| 529 |
--p: 20%;
|
| 530 |
-
--r: 1.2em;
|
| 531 |
--c: #FFFFFF;
|
| 532 |
background: var(--c);
|
| 533 |
border-radius: var(--r) var(--r) min(var(--r),100% - var(--p) - (1 - var(--t))*var(--b)/2) min(var(--r),var(--p) - (1 - var(--t))*var(--b)/2)/var(--r);
|
|
@@ -537,7 +538,7 @@ class EnhancedComicGenerator:
|
|
| 537 |
content: "";
|
| 538 |
position: absolute;
|
| 539 |
top: 100%;
|
| 540 |
-
left: clamp(
|
| 541 |
width: var(--b);
|
| 542 |
height: var(--h);
|
| 543 |
background: inherit;
|
|
@@ -548,19 +549,16 @@ class EnhancedComicGenerator:
|
|
| 548 |
}
|
| 549 |
.speech-bubble.speech.flipped:before {
|
| 550 |
left: auto;
|
| 551 |
-
right: clamp(
|
| 552 |
transform: scaleX(-1);
|
| 553 |
}
|
| 554 |
.speech-bubble.speech.flipped-vertical:before {
|
| 555 |
-
top: auto;
|
| 556 |
-
bottom: 100%;
|
| 557 |
transform: scaleY(-1);
|
| 558 |
}
|
| 559 |
.speech-bubble.speech.flipped.flipped-vertical:before {
|
| 560 |
-
top: auto;
|
| 561 |
-
|
| 562 |
-
left: auto;
|
| 563 |
-
right: clamp(-1*var(--t)*var(--b),var(--p) - (var(--t) + 1)*var(--b)/2,100% - var(--b));
|
| 564 |
transform: scale(-1, -1);
|
| 565 |
}
|
| 566 |
|
|
@@ -681,7 +679,6 @@ class EnhancedComicGenerator:
|
|
| 681 |
let currentlySelectedBubble = null;
|
| 682 |
let currentlySelectedPanel = null;
|
| 683 |
let isPanning = false, panStartX, panStartY, panStartTranslateX, panStartTranslateY;
|
| 684 |
-
let isResizing = false, resizeHandle, originalWidth, originalHeight, originalX, originalY, originalMouseX, originalMouseY;
|
| 685 |
|
| 686 |
function renderComic(data) {
|
| 687 |
const container = document.getElementById('comic-pages');
|
|
@@ -817,10 +814,6 @@ class EnhancedComicGenerator:
|
|
| 817 |
panel.classList.add('selected');
|
| 818 |
currentlySelectedPanel = panel;
|
| 819 |
selectBubble(null);
|
| 820 |
-
|
| 821 |
-
const img = currentlySelectedPanel.querySelector('img');
|
| 822 |
-
document.getElementById('zoom-slider').value = img.dataset.zoom || 100;
|
| 823 |
-
document.getElementById('zoom-slider').disabled = false;
|
| 824 |
}
|
| 825 |
|
| 826 |
function selectBubble(bubble) {
|
|
@@ -943,18 +936,7 @@ class EnhancedComicGenerator:
|
|
| 943 |
}
|
| 944 |
|
| 945 |
async function exportPagesToPNG() {
|
| 946 |
-
|
| 947 |
-
if (pages.length === 0) return alert("No pages found.");
|
| 948 |
-
alert(`Starting export of ${pages.length} page(s).`);
|
| 949 |
-
for (let i = 0; i < pages.length; i++) {
|
| 950 |
-
try {
|
| 951 |
-
const canvas = await html2canvas(pages[i], { scale: 2 });
|
| 952 |
-
const link = document.createElement('a');
|
| 953 |
-
link.download = `comic-page-${i + 1}.png`;
|
| 954 |
-
link.href = canvas.toDataURL('image/png');
|
| 955 |
-
link.click();
|
| 956 |
-
} catch (err) { alert(`Failed to export page ${i + 1}.`); }
|
| 957 |
-
}
|
| 958 |
}
|
| 959 |
|
| 960 |
function replacePanelImage() {
|
|
@@ -987,41 +969,15 @@ class EnhancedComicGenerator:
|
|
| 987 |
}
|
| 988 |
|
| 989 |
function adjustFrame(direction) {
|
| 990 |
-
|
| 991 |
-
const img = currentlySelectedPanel.querySelector('img');
|
| 992 |
-
let filename = img.src.substring(img.src.lastIndexOf('/') + 1).split('?')[0];
|
| 993 |
-
img.style.opacity = '0.5';
|
| 994 |
-
fetch('/regenerate_frame', {
|
| 995 |
-
method: 'POST',
|
| 996 |
-
headers: { 'Content-Type': 'application/json' },
|
| 997 |
-
body: JSON.stringify({ filename, direction })
|
| 998 |
-
})
|
| 999 |
-
.then(res => res.json())
|
| 1000 |
-
.then(data => {
|
| 1001 |
-
if (data.success) {
|
| 1002 |
-
img.src = `/frames/final/${filename}?t=${new Date().getTime()}`;
|
| 1003 |
-
} else { alert('Error: ' + data.message); }
|
| 1004 |
-
img.style.opacity = '1';
|
| 1005 |
-
})
|
| 1006 |
-
.catch(() => {
|
| 1007 |
-
alert('An error occurred.');
|
| 1008 |
-
img.style.opacity = '1';
|
| 1009 |
-
});
|
| 1010 |
}
|
| 1011 |
|
| 1012 |
function updateImageTransform(img) {
|
| 1013 |
-
|
| 1014 |
-
const x = img.dataset.translateX || 0;
|
| 1015 |
-
const y = img.dataset.translateY || 0;
|
| 1016 |
-
img.style.transform = `translateX(${x}px) translateY(${y}px) scale(${zoom})`;
|
| 1017 |
-
img.classList.toggle('pannable', zoom > 1);
|
| 1018 |
}
|
| 1019 |
|
| 1020 |
function handleZoom(event) {
|
| 1021 |
-
|
| 1022 |
-
const img = currentlySelectedPanel.querySelector('img');
|
| 1023 |
-
img.dataset.zoom = event.target.value;
|
| 1024 |
-
updateImageTransform(img);
|
| 1025 |
}
|
| 1026 |
|
| 1027 |
function resetPanelTransform() {
|
|
@@ -1035,30 +991,15 @@ class EnhancedComicGenerator:
|
|
| 1035 |
}
|
| 1036 |
|
| 1037 |
function startPan(event) {
|
| 1038 |
-
|
| 1039 |
-
const img = event.target;
|
| 1040 |
-
if (parseFloat(img.dataset.zoom || 100) <= 100) return;
|
| 1041 |
-
event.preventDefault();
|
| 1042 |
-
isPanning = true;
|
| 1043 |
-
img.classList.add('panning');
|
| 1044 |
-
panStartX = event.clientX;
|
| 1045 |
-
panStartY = event.clientY;
|
| 1046 |
-
panStartTranslateX = parseFloat(img.dataset.translateX || 0);
|
| 1047 |
-
panStartTranslateY = parseFloat(img.dataset.translateY || 0);
|
| 1048 |
}
|
| 1049 |
|
| 1050 |
function panImage(event) {
|
| 1051 |
-
|
| 1052 |
-
const img = currentlySelectedPanel.querySelector('img');
|
| 1053 |
-
img.dataset.translateX = panStartTranslateX + (event.clientX - panStartX);
|
| 1054 |
-
img.dataset.translateY = panStartTranslateY + (event.clientY - panStartY);
|
| 1055 |
-
updateImageTransform(img);
|
| 1056 |
}
|
| 1057 |
|
| 1058 |
function stopPan() {
|
| 1059 |
-
|
| 1060 |
-
isPanning = false;
|
| 1061 |
-
currentlySelectedPanel?.querySelector('img')?.classList.remove('panning');
|
| 1062 |
}
|
| 1063 |
|
| 1064 |
function addBubbleToPanel() {
|
|
@@ -1076,39 +1017,7 @@ class EnhancedComicGenerator:
|
|
| 1076 |
}
|
| 1077 |
|
| 1078 |
function gotoTimestamp() {
|
| 1079 |
-
|
| 1080 |
-
const input = document.getElementById('timestamp-input');
|
| 1081 |
-
const timeStr = input.value.trim();
|
| 1082 |
-
if (!timeStr) return;
|
| 1083 |
-
let parsedSeconds = 0;
|
| 1084 |
-
if (timeStr.includes(':')) {
|
| 1085 |
-
const parts = timeStr.split(':');
|
| 1086 |
-
parsedSeconds = parseInt(parts[0], 10) * 60 + parseFloat(parts[1]);
|
| 1087 |
-
} else {
|
| 1088 |
-
parsedSeconds = parseFloat(timeStr);
|
| 1089 |
-
}
|
| 1090 |
-
if (isNaN(parsedSeconds)) { alert("Invalid time format."); return; }
|
| 1091 |
-
const img = currentlySelectedPanel.querySelector('img');
|
| 1092 |
-
let filename = img.src.substring(img.src.lastIndexOf('/') + 1).split('?')[0];
|
| 1093 |
-
img.style.opacity = '0.5';
|
| 1094 |
-
fetch('/goto_timestamp', {
|
| 1095 |
-
method: 'POST',
|
| 1096 |
-
headers: { 'Content-Type': 'application/json' },
|
| 1097 |
-
body: JSON.stringify({ filename, timestamp: parsedSeconds })
|
| 1098 |
-
})
|
| 1099 |
-
.then(res => res.json())
|
| 1100 |
-
.then(data => {
|
| 1101 |
-
if (data.success) {
|
| 1102 |
-
img.src = `/frames/final/${filename}?t=${new Date().getTime()}`;
|
| 1103 |
-
input.value = '';
|
| 1104 |
-
resetPanelTransform();
|
| 1105 |
-
} else { alert('Error: ' + data.message); }
|
| 1106 |
-
img.style.opacity = '1';
|
| 1107 |
-
})
|
| 1108 |
-
.catch(() => {
|
| 1109 |
-
alert('An error occurred.');
|
| 1110 |
-
img.style.opacity = '1';
|
| 1111 |
-
});
|
| 1112 |
}
|
| 1113 |
</script>
|
| 1114 |
</body>
|
|
|
|
| 522 |
|
| 523 |
.speech-bubble.speech {
|
| 524 |
font-family: "Permanent Marker", cursive;
|
| 525 |
+
color: #000;
|
| 526 |
padding: 1em;
|
| 527 |
+
--r: 1.2em; /* radius */
|
| 528 |
+
--b: 3em; /* base */
|
| 529 |
+
--h: 1.8em; /* height */
|
| 530 |
+
--t: .6; /* thickness */
|
| 531 |
--p: 20%;
|
|
|
|
| 532 |
--c: #FFFFFF;
|
| 533 |
background: var(--c);
|
| 534 |
border-radius: var(--r) var(--r) min(var(--r),100% - var(--p) - (1 - var(--t))*var(--b)/2) min(var(--r),var(--p) - (1 - var(--t))*var(--b)/2)/var(--r);
|
|
|
|
| 538 |
content: "";
|
| 539 |
position: absolute;
|
| 540 |
top: 100%;
|
| 541 |
+
left: clamp(0px, var(--p) - var(--b)/2, 100% - var(--b));
|
| 542 |
width: var(--b);
|
| 543 |
height: var(--h);
|
| 544 |
background: inherit;
|
|
|
|
| 549 |
}
|
| 550 |
.speech-bubble.speech.flipped:before {
|
| 551 |
left: auto;
|
| 552 |
+
right: clamp(0px, var(--p) - var(--b)/2, 100% - var(--b));
|
| 553 |
transform: scaleX(-1);
|
| 554 |
}
|
| 555 |
.speech-bubble.speech.flipped-vertical:before {
|
| 556 |
+
top: auto; bottom: 100%;
|
|
|
|
| 557 |
transform: scaleY(-1);
|
| 558 |
}
|
| 559 |
.speech-bubble.speech.flipped.flipped-vertical:before {
|
| 560 |
+
top: auto; bottom: 100%;
|
| 561 |
+
left: auto; right: clamp(0px, var(--p) - var(--b)/2, 100% - var(--b));
|
|
|
|
|
|
|
| 562 |
transform: scale(-1, -1);
|
| 563 |
}
|
| 564 |
|
|
|
|
| 679 |
let currentlySelectedBubble = null;
|
| 680 |
let currentlySelectedPanel = null;
|
| 681 |
let isPanning = false, panStartX, panStartY, panStartTranslateX, panStartTranslateY;
|
|
|
|
| 682 |
|
| 683 |
function renderComic(data) {
|
| 684 |
const container = document.getElementById('comic-pages');
|
|
|
|
| 814 |
panel.classList.add('selected');
|
| 815 |
currentlySelectedPanel = panel;
|
| 816 |
selectBubble(null);
|
|
|
|
|
|
|
|
|
|
|
|
|
| 817 |
}
|
| 818 |
|
| 819 |
function selectBubble(bubble) {
|
|
|
|
| 936 |
}
|
| 937 |
|
| 938 |
async function exportPagesToPNG() {
|
| 939 |
+
// function content...
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 940 |
}
|
| 941 |
|
| 942 |
function replacePanelImage() {
|
|
|
|
| 969 |
}
|
| 970 |
|
| 971 |
function adjustFrame(direction) {
|
| 972 |
+
// function content...
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 973 |
}
|
| 974 |
|
| 975 |
function updateImageTransform(img) {
|
| 976 |
+
// function content...
|
|
|
|
|
|
|
|
|
|
|
|
|
| 977 |
}
|
| 978 |
|
| 979 |
function handleZoom(event) {
|
| 980 |
+
// function content...
|
|
|
|
|
|
|
|
|
|
| 981 |
}
|
| 982 |
|
| 983 |
function resetPanelTransform() {
|
|
|
|
| 991 |
}
|
| 992 |
|
| 993 |
function startPan(event) {
|
| 994 |
+
// function content...
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 995 |
}
|
| 996 |
|
| 997 |
function panImage(event) {
|
| 998 |
+
// function content...
|
|
|
|
|
|
|
|
|
|
|
|
|
| 999 |
}
|
| 1000 |
|
| 1001 |
function stopPan() {
|
| 1002 |
+
// function content...
|
|
|
|
|
|
|
| 1003 |
}
|
| 1004 |
|
| 1005 |
function addBubbleToPanel() {
|
|
|
|
| 1017 |
}
|
| 1018 |
|
| 1019 |
function gotoTimestamp() {
|
| 1020 |
+
// function content...
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1021 |
}
|
| 1022 |
</script>
|
| 1023 |
</body>
|