jhh6576 commited on
Commit
20377b5
·
verified ·
1 Parent(s): a9d8f9f

Update app_enhanced.py

Browse files
Files changed (1) hide show
  1. app_enhanced.py +40 -67
app_enhanced.py CHANGED
@@ -522,7 +522,8 @@ class EnhancedComicGenerator:
522
  <head>
523
  <meta charset="UTF-8">
524
  <title>Comic Editor</title>
525
- <script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js"></script>
 
526
  <link rel="preconnect" href="https://fonts.googleapis.com">
527
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
528
  <link href="https://fonts.googleapis.com/css2?family=Bangers&family=Comic+Neue:wght@700&family=Gloria+Hallelujah&family=Lato&display=swap" rel="stylesheet">
@@ -538,110 +539,79 @@ class EnhancedComicGenerator:
538
  .panel img { width: 100%; height: 100%; object-fit: cover; object-position: center; transition: transform 0.1s ease-out; }
539
  .panel img.pannable { cursor: grab; }
540
  .panel img.panning { cursor: grabbing; }
541
-
542
- /* General Bubble Container */
543
- .speech-bubble {
544
- position: absolute;
545
- display: flex;
546
- justify-content: center;
547
- align-items: center;
548
- width: 150px;
549
- height: 80px;
550
- min-width: 50px;
551
- min-height: 30px;
552
- box-sizing: border-box;
553
- z-index: 10;
554
- cursor: move;
555
- overflow: visible;
556
- font-size: 13px;
557
- font-weight: bold;
558
- text-align: center;
559
- font-family: 'Comic Neue', cursive;
560
- }
561
- .bubble-text { padding: 0.8em; word-wrap: break-word; position: relative; z-index: 5; }
562
  .speech-bubble.selected { outline: 2px dashed #4CAF50; }
563
  .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; }
564
 
565
- /* <<< USER REQUESTED STYLE #5 "SHARK FIN" (EXPORT FIXED) >>> */
 
 
566
  .speech-bubble.speech {
567
- /* Variables defined inline via slider/JS */
568
  --b: 3em; /* tail base width */
569
  --h: 1.8em; /* tail height */
570
- --t: 0.6; /* thickness factor (0 to 1) */
571
- --p: var(--tail-pos, 50%); /* Slider position */
572
- --r: 1.2em; /* Border Radius */
573
-
574
- /* Colors */
575
  --c: var(--bubble-fill-color, #4ECDC4);
 
576
  background: var(--c);
577
  color: var(--bubble-text-color, #fff);
578
  padding: 1em;
 
579
 
580
- /* The Exact Body Shape from user request */
581
- border-radius: var(--r) var(--r)
582
- min(var(--r), calc(100% - var(--p) - (1 - var(--t)) * var(--b) / 2))
583
- min(var(--r), calc(var(--p) - (1 - var(--t)) * var(--b) / 2))
584
- / var(--r);
585
-
586
- position: absolute;
587
  }
588
 
589
- /*
590
- FIX FOR EXPORTING:
591
- We convert the mask into a "RADIAL GRADIENT BACKGROUND".
592
- This creates the EXACT same concave curve shape, but it is supported 100% by html2canvas.
593
- */
594
  .speech-bubble.speech:before {
595
  content: "";
596
  position: absolute;
597
  width: var(--b);
598
  height: var(--h);
599
-
600
- /* The Magic Gradient that creates the fin shape */
601
- background: radial-gradient(100% 100% at 100% 0, transparent calc(var(--t) * 100% - 1px), var(--c) calc(var(--t) * 100%));
602
-
603
  border-bottom-left-radius: 100%;
604
- pointer-events: none;
605
  z-index: 1;
 
 
 
 
 
606
  }
607
 
608
- /* BOTTOM TAIL (Standard) */
609
  .speech-bubble.speech.tail-bottom:before {
610
- top: 99%; /* 1% overlap to prevent thin line gaps */
611
- left: clamp(0%, calc(var(--p) - (1 - var(--t)) * var(--b) / 2), calc(100% - (1 - var(--t)) * var(--b)));
612
  }
613
 
614
- /* TOP TAIL (Flipped) */
615
  .speech-bubble.speech.tail-top {
616
- /* Flip border radius for top */
617
- border-radius: min(var(--r), calc(var(--p) - (1 - var(--t)) * var(--b) / 2))
618
- min(var(--r), calc(100% - var(--p) - (1 - var(--t)) * var(--b) / 2))
619
- var(--r) var(--r) / var(--r);
620
  }
621
  .speech-bubble.speech.tail-top:before {
622
- bottom: 99%;
623
- left: clamp(0%, calc(var(--p) - (1 - var(--t)) * var(--b) / 2), calc(100% - (1 - var(--t)) * var(--b)));
624
  transform: scaleY(-1);
625
  }
626
 
627
- /* LEFT TAIL (Rotated) */
628
  .speech-bubble.speech.tail-left {
629
  border-radius: var(--r);
630
  }
631
  .speech-bubble.speech.tail-left:before {
632
- right: 99%;
633
- top: clamp(0%, calc(var(--p) - (1 - var(--t)) * var(--b) / 2), calc(100% - (1 - var(--t)) * var(--b)));
634
  transform: rotate(90deg);
635
  transform-origin: top right;
636
  }
637
 
638
- /* RIGHT TAIL (Rotated) */
639
  .speech-bubble.speech.tail-right {
640
- border-radius: var(--r);
641
  }
642
  .speech-bubble.speech.tail-right:before {
643
- left: 99%;
644
- top: clamp(0%, calc(var(--p) - (1 - var(--t)) * var(--b) / 2), calc(100% - (1 - var(--t)) * var(--b)));
645
  transform: rotate(-90deg);
646
  transform-origin: top left;
647
  }
@@ -1090,12 +1060,16 @@ class EnhancedComicGenerator:
1090
  alert(`Starting export of ${pages.length} page(s).`);
1091
  for (let i = 0; i < pages.length; i++) {
1092
  try {
1093
- const canvas = await html2canvas(pages[i], { scale: 2 });
 
1094
  const link = document.createElement('a');
1095
  link.download = `comic-page-${i + 1}.png`;
1096
- link.href = canvas.toDataURL('image/png');
1097
  link.click();
1098
- } catch (err) { alert(`Failed to export page ${i + 1}.`); }
 
 
 
1099
  }
1100
 
1101
  // 2. UNFREEZE DIMENSIONS (Restore normal behavior)
@@ -1263,7 +1237,6 @@ class EnhancedComicGenerator:
1263
  </script>
1264
  </body>
1265
  </html>'''
1266
- # --- CORRECTED INDENTATION ---
1267
  with open(os.path.join(self.output_dir, 'page.html'), 'w', encoding='utf-8') as f:
1268
  f.write(template_html)
1269
  print("📄 Template files copied successfully!")
 
522
  <head>
523
  <meta charset="UTF-8">
524
  <title>Comic Editor</title>
525
+ <!-- Use dom-to-image for better mask support on export -->
526
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/dom-to-image/2.6.0/dom-to-image.min.js"></script>
527
  <link rel="preconnect" href="https://fonts.googleapis.com">
528
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
529
  <link href="https://fonts.googleapis.com/css2?family=Bangers&family=Comic+Neue:wght@700&family=Gloria+Hallelujah&family=Lato&display=swap" rel="stylesheet">
 
539
  .panel img { width: 100%; height: 100%; object-fit: cover; object-position: center; transition: transform 0.1s ease-out; }
540
  .panel img.pannable { cursor: grab; }
541
  .panel img.panning { cursor: grabbing; }
542
+ .speech-bubble { position: absolute; display: flex; justify-content: center; align-items: center; width: 150px; height: 80px; min-width: 50px; min-height: 30px; box-sizing: border-box; z-index: 10; cursor: move; overflow: visible; font-size: 13px; font-weight: bold; text-align: center; font-family: 'Comic Neue', cursive; }
543
+ .bubble-text { padding: 0.8em; word-wrap: break-word; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
544
  .speech-bubble.selected { outline: 2px dashed #4CAF50; }
545
  .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; }
546
 
547
+ /* <<< USER REQUESTED EXACT CSS (#5 Shape) with Mask >>> */
548
+ /* NOTE: dom-to-image is required for this to export correctly. */
549
+
550
  .speech-bubble.speech {
 
551
  --b: 3em; /* tail base width */
552
  --h: 1.8em; /* tail height */
553
+ --t: .6; /* thickness */
554
+ --p: var(--tail-pos, 50%);
555
+ --r: 1.2em; /* radius */
 
 
556
  --c: var(--bubble-fill-color, #4ECDC4);
557
+
558
  background: var(--c);
559
  color: var(--bubble-text-color, #fff);
560
  padding: 1em;
561
+ position: absolute;
562
 
563
+ 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);
 
 
 
 
 
 
564
  }
565
 
 
 
 
 
 
566
  .speech-bubble.speech:before {
567
  content: "";
568
  position: absolute;
569
  width: var(--b);
570
  height: var(--h);
571
+ background: inherit;
 
 
 
572
  border-bottom-left-radius: 100%;
 
573
  z-index: 1;
574
+ pointer-events: none;
575
+
576
+ /* The specific mask requested */
577
+ -webkit-mask: radial-gradient(calc(var(--t)*100%) 105% at 100% 0,#0000 99%,#000 101%);
578
+ mask: radial-gradient(calc(var(--t)*100%) 105% at 100% 0,#0000 99%,#000 101%);
579
  }
580
 
581
+ /* BOTTOM TAIL (Default) */
582
  .speech-bubble.speech.tail-bottom:before {
583
+ top: 100%;
584
+ left: clamp(0%,var(--p) - (1 - var(--t))*var(--b)/2,100% - (1 - var(--t))*var(--b));
585
  }
586
 
587
+ /* TOP TAIL (Flip Vertical) */
588
  .speech-bubble.speech.tail-top {
589
+ border-radius: min(var(--r),var(--p) - (1 - var(--t))*var(--b)/2) min(var(--r),100% - var(--p) - (1 - var(--t))*var(--b)/2) var(--r) var(--r)/var(--r);
 
 
 
590
  }
591
  .speech-bubble.speech.tail-top:before {
592
+ bottom: 100%;
593
+ left: clamp(0%,var(--p) - (1 - var(--t))*var(--b)/2,100% - (1 - var(--t))*var(--b));
594
  transform: scaleY(-1);
595
  }
596
 
597
+ /* LEFT TAIL (Rotate 90) */
598
  .speech-bubble.speech.tail-left {
599
  border-radius: var(--r);
600
  }
601
  .speech-bubble.speech.tail-left:before {
602
+ right: 100%;
603
+ top: clamp(0%,var(--p) - (1 - var(--t))*var(--b)/2,100% - (1 - var(--t))*var(--b));
604
  transform: rotate(90deg);
605
  transform-origin: top right;
606
  }
607
 
608
+ /* RIGHT TAIL (Rotate -90) */
609
  .speech-bubble.speech.tail-right {
610
+ border-radius: var(--r);
611
  }
612
  .speech-bubble.speech.tail-right:before {
613
+ left: 100%;
614
+ top: clamp(0%,var(--p) - (1 - var(--t))*var(--b)/2,100% - (1 - var(--t))*var(--b));
615
  transform: rotate(-90deg);
616
  transform-origin: top left;
617
  }
 
1060
  alert(`Starting export of ${pages.length} page(s).`);
1061
  for (let i = 0; i < pages.length; i++) {
1062
  try {
1063
+ // SWITCHED TO DOM-TO-IMAGE FOR MASK SUPPORT
1064
+ const dataUrl = await domtoimage.toPng(pages[i]);
1065
  const link = document.createElement('a');
1066
  link.download = `comic-page-${i + 1}.png`;
1067
+ link.href = dataUrl;
1068
  link.click();
1069
+ } catch (err) {
1070
+ console.error(err);
1071
+ alert(`Failed to export page ${i + 1}.`);
1072
+ }
1073
  }
1074
 
1075
  // 2. UNFREEZE DIMENSIONS (Restore normal behavior)
 
1237
  </script>
1238
  </body>
1239
  </html>'''
 
1240
  with open(os.path.join(self.output_dir, 'page.html'), 'w', encoding='utf-8') as f:
1241
  f.write(template_html)
1242
  print("📄 Template files copied successfully!")