jhh6576 commited on
Commit
05654c6
·
verified ·
1 Parent(s): 0bcd22d

Update app_enhanced.py

Browse files
Files changed (1) hide show
  1. app_enhanced.py +73 -36
app_enhanced.py CHANGED
@@ -543,14 +543,14 @@ class EnhancedComicGenerator:
543
  .speech-bubble.selected { outline: 2px dashed #4CAF50; }
544
  .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; }
545
 
546
- /* <<< MODIFICATION START: New CSS Triangle Bubble >>> */
547
  .speech-bubble.speech {
548
  background: var(--bubble-fill-color, white);
549
  color: var(--bubble-text-color, #333);
550
  border-radius: .4em;
551
  padding: 0;
552
  border: none;
553
- box-shadow: 0 0 5px rgba(0,0,0,0.2);
554
  }
555
 
556
  .speech-bubble.speech::after {
@@ -558,47 +558,43 @@ class EnhancedComicGenerator:
558
  position: absolute;
559
  width: 0;
560
  height: 0;
561
- /* The color is inherited from the parent's fill color */
562
  border-color: transparent;
563
  border-style: solid;
 
 
564
  }
565
 
566
- /* 4-WAY TAIL ROTATION CLASSES using the CSS Triangle method */
567
  .speech-bubble.speech.tail-bottom::after {
568
  bottom: 0;
569
- left: 50%;
570
- border-width: 20px;
571
  border-top-color: var(--bubble-fill-color, white);
572
  border-bottom: 0;
573
- margin-left: -20px;
574
- margin-bottom: -20px;
575
  }
576
  .speech-bubble.speech.tail-top::after {
577
  top: 0;
578
- left: 50%;
579
- border-width: 20px;
580
  border-bottom-color: var(--bubble-fill-color, white);
581
  border-top: 0;
582
- margin-left: -20px;
583
- margin-top: -20px;
584
  }
585
  .speech-bubble.speech.tail-right::after {
586
- top: 50%;
587
  right: 0;
588
- border-width: 20px;
589
  border-left-color: var(--bubble-fill-color, white);
590
  border-right: 0;
591
- margin-top: -20px;
592
- margin-right: -20px;
593
  }
594
  .speech-bubble.speech.tail-left::after {
595
- top: 50%;
596
  left: 0;
597
- border-width: 20px;
598
  border-right-color: var(--bubble-fill-color, white);
599
  border-left: 0;
600
- margin-top: -20px;
601
- margin-left: -20px;
602
  }
603
  /* <<< MODIFICATION END >>> */
604
 
@@ -632,6 +628,8 @@ class EnhancedComicGenerator:
632
  .color-picker-grid div { text-align: center; }
633
  .color-picker-grid label { font-size: 11px; }
634
  .color-picker-grid input[type="color"] { height: 25px; padding: 2px; }
 
 
635
  </style>
636
  </head>
637
  <body>
@@ -663,10 +661,22 @@ class EnhancedComicGenerator:
663
  <input type="color" id="bubble-fill-color" value="#FFFFFF" disabled>
664
  </div>
665
  </div>
666
- <button onclick="rotateBubbleTail()" class="secondary-button">🔄 Rotate Tail</button>
667
  <button onclick="addBubbleToPanel()" class="action-button">💬 Add Bubble</button>
668
  <button onclick="deleteBubble()" class="reset-button">🗑️ Delete Bubble</button>
669
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
670
  <div class="control-group">
671
  <label>Panel Tools (Select Panel):</label>
672
  <button onclick="replacePanelImage()" class="action-button">🖼️ Replace Image</button>
@@ -758,6 +768,18 @@ class EnhancedComicGenerator:
758
  currentlySelectedBubble.style.setProperty('--bubble-fill-color', e.target.value);
759
  }
760
  });
 
 
 
 
 
 
 
 
 
 
 
 
761
 
762
  document.addEventListener('mousemove', e => { if (isPanning) panImage(e); if (draggedBubble) drag(e); if(isResizing) resizeBubble(e); });
763
  document.addEventListener('mouseup', e => { if (isPanning) stopPan(e); if (draggedBubble) stopDrag(e); if(isResizing) stopResize(e);});
@@ -810,9 +832,8 @@ class EnhancedComicGenerator:
810
  bubble.dataset.type = type;
811
 
812
  if (type === 'speech') {
813
- // <<< MODIFICATION: Set default tail position class >>>
814
  bubble.classList.add('tail-bottom');
815
- bubble.dataset.tailPos = '0';
816
  }
817
  if (type === 'thought') {
818
  for (let i = 1; i <= 2; i++) {
@@ -826,6 +847,8 @@ class EnhancedComicGenerator:
826
  function changeBubbleType(type) {
827
  if (!currentlySelectedBubble) return;
828
  applyBubbleType(currentlySelectedBubble, type);
 
 
829
  }
830
 
831
  function changeFont(font) {
@@ -833,20 +856,19 @@ class EnhancedComicGenerator:
833
  currentlySelectedBubble.style.fontFamily = font;
834
  }
835
 
836
- // <<< MODIFICATION: Updated rotate function for new classes >>>
837
- function rotateBubbleTail() {
838
- if (!currentlySelectedBubble) { alert("Please select a bubble first."); return; }
839
- if (currentlySelectedBubble.dataset.type !== 'speech') {
840
- alert("Tail rotation is only available for the 'Speech' bubble type.");
841
- return;
842
- }
843
 
844
- const positions = ['tail-bottom', 'tail-right', 'tail-top', 'tail-left'];
845
- let currentPos = parseInt(currentlySelectedBubble.dataset.tailPos || 0);
846
- currentlySelectedBubble.classList.remove(positions[currentPos]);
847
- let nextPos = (currentPos + 1) % positions.length;
848
- currentlySelectedBubble.classList.add(positions[nextPos]);
849
- currentlySelectedBubble.dataset.tailPos = nextPos;
 
 
 
850
  }
851
 
852
  function selectPanel(panel) {
@@ -860,6 +882,7 @@ class EnhancedComicGenerator:
860
  if (currentlySelectedBubble) currentlySelectedBubble.classList.remove('selected');
861
  currentlySelectedBubble = bubble;
862
  const bubbleControls = ['bubble-text-color', 'bubble-fill-color', 'bubble-type-select', 'font-select'];
 
863
 
864
  if (currentlySelectedBubble) {
865
  currentlySelectedBubble.classList.add('selected');
@@ -881,8 +904,22 @@ class EnhancedComicGenerator:
881
 
882
  document.getElementById('zoom-slider').disabled = true;
883
  bubbleControls.forEach(id => document.getElementById(id).disabled = false);
 
 
 
 
 
 
 
 
 
 
 
 
 
884
  } else {
885
  bubbleControls.forEach(id => document.getElementById(id).disabled = true);
 
886
  if(currentlySelectedPanel) document.getElementById('zoom-slider').disabled = false;
887
  }
888
  }
 
543
  .speech-bubble.selected { outline: 2px dashed #4CAF50; }
544
  .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; }
545
 
546
+ /* <<< MODIFICATION START: New CSS for Speech Bubble with Sliders >>> */
547
  .speech-bubble.speech {
548
  background: var(--bubble-fill-color, white);
549
  color: var(--bubble-text-color, #333);
550
  border-radius: .4em;
551
  padding: 0;
552
  border: none;
553
+ box-shadow: 0 1px 4px rgba(0,0,0,0.25);
554
  }
555
 
556
  .speech-bubble.speech::after {
 
558
  position: absolute;
559
  width: 0;
560
  height: 0;
 
561
  border-color: transparent;
562
  border-style: solid;
563
+ /* Use CSS variables for dynamic control from sliders */
564
+ border-width: var(--tail-size, 20px);
565
  }
566
 
 
567
  .speech-bubble.speech.tail-bottom::after {
568
  bottom: 0;
569
+ left: var(--tail-pos, 50%); /* Controlled by position slider */
 
570
  border-top-color: var(--bubble-fill-color, white);
571
  border-bottom: 0;
572
+ transform: translateX(-50%); /* Center the tip on the slider's value */
573
+ margin-bottom: calc(-1 * var(--tail-size, 20px));
574
  }
575
  .speech-bubble.speech.tail-top::after {
576
  top: 0;
577
+ left: var(--tail-pos, 50%);
 
578
  border-bottom-color: var(--bubble-fill-color, white);
579
  border-top: 0;
580
+ transform: translateX(-50%);
581
+ margin-top: calc(-1 * var(--tail-size, 20px));
582
  }
583
  .speech-bubble.speech.tail-right::after {
584
+ top: var(--tail-pos, 50%);
585
  right: 0;
 
586
  border-left-color: var(--bubble-fill-color, white);
587
  border-right: 0;
588
+ transform: translateY(-50%);
589
+ margin-right: calc(-1 * var(--tail-size, 20px));
590
  }
591
  .speech-bubble.speech.tail-left::after {
592
+ top: var(--tail-pos, 50%);
593
  left: 0;
 
594
  border-right-color: var(--bubble-fill-color, white);
595
  border-left: 0;
596
+ transform: translateY(-50%);
597
+ margin-left: calc(-1 * var(--tail-size, 20px));
598
  }
599
  /* <<< MODIFICATION END >>> */
600
 
 
628
  .color-picker-grid div { text-align: center; }
629
  .color-picker-grid label { font-size: 11px; }
630
  .color-picker-grid input[type="color"] { height: 25px; padding: 2px; }
631
+ .slider-group { margin-top: 5px; }
632
+ .slider-group label { font-size: 11px; margin-right: 5px; }
633
  </style>
634
  </head>
635
  <body>
 
661
  <input type="color" id="bubble-fill-color" value="#FFFFFF" disabled>
662
  </div>
663
  </div>
 
664
  <button onclick="addBubbleToPanel()" class="action-button">💬 Add Bubble</button>
665
  <button onclick="deleteBubble()" class="reset-button">🗑️ Delete Bubble</button>
666
  </div>
667
+ <!-- <<< MODIFICATION: New Tail Controls Section >>> -->
668
+ <div class="control-group" id="tail-controls" style="display: none;">
669
+ <label>Tail Controls (Speech Only)</label>
670
+ <div class="slider-group">
671
+ <label for="tail-pos-slider">Position</label>
672
+ <input type="range" id="tail-pos-slider" min="10" max="90" value="50" step="1">
673
+ </div>
674
+ <div class="slider-group">
675
+ <label for="tail-size-slider">Size</label>
676
+ <input type="range" id="tail-size-slider" min="10" max="40" value="20" step="1">
677
+ </div>
678
+ <button onclick="flipBubbleTail()" class="secondary-button">🔄 Flip Edge</button>
679
+ </div>
680
  <div class="control-group">
681
  <label>Panel Tools (Select Panel):</label>
682
  <button onclick="replacePanelImage()" class="action-button">🖼️ Replace Image</button>
 
768
  currentlySelectedBubble.style.setProperty('--bubble-fill-color', e.target.value);
769
  }
770
  });
771
+
772
+ // <<< MODIFICATION: Event listeners for new tail sliders >>>
773
+ document.getElementById('tail-pos-slider').addEventListener('input', (e) => {
774
+ if (currentlySelectedBubble) {
775
+ currentlySelectedBubble.style.setProperty('--tail-pos', e.target.value + '%');
776
+ }
777
+ });
778
+ document.getElementById('tail-size-slider').addEventListener('input', (e) => {
779
+ if (currentlySelectedBubble) {
780
+ currentlySelectedBubble.style.setProperty('--tail-size', e.target.value + 'px');
781
+ }
782
+ });
783
 
784
  document.addEventListener('mousemove', e => { if (isPanning) panImage(e); if (draggedBubble) drag(e); if(isResizing) resizeBubble(e); });
785
  document.addEventListener('mouseup', e => { if (isPanning) stopPan(e); if (draggedBubble) stopDrag(e); if(isResizing) stopResize(e);});
 
832
  bubble.dataset.type = type;
833
 
834
  if (type === 'speech') {
 
835
  bubble.classList.add('tail-bottom');
836
+ bubble.dataset.tailEdge = 'bottom';
837
  }
838
  if (type === 'thought') {
839
  for (let i = 1; i <= 2; i++) {
 
847
  function changeBubbleType(type) {
848
  if (!currentlySelectedBubble) return;
849
  applyBubbleType(currentlySelectedBubble, type);
850
+ // Reselect bubble to update UI controls visibility
851
+ selectBubble(currentlySelectedBubble);
852
  }
853
 
854
  function changeFont(font) {
 
856
  currentlySelectedBubble.style.fontFamily = font;
857
  }
858
 
859
+ // <<< MODIFICATION: Replaced rotate with flip function >>>
860
+ function flipBubbleTail() {
861
+ if (!currentlySelectedBubble || currentlySelectedBubble.dataset.type !== 'speech') return;
 
 
 
 
862
 
863
+ const edges = ['bottom', 'right', 'top', 'left'];
864
+ const currentEdge = currentlySelectedBubble.dataset.tailEdge || 'bottom';
865
+ let currentEdgeIndex = edges.indexOf(currentEdge);
866
+
867
+ currentlySelectedBubble.classList.remove('tail-' + edges[currentEdgeIndex]);
868
+ let nextEdgeIndex = (currentEdgeIndex + 1) % edges.length;
869
+
870
+ currentlySelectedBubble.classList.add('tail-' + edges[nextEdgeIndex]);
871
+ currentlySelectedBubble.dataset.tailEdge = edges[nextEdgeIndex];
872
  }
873
 
874
  function selectPanel(panel) {
 
882
  if (currentlySelectedBubble) currentlySelectedBubble.classList.remove('selected');
883
  currentlySelectedBubble = bubble;
884
  const bubbleControls = ['bubble-text-color', 'bubble-fill-color', 'bubble-type-select', 'font-select'];
885
+ const tailControls = document.getElementById('tail-controls');
886
 
887
  if (currentlySelectedBubble) {
888
  currentlySelectedBubble.classList.add('selected');
 
904
 
905
  document.getElementById('zoom-slider').disabled = true;
906
  bubbleControls.forEach(id => document.getElementById(id).disabled = false);
907
+
908
+ // <<< MODIFICATION: Show/hide tail controls and set slider values >>>
909
+ if (currentlySelectedBubble.dataset.type === 'speech') {
910
+ tailControls.style.display = 'block';
911
+ const currentSize = parseInt(styles.getPropertyValue('--tail-size')) || 20;
912
+ document.getElementById('tail-size-slider').value = currentSize;
913
+
914
+ const currentPos = parseInt(styles.getPropertyValue('--tail-pos')) || 50;
915
+ document.getElementById('tail-pos-slider').value = currentPos;
916
+ } else {
917
+ tailControls.style.display = 'none';
918
+ }
919
+
920
  } else {
921
  bubbleControls.forEach(id => document.getElementById(id).disabled = true);
922
+ tailControls.style.display = 'none';
923
  if(currentlySelectedPanel) document.getElementById('zoom-slider').disabled = false;
924
  }
925
  }