tester343 commited on
Commit
84afe3b
·
verified ·
1 Parent(s): 3da224e

Update app_enhanced.py

Browse files
Files changed (1) hide show
  1. 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: -280px; /* Compensate for scale empty space */
382
  }
383
 
384
  /* === 5-PANEL TEMPLATE (2 TOP, 3 BOTTOM) === */
385
- /* Background White for 12px Gutter (1.2% width, 1.7% height of 712px) */
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 Debugger Screenshot:
395
- Row 1 Height: 46.63%
396
- Row 2 Top: 48.45% (Gap = 1.82%)
397
-
398
- Panel 1 (TL): polygon(0% 0%, 59.07% 0%, 51.57% 100%, 0% 100%)
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 46.63% */
 
 
407
  .panel:nth-child(1) {
408
- top: 0; left: 0; height: 46.63%; width: 100%;
409
- clip-path: polygon(0% 0%, 59.07% 0%, 51.57% 100%, 0% 100%);
410
  }
 
411
  .panel:nth-child(2) {
412
- top: 0; left: 0; height: 46.63%; width: 100%;
413
- clip-path: polygon(60.37% 0%, 100% 0%, 100% 100%, 52.87% 100%);
414
  }
415
 
416
- /* --- ROW 2 (BOTTOM) --- Top: 48.45%, Height: 51.55% */
 
 
417
  .panel:nth-child(3) {
418
- top: 48.45%; left: 0; height: 51.55%; width: 100%;
419
- clip-path: polygon(0% 0%, 27.47% 0%, 33.77% 100%, 0% 100%);
420
  }
 
421
  .panel:nth-child(4) {
422
- top: 48.45%; left: 0; height: 51.55%; width: 100%;
423
- clip-path: polygon(28.77% 0%, 60.97% 0%, 67.27% 100%, 35.07% 100%);
424
  }
 
425
  .panel:nth-child(5) {
426
- top: 48.45%; left: 0; height: 51.55%; width: 100%;
427
- clip-path: polygon(62.27% 0%, 100% 0%, 100% 100%, 68.57% 100%);
428
  }
429
 
430
  /* ====================== */
431
 
432
  /*
433
- IMAGE FIT: 'contain' ensures full image is visible (100% fit) without crop.
434
- Gaps are handled by the white background of the panel/grid.
435
  */
436
  .panel img {
437
  width: 100%; height: 100%;
438
- object-fit: contain; /* DEFAULT: Show full image, padded with white */
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-fill { object-fit: fill !important; }
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 'contain' (no crop/zoom, but bars), 'fill' (stretch, no bars), and 'cover' (crop, no bars)
983
  function toggleFitCover() {
984
  if(!selectedPanel) return alert("Select a panel");
985
  const img = selectedPanel.querySelector('img');
986
- const current = img.style.objectFit || 'contain'; // Default if not set
987
- if(current === 'contain') {
988
- img.style.objectFit = 'fill'; // Stretch to fill
989
- } else if(current === 'fill') {
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 for print
1037
- style: { transform: 'none' } // Ensure no transforms for html-to-image
1038
  });
1039
  const a = document.createElement('a');
1040
  a.href = u;
@@ -1046,11 +1030,8 @@ INDEX_HTML = '''
1046
  }
1047
  }
1048
 
1049
- // --- RESTORE EDITOR SCALING AFTER EXPORT ---
1050
- comicPages.forEach(page => {
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'; } }