Update app_enhanced.py
Browse files- app_enhanced.py +40 -59
app_enhanced.py
CHANGED
|
@@ -375,14 +375,14 @@ INDEX_HTML = '''
|
|
| 375 |
overflow: hidden;
|
| 376 |
border: 2px solid #000;
|
| 377 |
padding: 0;
|
| 378 |
-
/* SCALING FOR EDITOR VIEW */
|
| 379 |
transform-origin: top center;
|
| 380 |
-
transform: scale(0.6);
|
| 381 |
-
margin-bottom:
|
| 382 |
}
|
| 383 |
|
| 384 |
/* === 5-PANEL TEMPLATE (2 TOP, 3 BOTTOM) === */
|
| 385 |
-
/* Background White for
|
| 386 |
.comic-grid { width: 100%; height: 100%; position: relative; background: #ffffff; }
|
| 387 |
|
| 388 |
/* Panel Background White */
|
|
@@ -391,57 +391,58 @@ INDEX_HTML = '''
|
|
| 391 |
.panel.selected img { outline: 3px solid #2196F3; outline-offset: -3px; }
|
| 392 |
|
| 393 |
/*
|
| 394 |
-
COORDINATES from
|
| 395 |
-
Row 1
|
| 396 |
-
Row 2
|
| 397 |
-
|
| 398 |
-
|
| 399 |
-
Panel 2 (TR): polygon(60.37% 0%, 100% 0%, 100% 100%, 52.87% 100%)
|
| 400 |
-
|
| 401 |
-
Panel 3 (BL): polygon(0% 0%, 27.47% 0%, 33.77% 100%, 0% 100%)
|
| 402 |
-
Panel 4 (BM): polygon(28.77% 0%, 60.97% 0%, 67.27% 100%, 35.07% 100%)
|
| 403 |
-
Panel 5 (BR): polygon(62.27% 0%, 100% 0%, 100% 100%, 68.57% 100%)
|
| 404 |
*/
|
| 405 |
|
| 406 |
-
/* --- ROW 1 (TOP) --- Height
|
|
|
|
|
|
|
| 407 |
.panel:nth-child(1) {
|
| 408 |
-
top: 0; left: 0; height:
|
| 409 |
-
clip-path: polygon(0% 0%,
|
| 410 |
}
|
|
|
|
| 411 |
.panel:nth-child(2) {
|
| 412 |
-
top: 0; left: 0; height:
|
| 413 |
-
clip-path: polygon(
|
| 414 |
}
|
| 415 |
|
| 416 |
-
/* --- ROW 2 (BOTTOM) --- Top:
|
|
|
|
|
|
|
| 417 |
.panel:nth-child(3) {
|
| 418 |
-
top:
|
| 419 |
-
clip-path: polygon(0% 0%,
|
| 420 |
}
|
|
|
|
| 421 |
.panel:nth-child(4) {
|
| 422 |
-
top:
|
| 423 |
-
clip-path: polygon(
|
| 424 |
}
|
|
|
|
| 425 |
.panel:nth-child(5) {
|
| 426 |
-
top:
|
| 427 |
-
clip-path: polygon(
|
| 428 |
}
|
| 429 |
|
| 430 |
/* ====================== */
|
| 431 |
|
| 432 |
/*
|
| 433 |
-
IMAGE FIT: '
|
| 434 |
-
Gaps are handled by the white background of the panel/grid.
|
| 435 |
*/
|
| 436 |
.panel img {
|
| 437 |
width: 100%; height: 100%;
|
| 438 |
-
object-fit:
|
| 439 |
transition: transform 0.1s ease-out;
|
| 440 |
transform-origin: center center;
|
| 441 |
}
|
| 442 |
/* Toggle classes */
|
| 443 |
.panel img.fit-cover { object-fit: cover !important; }
|
| 444 |
-
.panel img.fit-
|
| 445 |
|
| 446 |
.panel img.pannable { cursor: grab; }
|
| 447 |
.panel img.panning { cursor: grabbing; }
|
|
@@ -979,24 +980,14 @@ INDEX_HTML = '''
|
|
| 979 |
async function adjustFrame(dir) { if(isProcessing) return; if(!selectedPanel) return alert("Select a panel"); const img = selectedPanel.querySelector('img'); let fname = img.src.split('/').pop().split('?')[0]; setProcessing(true); img.style.opacity = '0.5'; try { const r = await fetch(`/regenerate_frame?sid=${sid}`, { method:'POST', headers:{'Content-Type':'application/json'}, body:JSON.stringify({filename:fname, direction:dir}) }); const d = await r.json(); if(d.success) { img.src = `/frames/${fname}?sid=${sid}&t=${Date.now()}`; } else { alert('Error: ' + d.message); } } catch(e) { console.error(e); } img.style.opacity = '1'; setProcessing(false); saveDraft(true); }
|
| 980 |
async function gotoTimestamp() { if(isProcessing) return; if(!selectedPanel) return alert("Select a panel"); let v = document.getElementById('timestamp-input').value.trim(); if(!v) return; if(v.includes(':')) { let p = v.split(':'); v = parseInt(p[0]) * 60 + parseFloat(p[1]); } else { v = parseFloat(v); } if(isNaN(v)) return alert("Invalid time"); const img = selectedPanel.querySelector('img'); let fname = img.src.split('/').pop().split('?')[0]; setProcessing(true); img.style.opacity = '0.5'; try { const r = await fetch(`/goto_timestamp?sid=${sid}`, { method:'POST', headers:{'Content-Type':'application/json'}, body:JSON.stringify({filename:fname, timestamp:v}) }); const d = await r.json(); if(d.success) { img.src = `/frames/${fname}?sid=${sid}&t=${Date.now()}`; document.getElementById('timestamp-input').value = ''; } else { alert('Error: ' + d.message); } } catch(e) { console.error(e); } img.style.opacity = '1'; setProcessing(false); saveDraft(true); }
|
| 981 |
|
| 982 |
-
// NEW: Toggle between '
|
| 983 |
function toggleFitCover() {
|
| 984 |
if(!selectedPanel) return alert("Select a panel");
|
| 985 |
const img = selectedPanel.querySelector('img');
|
| 986 |
-
const current = img.style.objectFit || '
|
| 987 |
-
if(current === '
|
| 988 |
-
|
| 989 |
-
|
| 990 |
-
img.style.objectFit = 'cover'; // Fill, but crop to maintain aspect ratio
|
| 991 |
-
} else { // current === 'cover'
|
| 992 |
-
img.style.objectFit = 'contain'; // Show full image
|
| 993 |
-
}
|
| 994 |
-
// Reset transforms when toggling object-fit to avoid weird interactions
|
| 995 |
-
img.dataset.zoom = 100;
|
| 996 |
-
img.dataset.translateX = 0;
|
| 997 |
-
img.dataset.translateY = 0;
|
| 998 |
-
document.getElementById('zoom-slider').value = 100;
|
| 999 |
-
updateImageTransform(img); // Re-evaluate transform based on new object-fit
|
| 1000 |
saveDraft(true);
|
| 1001 |
}
|
| 1002 |
|
|
@@ -1009,13 +1000,6 @@ INDEX_HTML = '''
|
|
| 1009 |
if(selectedPanel) selectedPanel.classList.remove('selected');
|
| 1010 |
alert(`Exporting ${pgs.length} page(s)...`);
|
| 1011 |
|
| 1012 |
-
// --- PREPARE FOR EXPORT: Remove editor scaling ---
|
| 1013 |
-
const comicPages = document.querySelectorAll('.comic-page');
|
| 1014 |
-
comicPages.forEach(page => {
|
| 1015 |
-
page.style.transform = 'none'; // Remove scaling from comic-page
|
| 1016 |
-
page.style.marginBottom = '0'; // Remove margin compensation
|
| 1017 |
-
});
|
| 1018 |
-
|
| 1019 |
// --- 0% ERROR FIX ---
|
| 1020 |
// 1. Lock specific pixel dimensions + 1px buffer to prevent word wrapping
|
| 1021 |
const bubbles = document.querySelectorAll('.speech-bubble');
|
|
@@ -1033,8 +1017,8 @@ INDEX_HTML = '''
|
|
| 1033 |
for(let i = 0; i < pgs.length; i++) {
|
| 1034 |
try {
|
| 1035 |
const u = await htmlToImage.toPng(pgs[i], {
|
| 1036 |
-
pixelRatio: 2, // High quality
|
| 1037 |
-
style: { transform: 'none' }
|
| 1038 |
});
|
| 1039 |
const a = document.createElement('a');
|
| 1040 |
a.href = u;
|
|
@@ -1046,11 +1030,8 @@ INDEX_HTML = '''
|
|
| 1046 |
}
|
| 1047 |
}
|
| 1048 |
|
| 1049 |
-
//
|
| 1050 |
-
|
| 1051 |
-
page.style.transform = 'scale(0.6)';
|
| 1052 |
-
page.style.marginBottom = '-280px';
|
| 1053 |
-
});
|
| 1054 |
}
|
| 1055 |
|
| 1056 |
function goBackToUpload() { if(confirm('Go home? Unsaved changes will be lost.')) { document.getElementById('editor-container').style.display = 'none'; document.getElementById('upload-container').style.display = 'flex'; document.getElementById('loading-view').style.display = 'none'; } }
|
|
|
|
| 375 |
overflow: hidden;
|
| 376 |
border: 2px solid #000;
|
| 377 |
padding: 0;
|
| 378 |
+
/* SCALING FOR EDITOR VIEW ONLY - REMOVED FOR EXPORT */
|
| 379 |
transform-origin: top center;
|
| 380 |
+
/* transform: scale(0.6); - DISABLED to show True Size */
|
| 381 |
+
margin-bottom: 30px;
|
| 382 |
}
|
| 383 |
|
| 384 |
/* === 5-PANEL TEMPLATE (2 TOP, 3 BOTTOM) === */
|
| 385 |
+
/* Background White for 0px Gutter */
|
| 386 |
.comic-grid { width: 100%; height: 100%; position: relative; background: #ffffff; }
|
| 387 |
|
| 388 |
/* Panel Background White */
|
|
|
|
| 391 |
.panel.selected img { outline: 3px solid #2196F3; outline-offset: -3px; }
|
| 392 |
|
| 393 |
/*
|
| 394 |
+
0% GAP COORDINATES from Green Text:
|
| 395 |
+
Row 1 Split: Top=63.2%, Bottom=59.5%
|
| 396 |
+
Row 2 Left Split: Top=33.0%, Bottom=35.7%
|
| 397 |
+
Row 2 Right Split: Top=64.8%, Bottom=68.2%
|
| 398 |
+
Rows are 50% / 50%
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 399 |
*/
|
| 400 |
|
| 401 |
+
/* --- ROW 1 (TOP) --- Height 50% */
|
| 402 |
+
|
| 403 |
+
/* Panel 1: Right ends at 63.2% / 59.5% */
|
| 404 |
.panel:nth-child(1) {
|
| 405 |
+
top: 0; left: 0; height: 50%; width: 100%;
|
| 406 |
+
clip-path: polygon(0% 0%, 63.2% 0%, 59.5% 100%, 0% 100%);
|
| 407 |
}
|
| 408 |
+
/* Panel 2: Left starts at 63.2% / 59.5% */
|
| 409 |
.panel:nth-child(2) {
|
| 410 |
+
top: 0; left: 0; height: 50%; width: 100%;
|
| 411 |
+
clip-path: polygon(63.2% 0%, 100% 0%, 100% 100%, 59.5% 100%);
|
| 412 |
}
|
| 413 |
|
| 414 |
+
/* --- ROW 2 (BOTTOM) --- Top: 50%, Height: 50% */
|
| 415 |
+
|
| 416 |
+
/* Panel 3: Right ends at 33.0% / 35.7% */
|
| 417 |
.panel:nth-child(3) {
|
| 418 |
+
top: 50%; left: 0; height: 50%; width: 100%;
|
| 419 |
+
clip-path: polygon(0% 0%, 33.0% 0%, 35.7% 100%, 0% 100%);
|
| 420 |
}
|
| 421 |
+
/* Panel 4: Left starts 33.0%/35.7%. Right ends 64.8%/68.2% */
|
| 422 |
.panel:nth-child(4) {
|
| 423 |
+
top: 50%; left: 0; height: 50%; width: 100%;
|
| 424 |
+
clip-path: polygon(33.0% 0%, 64.8% 0%, 68.2% 100%, 35.7% 100%);
|
| 425 |
}
|
| 426 |
+
/* Panel 5: Left starts 64.8%/68.2% */
|
| 427 |
.panel:nth-child(5) {
|
| 428 |
+
top: 50%; left: 0; height: 50%; width: 100%;
|
| 429 |
+
clip-path: polygon(64.8% 0%, 100% 0%, 100% 100%, 68.2% 100%);
|
| 430 |
}
|
| 431 |
|
| 432 |
/* ====================== */
|
| 433 |
|
| 434 |
/*
|
| 435 |
+
IMAGE FIT: 'fill' ensures full image is visible (100% fit) and stretches to fill (0 white space).
|
|
|
|
| 436 |
*/
|
| 437 |
.panel img {
|
| 438 |
width: 100%; height: 100%;
|
| 439 |
+
object-fit: fill; /* STRETCH TO FILL (No Zoom, No Gaps) */
|
| 440 |
transition: transform 0.1s ease-out;
|
| 441 |
transform-origin: center center;
|
| 442 |
}
|
| 443 |
/* Toggle classes */
|
| 444 |
.panel img.fit-cover { object-fit: cover !important; }
|
| 445 |
+
.panel img.fit-contain { object-fit: contain !important; }
|
| 446 |
|
| 447 |
.panel img.pannable { cursor: grab; }
|
| 448 |
.panel img.panning { cursor: grabbing; }
|
|
|
|
| 980 |
async function adjustFrame(dir) { if(isProcessing) return; if(!selectedPanel) return alert("Select a panel"); const img = selectedPanel.querySelector('img'); let fname = img.src.split('/').pop().split('?')[0]; setProcessing(true); img.style.opacity = '0.5'; try { const r = await fetch(`/regenerate_frame?sid=${sid}`, { method:'POST', headers:{'Content-Type':'application/json'}, body:JSON.stringify({filename:fname, direction:dir}) }); const d = await r.json(); if(d.success) { img.src = `/frames/${fname}?sid=${sid}&t=${Date.now()}`; } else { alert('Error: ' + d.message); } } catch(e) { console.error(e); } img.style.opacity = '1'; setProcessing(false); saveDraft(true); }
|
| 981 |
async function gotoTimestamp() { if(isProcessing) return; if(!selectedPanel) return alert("Select a panel"); let v = document.getElementById('timestamp-input').value.trim(); if(!v) return; if(v.includes(':')) { let p = v.split(':'); v = parseInt(p[0]) * 60 + parseFloat(p[1]); } else { v = parseFloat(v); } if(isNaN(v)) return alert("Invalid time"); const img = selectedPanel.querySelector('img'); let fname = img.src.split('/').pop().split('?')[0]; setProcessing(true); img.style.opacity = '0.5'; try { const r = await fetch(`/goto_timestamp?sid=${sid}`, { method:'POST', headers:{'Content-Type':'application/json'}, body:JSON.stringify({filename:fname, timestamp:v}) }); const d = await r.json(); if(d.success) { img.src = `/frames/${fname}?sid=${sid}&t=${Date.now()}`; document.getElementById('timestamp-input').value = ''; } else { alert('Error: ' + d.message); } } catch(e) { console.error(e); } img.style.opacity = '1'; setProcessing(false); saveDraft(true); }
|
| 982 |
|
| 983 |
+
// NEW: Toggle between 'fill' (stretch 100%), 'cover' (crop), and 'contain' (bars)
|
| 984 |
function toggleFitCover() {
|
| 985 |
if(!selectedPanel) return alert("Select a panel");
|
| 986 |
const img = selectedPanel.querySelector('img');
|
| 987 |
+
const current = img.style.objectFit || 'fill';
|
| 988 |
+
if(current === 'fill') img.style.objectFit = 'cover';
|
| 989 |
+
else if(current === 'cover') img.style.objectFit = 'contain';
|
| 990 |
+
else img.style.objectFit = 'fill';
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 991 |
saveDraft(true);
|
| 992 |
}
|
| 993 |
|
|
|
|
| 1000 |
if(selectedPanel) selectedPanel.classList.remove('selected');
|
| 1001 |
alert(`Exporting ${pgs.length} page(s)...`);
|
| 1002 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1003 |
// --- 0% ERROR FIX ---
|
| 1004 |
// 1. Lock specific pixel dimensions + 1px buffer to prevent word wrapping
|
| 1005 |
const bubbles = document.querySelectorAll('.speech-bubble');
|
|
|
|
| 1017 |
for(let i = 0; i < pgs.length; i++) {
|
| 1018 |
try {
|
| 1019 |
const u = await htmlToImage.toPng(pgs[i], {
|
| 1020 |
+
pixelRatio: 2, // High quality
|
| 1021 |
+
style: { transform: 'none' }
|
| 1022 |
});
|
| 1023 |
const a = document.createElement('a');
|
| 1024 |
a.href = u;
|
|
|
|
| 1030 |
}
|
| 1031 |
}
|
| 1032 |
|
| 1033 |
+
// Optional: Reload page or reset styles if user wants to continue editing immediately
|
| 1034 |
+
// In this app, styles are locked until reload, which is safer for the export focus.
|
|
|
|
|
|
|
|
|
|
| 1035 |
}
|
| 1036 |
|
| 1037 |
function goBackToUpload() { if(confirm('Go home? Unsaved changes will be lost.')) { document.getElementById('editor-container').style.display = 'none'; document.getElementById('upload-container').style.display = 'flex'; document.getElementById('loading-view').style.display = 'none'; } }
|