understand this app and its functions, so I can ask you for changes later: <!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Comic Strip Creator</title>
  <!-- Google Fonts -->
  <link href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;600&display=swap" rel="stylesheet">
  <!-- Interact.js for draggable, resizable, rotatable dialogues -->
  <script src="https://cdn.jsdelivr.net/npm/interactjs/dist/interact.min.js"></script>
  <!-- html2canvas for capturing the final image -->
  <script src="https://html2canvas.hertzen.com/dist/html2canvas.min.js"></script>
  <style>
    /* Root Variables for Consistent Theming */
    :root {
      --primary-color: #6B48FF;
      --secondary-color: #FFB648;
      --accent-color: #FF6B6B;
      --background-color: #F0F2F5;
      --card-bg: #FFFFFF;
      --text-primary: #2C3E50;
      --text-secondary: #7F8C8D;
      --success: #2ECC71;
      --error: #E74C3C;
      --shadow: rgba(0, 0, 0, 0.1);
      --border-radius: 8px;
      --transition-speed: 0.3s;
    }

    /* Global Styles */
    body {
      font-family: 'Poppins', sans-serif;
      max-width: 1200px;
      margin: 0 auto;
      padding: 20px;
      background: var(--background-color);
      color: var(--text-primary);
      line-height: 1.6;
    }

    h1, h2 {
      color: var(--primary-color);
      text-align: center;
      margin-bottom: 20px;
    }

    h1 {
      font-size: 2.5em;
      position: relative;
    }

    h1::after {
      content: '';
      display: block;
      width: 60px;
      height: 4px;
      background: var(--secondary-color);
      margin: 10px auto;
      border-radius: 2px;
    }

    /* Input Section Styling */
    .input-section {
      background: var(--card-bg);
      padding: 30px;
      border-radius: var(--border-radius);
      box-shadow: 0 8px 24px var(--shadow);
      margin-bottom: 30px;
      transition: transform var(--transition-speed) ease;
    }

    .input-section:hover {
      transform: translateY(-2px);
    }

    .character-inputs {
      display: grid;
      grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
      gap: 30px;
      margin-bottom: 20px;
    }

    .input-group {
      margin-bottom: 20px;
      position: relative;
    }

    label {
      display: block;
      margin-bottom: 8px;
      font-weight: 600;
      color: var(--text-primary);
      font-size: 0.95em;
    }

    input, textarea, select {
      width: 100%;
      padding: 12px;
      border: 2px solid #E0E7FF;
      border-radius: var(--border-radius);
      font-family: 'Poppins', sans-serif;
      transition: border-color var(--transition-speed) ease, box-shadow var(--transition-speed) ease;
      font-size: 1em;
    }

    input:focus, textarea:focus, select:focus {
      outline: none;
      border-color: var(--primary-color);
      box-shadow: 0 0 0 3px rgba(107, 72, 255, 0.1);
    }

    /* Buttons Styling */
    .button-group {
      display: flex;
      flex-wrap: wrap;
      gap: 15px;
      justify-content: center;
      margin-top: 20px;
    }

    button {
      background: var(--primary-color);
      color: white;
      padding: 12px 24px;
      border: none;
      border-radius: var(--border-radius);
      cursor: pointer;
      font-size: 16px;
      font-weight: 600;
      transition: background var(--transition-speed) ease, transform var(--transition-speed) ease, box-shadow var(--transition-speed) ease;
      text-transform: uppercase;
      letter-spacing: 1px;
    }

    button:hover {
      background: #5536FF;
      transform: translateY(-2px);
      box-shadow: 0 4px 12px rgba(107, 72, 255, 0.2);
    }

    #generate-combined-image {
      background: var(--secondary-color);
    }

    #generate-combined-image:hover {
      background: #FF9F29;
    }

    #generate-dialogues {
      background: #3498DB;
    }

    #generate-dialogues:hover {
      background: #2980B9;
    }

    /* Undo/Redo Button Styles */
    #undo-action,
    #redo-action {
      background: #95A5A6; /* Gray color for initially disabled state */
      cursor: not-allowed;
    }

    #undo-action.enabled,
    #redo-action.enabled {
      background: var(--primary-color);
      cursor: pointer;
    }

    #undo-action.enabled:hover,
    #redo-action.enabled:hover {
      background: #5536FF;
    }

    /* Feedback Messages */
    .error-message, .success-message {
      padding: 15px;
      border-radius: var(--border-radius);
      margin: 15px 0;
      display: none;
      font-size: 0.95em;
    }

    .error-message {
      color: var(--error);
      background: #FFF5F5;
      border-left: 4px solid var(--error);
    }

    .success-message {
      color: var(--success);
      background: #F0FFF0;
      border-left: 4px solid var(--success);
    }

    /* Loading Indicators */
    .loading {
      display: none;
      text-align: center;
      padding: 20px;
      color: var(--primary-color);
      font-weight: 600;
      position: relative;
    }

    .loading::before {
      content: '';
      display: block;
      width: 40px;
      height: 40px;
      margin: 0 auto 10px;
      border: 4px solid var(--primary-color);
      border-top: 4px solid transparent;
      border-radius: 50%;
      animation: spin 1s linear infinite;
    }

    .loading::after {
      content: attr(data-message);
      display: block;
      font-size: 14px;
      margin-top: 8px;
    }

    @keyframes spin {
      0% { transform: rotate(0deg); }
      100% { transform: rotate(360deg); }
    }

    /* Story and Image Containers */
    #story-preview, #combined-image-container {
      background: var(--card-bg);
      padding: 30px;
      border-radius: var(--border-radius);
      margin: 30px 0;
      box-shadow: 0 8px 24px var(--shadow);
      display: none;
      animation: fadeIn 0.5s ease;
    }

    #story-preview p {
      white-space: pre-wrap;
    }

    #combined-comic-image {
      width: 100%;
      max-width: 1024px;
      border: 3px solid var(--primary-color);
      border-radius: 12px;
      transition: transform var(--transition-speed) ease;
    }

    #combined-comic-image:hover {
      transform: scale(1.02);
    }

    /* Download Button */
    #download-image {
      background: var(--success);
      margin-top: 15px;
    }

    #download-image:hover {
      background: #28a745;
    }

    /* Dialogue Bubbles */
    .dialogue-bubble {
      position: absolute;
      min-width: 100px;
      max-width: 250px;
      padding: 10px;
      background: rgba(255, 255, 255, 0.9);
      border: 2px solid var(--primary-color);
      border-radius: 12px;
      cursor: move;
      box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
      user-select: text;
      font-size: 0.95em;
      transform-origin: top left;
    }

    .dialogue-bubble::after {
      content: '';
      position: absolute;
      bottom: -10px;
      left: 20px;
      border-width: 10px 10px 0;
      border-style: solid;
      border-color: rgba(255, 255, 255, 0.9) transparent transparent transparent;
    }

    .dialogue-bubble.rotate-180::after {
      top: -10px;
      bottom: auto;
      left: 20px;
      transform: rotate(180deg);
    }

    .dialogue-bubble.rotate-90::after {
      top: 20px;
      left: -10px;
      transform: rotate(90deg);
    }

    .dialogue-bubble.rotate-270::after {
      top: 20px;
      right: -10px;
      transform: rotate(270deg);
    }

    .dialogue-bubble .delete-button {
      position: absolute;
      top: -10px;
      right: -10px;
      background: var(--error);
      color: white;
      border: none;
      border-radius: 50%;
      width: 20px;
      height: 20px;
      cursor: pointer;
      font-size: 14px;
      line-height: 18px;
      text-align: center;
      padding: 0;
    }

    .dialogue-bubble .rotate-button {
      position: absolute;
      bottom: -10px;
      right: -10px;
      background: var(--secondary-color);
      color: white;
      border: none;
      border-radius: 50%;
      width: 20px;
      height: 20px;
      cursor: pointer;
      font-size: 14px;
      line-height: 18px;
      text-align: center;
      padding: 0;
    }

    .dialogue-bubble.selected {
      border-color: var(--accent-color);
      box-shadow: 0 0 0 3px rgba(255, 107, 107, 0.2);
    }

    /* Customization Panel Styles */
    .customization-panel {
      position: fixed;
      top: 10%;
      left: 50%;
      transform: translateX(-50%);
      background: var(--card-bg);
      padding: 20px;
      border-radius: var(--border-radius);
      box-shadow: 0 8px 24px var(--shadow);
      z-index: 1000;
      width: 300px;
      display: none; /* Hidden by default */
    }

    .customization-panel.active {
      display: block;
    }

    .customization-panel h2 {
      margin-top: 0;
      text-align: center;
      color: var(--primary-color);
    }

    .customization-group {
      margin-bottom: 15px;
    }

    .customization-group label {
      display: block;
      margin-bottom: 5px;
      font-weight: 600;
    }

    .customization-group input,
    .customization-group select {
      width: 100%;
      padding: 8px;
      border: 2px solid #E0E7FF;
      border-radius: var(--border-radius);
      font-family: 'Poppins', sans-serif;
      font-size: 1em;
    }

    .button-group > .button-group {
      gap: 10px;
    }

    /* Speech Bubble */
    .dialogue-bubble.speech::after {
      content: '';
      position: absolute;
      bottom: -10px;
      left: 20px;
      border-width: 10px 10px 0;
      border-style: solid;
      border-color: rgba(255, 255, 255, 0.9) transparent transparent transparent;
    }

    /* Thought Bubble */
    .dialogue-bubble.thought {
      border-color: var(--primary-color);
      background: rgba(255, 255, 255, 0.7);
    }

    .dialogue-bubble.thought::after {
      content: '';
      position: absolute;
      bottom: -10px;
      left: 20px;
      border-width: 10px 10px 0;
      border-style: solid;
      border-color: rgba(255, 255, 255, 0.7) transparent transparent transparent;
      border-radius: 50%;
    }

    /* Responsive Design */
    @media (max-width: 768px) {
      body {
        padding: 10px;
      }

      .input-section {
        padding: 20px;
      }

      h1 {
        font-size: 2em;
      }

      button {
        width: 100%;
      }

      .dialogue-bubble {
        max-width: 120px;
        padding: 8px;
        font-size: 0.9em;
      }

      .customization-panel {
        width: 90%;
      }
    }
  </style>
</head>
<body>
  <main>
    <!-- Input Section -->
    <section class="input-section" aria-labelledby="app-title">
      <h1 id="app-title">Comic Strip Creator</h1>
      
      <form id="comic-form" novalidate>
        <div class="character-inputs">
          <!-- Character 1 Inputs -->
          <div class="character-1">
            <h2>Character 1</h2>
            <div class="input-group">
              <label for="char1-name">Name:</label>
              <input type="text" id="char1-name" name="char1-name" required placeholder="Enter character name" aria-required="true">
            </div>
            <div class="input-group">
              <label for="char1-description">Description:</label>
              <textarea id="char1-description" name="char1-description" rows="4" required placeholder="Describe your character's appearance and personality" aria-required="true"></textarea>
            </div>
          </div>
          
          <!-- Character 2 Inputs -->
          <div class="character-2">
            <h2>Character 2</h2>
            <div class="input-group">
              <label for="char2-name">Name:</label>
              <input type="text" id="char2-name" name="char2-name" required placeholder="Enter character name" aria-required="true">
            </div>
            <div class="input-group">
              <label for="char2-description">Description:</label>
              <textarea id="char2-description" name="char2-description" rows="4" required placeholder="Describe your character's appearance and personality" aria-required="true"></textarea>
            </div>
          </div>
        </div>
    
        <!-- Story Outline -->
        <div class="input-group">
          <label for="story-outline">Story Outline:</label>
          <textarea id="story-outline" name="story-outline" rows="4" required placeholder="Write your story outline here" aria-required="true"></textarea>
        </div>
    
        <!-- Scene Description -->
        <div class="input-group">
          <label for="scene-description">Scene Description:</label>
          <textarea id="scene-description" name="scene-description" rows="4" required placeholder="Describe the scene and setting" aria-required="true"></textarea>
        </div>
    
        <!-- Art Style Selection -->
        <div class="input-group">
          <label for="art-style">Art Style:</label>
          <select id="art-style" name="art-style" required aria-required="true">
            <option value="" disabled selected>Select an art style</option>
            <option value="manga">Manga</option>
            <option value="comic">Comic Book</option>
            <option value="realistic">Realistic</option>
            <option value="cartoon">Cartoon</option>
            <option value="watercolor">Watercolor</option>
            <option value="whimsical">Whimsical Storybook</option>
            <option value="pop">Pop Art</option>
            <option value="sketch">Detailed Sketch</option>
          </select>
        </div>
    
        <!-- Feedback Messages -->
        <div id="error-message" class="error-message" role="alert"></div>
        <div id="success-message" class="success-message" role="status"></div>
    
        <!-- Buttons -->
        <div class="button-group">
          <button type="button" id="generate-story">Generate Story</button>
          <button type="button" id="generate-combined-image" style="display: none;">Generate Comic Strip</button>
          <button type="button" id="generate-dialogues" style="display: none;">Generate Dialogues</button>
          <button type="button" id="undo-action" disabled>Undo</button>
          <button type="button" id="redo-action" disabled>Redo</button>
        </div>
      </form>
    </section>
    
    <!-- Story Preview -->
    <section id="story-preview" aria-live="polite">
      <h2>Generated Story Panels:</h2>
      <p></p>
    </section>
    
    <!-- Combined Comic Image -->
    <section id="combined-image-container" aria-live="polite">
      <h2>Your Comic Strip:</h2>
      <img id="combined-comic-image" src="" alt="Combined Comic Strip">
      <button id="download-image">Download Comic Strip</button>
    </section>
    
    <!-- Customization Panel -->
    <section id="customization-panel" class="customization-panel" aria-hidden="true">
      <h2>Customize Dialogue</h2>
      
      <div class="customization-group">
        <label for="font-style">Font Style:</label>
        <select id="font-style">
          <option value="Poppins" selected>Poppins</option>
          <option value="Arial">Arial</option>
          <option value="Times New Roman">Times New Roman</option>
          <option value="Courier New">Courier New</option>
          <option value="Georgia">Georgia</option>
          <!-- Add more fonts as needed -->
        </select>
      </div>
      
      <div class="customization-group">
        <label for="font-size">Font Size:</label>
        <input type="number" id="font-size" min="10" max="36" value="14"> px
      </div>
      
      <div class="customization-group">
        <label for="text-color">Text Color:</label>
        <input type="color" id="text-color" value="#000000">
      </div>
      
      <div class="customization-group">
        <label for="bubble-color">Bubble Background:</label>
        <input type="color" id="bubble-color" value="#FFFFFF">
      </div>
      
      <div class="customization-group">
        <label for="bubble-style">Bubble Style:</label>
        <select id="bubble-style">
          <option value="speech" selected>Speech Bubble</option>
          <option value="thought">Thought Bubble</option>
          <!-- Add more styles if desired -->
        </select>
      </div>
      
      <div class="button-group">
        <button type="button" id="apply-customization">Apply</button>
        <button type="button" id="reset-customization">Reset</button>
      </div>
    </section>
    
    <!-- Loading Indicators -->
    <div id="loading-story" class="loading" data-message="Creating your story..."></div>
    <div id="loading-image" class="loading" data-message="Crafting your comic panels..."></div>
    <div id="loading-dialogues" class="loading" data-message="Generating dialogues..."></div>
    
  </main>

  <script>
    // Check if html2canvas is loaded
    if (typeof html2canvas === 'undefined') {
      console.error('html2canvas library not loaded');
      // Fallback to load the library dynamically if needed
      const script = document.createElement('script');
      script.src = 'https://html2canvas.hertzen.com/dist/html2canvas.min.js';
      script.onload = () => console.log('html2canvas loaded dynamically');
      document.head.appendChild(script);
    }

    // Utility Function for Displaying Messages
    const displayMessage = (element, message, type) => {
      element.textContent = message;
      element.style.display = 'block';
      element.className = type === 'error' ? 'error-message' : 'success-message';
      setTimeout(() => {
        element.style.display = 'none';
      }, 5000);
    };

    // Validate Form Inputs
    const validateForm = () => {
      const form = document.getElementById('comic-form');
      const requiredFields = form.querySelectorAll('[required]');
      let isValid = true;
      requiredFields.forEach(field => {
        if (!field.value.trim()) {
          isValid = false;
          field.classList.add('input-error');
        } else {
          field.classList.remove('input-error');
        }
      });
      return isValid;
    };

    // Fetch API Wrapper with Error Handling
    const fetchAPI = async (url, options) => {
      try {
        const response = await fetch(url, options);
        if (!response.ok) throw new Error(`Error: ${response.status} ${response.statusText}`);
        return response;
      } catch (error) {
        throw error;
      }
    };

    // Initialize Undo and Redo Stacks
    const undoStack = [];
    const redoStack = [];

    // Function to Record an Action to the Undo Stack
    const recordAction = (action) => {
      undoStack.push(action);
      // Clear the redo stack whenever a new action is performed
      redoStack.length = 0;
      updateUndoRedoButtons();
    };

    // Function to Update the State of Undo and Redo Buttons
    const updateUndoRedoButtons = () => {
      const undoBtn = document.getElementById('undo-action');
      const redoBtn = document.getElementById('redo-action');
      
      if (undoStack.length > 0) {
        undoBtn.disabled = false;
        undoBtn.classList.add('enabled');
      } else {
        undoBtn.disabled = true;
        undoBtn.classList.remove('enabled');
      }
      
      if (redoStack.length > 0) {
        redoBtn.disabled = false;
        redoBtn.classList.add('enabled');
      } else {
        redoBtn.disabled = true;
        redoBtn.classList.remove('enabled');
      }
    };

    // Utility Function to Convert RGB to HEX
    const rgbToHex = (rgb) => {
      const result = rgb.match(/\d+/g);
      if (!result) return '#000000';
      return `#${((1 << 24) + (parseInt(result[0]) << 16) + (parseInt(result[1]) << 8) + parseInt(result[2])).toString(16).slice(1)}`;
    };

    // Global Variable to Track the Selected Bubble
    let selectedBubble = null;

    // Function to Open the Customization Panel
    const openCustomizationPanel = (bubble) => {
      selectedBubble = bubble;
      const customizationPanel = document.getElementById('customization-panel');
      
      // Populate the customization panel with current styles
      const fontStyle = window.getComputedStyle(bubble).fontFamily.replace(/['"]/g, "");
      const fontSize = parseInt(window.getComputedStyle(bubble).fontSize);
      const textColor = rgbToHex(window.getComputedStyle(bubble).color);
      const bubbleColor = rgbToHex(window.getComputedStyle(bubble).backgroundColor);
      
      // Set font style
      const fonts = document.getElementById('font-style');
      fonts.value = fonts.querySelector(`option[value="${fontStyle}"]`) ? fontStyle : 'Poppins';

      // Set font size
      document.getElementById('font-size').value = fontSize;

      // Set text color
      document.getElementById('text-color').value = textColor;

      // Set bubble background color
      document.getElementById('bubble-color').value = bubbleColor;
      
      // Set bubble style
      const currentBubbleStyle = bubble.getAttribute('data-bubble-style') || 'speech';
      document.getElementById('bubble-style').value = currentBubbleStyle;
      
      // Show the customization panel
      customizationPanel.classList.add('active');
      customizationPanel.setAttribute('aria-hidden', 'false');
    };

    // Function to Close the Customization Panel
    const closeCustomizationPanel = () => {
      const customizationPanel = document.getElementById('customization-panel');
      customizationPanel.classList.remove('active');
      customizationPanel.setAttribute('aria-hidden', 'true');
      selectedBubble = null;
    };

    // Function to Generate Story
    const generateStory = async () => {
      const errorMessage = document.getElementById('error-message');
      const successMessage = document.getElementById('success-message');
      const loading = document.getElementById('loading-story');
      const storyPreview = document.getElementById('story-preview');
      const generateImageBtn = document.getElementById('generate-combined-image');
      const generateDialoguesBtn = document.getElementById('generate-dialogues');

      errorMessage.style.display = 'none';
      successMessage.style.display = 'none';
      storyPreview.style.display = 'none';
      generateImageBtn.style.display = 'none';
      generateDialoguesBtn.style.display = 'none';

      if (!validateForm()) {
        displayMessage(errorMessage, 'Please fill in all required fields correctly.', 'error');
        return;
      }

      loading.style.display = 'block';

      try {
        const char1Name = document.getElementById('char1-name').value.trim();
        const char1Desc = document.getElementById('char1-description').value.trim();
        const char2Name = document.getElementById('char2-name').value.trim();
        const char2Desc = document.getElementById('char2-description').value.trim();
        const storyOutline = document.getElementById('story-outline').value.trim();
        const sceneDesc = document.getElementById('scene-description').value.trim();
        const artStyle = document.getElementById('art-style').value;

        const prompt = `Compose a 4-panel strip story featuring two characters: ${char1Name} (${char1Desc}) and ${char2Name} (${char2Desc}). Based on the following inputs:

- **Story Outline:** ${storyOutline}
- **Scene Description:** ${sceneDesc}
- **Art Style:** ${artStyle}

Please create a cohesive and engaging narrative broken down into four distinct visual scenes without any dialogue. Each panel should clearly depict a sequential part of the story, emphasizing the characters actions and emotions. Ensure that the tone aligns with the specified art style, and introduce creative elements to make the story captivating.`;

        const response = await fetchAPI('https://text.pollinations.ai/', {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json',
          },
          body: JSON.stringify({
            messages: [
              { role: 'system', content: 'You are a creative story strip writer.' },
              { role: 'user', content: prompt }
            ],
            model: 'openai',
            jsonMode: false
          }),
        });

        const data = await response.text();

        // Display the generated story
        storyPreview.querySelector('p').innerHTML = data.replace(/\n/g, '<br>');
        storyPreview.style.display = 'block';
        generateImageBtn.style.display = 'inline-block';
        window.storyData = data; // Store story data globally
        displayMessage(successMessage, 'Story generated successfully!', 'success');
      } catch (error) {
        displayMessage(errorMessage, error.message || 'Failed to generate story. Please try again.', 'error');
        console.error('Error:', error);
      } finally {
        loading.style.display = 'none';
      }
    };

    // Function to Generate Combined Image
    const generateCombinedImage = () => {
      const loadingImage = document.getElementById('loading-image');
      const combinedImageContainer = document.getElementById('combined-image-container');
      const combinedComicImage = document.getElementById('combined-comic-image');
      const errorMessage = document.getElementById('error-message');
      const successMessage = document.getElementById('success-message');
      const generateDialoguesBtn = document.getElementById('generate-dialogues');

      errorMessage.style.display = 'none';
      successMessage.style.display = 'none';
      combinedImageContainer.style.display = 'none';
      generateDialoguesBtn.style.display = 'none';

      try {
        const artStyle = document.getElementById('art-style').value;
        const char1Name = document.getElementById('char1-name').value.trim();
        const char2Name = document.getElementById('char2-name').value.trim();

        const scenes = window.storyData.split('\n').filter(scene => scene.trim()).slice(0, 4);
        if (scenes.length < 4) {
          throw new Error('Insufficient number of scenes to create a 4-panel story.');
        }

        const prompt = `Generate a single high-resolution image (1024x1024 pixels) of a 4-panel story strip arranged in a 2x2 grid, featuring two characters: ${char1Name} and ${char2Name} in the ${artStyle} style. Based on the following scene descriptions:

- **Panel 1:** ${scenes[0]}
- **Panel 2:** ${scenes[1]}
- **Panel 3:** ${scenes[2]}
- **Panel 4:** ${scenes[3]}

Ensure that:

1. **Consistent Design:** Both characters maintain consistent appearances, expressions, and postures across all panels.
2. **Art Style Adherence:** The entire strip adheres to the specified ${artStyle}, reflecting its unique aesthetic.
3. **Seamless Transitions:** Scenes flow logically from one panel to the next, showcasing a coherent and engaging storyline.
4. **No Text or Dialogues:** Exclude any textual elements, speech bubbles, or dialogues from the image.
5. **Visual Clarity:** Each panel should clearly represent the described scene with distinct and recognizable actions and emotions.
6. **High Quality:** The final image should be free from logos, watermarks, and maintain high visual fidelity.

Provide the 4 panel image without any annotations or additional elements outside the specified parameters.`;

        const encodedPrompt = encodeURIComponent(prompt);
        const imageUrl = `https://image.pollinations.ai/prompt/${encodedPrompt}?model=flux&width=1024&height=1024&nologo=true&private=true`;

        // Display the loading indicator
        loadingImage.style.display = 'block';

        // Once the image is loaded, display it
        combinedComicImage.onload = () => {
          loadingImage.style.display = 'none';
          combinedImageContainer.style.display = 'block';
          generateDialoguesBtn.style.display = 'inline-block';
          displayMessage(successMessage, 'Comic strip generated successfully!', 'success');
        };

        combinedComicImage.onerror = () => {
          loadingImage.style.display = 'none';
          displayMessage(errorMessage, 'Failed to load the comic strip image. Please try again.', 'error');
        };

        // Set the image source
        combinedComicImage.src = imageUrl;

      } catch (error) {
        displayMessage(errorMessage, error.message || 'Failed to generate the combined comic image.', 'error');
        console.error('Error generating combined image:', error);
      }
    };

    // Function to Generate Dialogues
    const generateDialogues = async () => {
      const errorMessage = document.getElementById('error-message');
      const successMessage = document.getElementById('success-message');
      const loading = document.getElementById('loading-dialogues');
      const dialogues = [];
      
      errorMessage.style.display = 'none';
      
      try {
        loading.style.display = 'block';
        
        // Extract necessary data
        const storyData = window.storyData; // Assuming this contains the generated story
        const panels = window.storyData.split('\n').filter(scene => scene.trim()).slice(0, 4);
        const artStyle = document.getElementById('art-style').value;
        const char1Name = document.getElementById('char1-name').value.trim();
        const char1Desc = document.getElementById('char1-description').value.trim();
        const char2Name = document.getElementById('char2-name').value.trim();
        const char2Desc = document.getElementById('char2-description').value.trim();
        
        // Generate dialogues for each panel
        for (let i = 0; i < panels.length; i++) {
          const prompt = `For Panel ${i + 1} of a 4-panel comic strip featuring ${char1Name} (${char1Desc}) and ${char2Name} (${char2Desc}) in the ${artStyle} style, based on the following scene description:

"${panels[i]}"

Generate a concise and contextually appropriate dialogue between ${char1Name} and ${char2Name}. The dialogue should reflect the characters' personalities and emotions conveyed in the scene, matching the overall narrative style. Limit the dialogue to 2-3 lines maximum.`;

          const response = await fetchAPI('https://text.pollinations.ai/', {
            method: 'POST',
            headers: {
              'Content-Type': 'application/json',
            },
            body: JSON.stringify({
              messages: [
                { role: 'system', content: 'You are a skilled dialogue writer for comic strips.' },
                { role: 'user', content: prompt }
              ],
              model: 'openai',
              jsonMode: true
            }),
          });
          
          const data = await response.text();
          dialogues.push(data.trim());
        }
        
        // Store dialogues globally
        window.dialogueData = dialogues;
        
        // Render dialogues on the comic strip
        renderDialogues(dialogues);
        displayMessage(successMessage, 'Dialogues generated successfully!', 'success');
        
      } catch (error) {
        displayMessage(errorMessage, error.message || 'Failed to generate dialogues. Please try again.', 'error');
        console.error('Error:', error);
      } finally {
        loading.style.display = 'none';
      }
    };

    // Function to Render Dialogues
    const renderDialogues = (dialogues) => {
      const combinedComicImage = document.getElementById('combined-comic-image');
      
      // Ensure the image container is positioned relatively to place absolute elements inside
      const container = document.getElementById('combined-image-container');
      container.style.position = 'relative';
      
      // Clear existing dialogue bubbles if any
      const existingBubbles = container.querySelectorAll('.dialogue-bubble');
      existingBubbles.forEach(bubble => bubble.remove());
      
      // Create dialogue bubbles for each panel
      dialogues.forEach((dialogue, index) => {
        const bubble = document.createElement('div');
        bubble.className = 'dialogue-bubble speech'; // Default style
        bubble.setAttribute('data-bubble-style', 'speech'); // Default style attribute
        bubble.setAttribute('data-id', `bubble-${Date.now()}-${index}`); // Unique ID for tracking
        bubble.contentEditable = 'true';
        bubble.innerText = dialogue;
        
        // Add Delete and Rotate Buttons
        const deleteBtn = document.createElement('button');
        deleteBtn.className = 'delete-button';
        deleteBtn.innerHTML = '&times;';
        deleteBtn.title = 'Delete Bubble';
        bubble.appendChild(deleteBtn);
        
        const rotateBtn = document.createElement('button');
        rotateBtn.className = 'rotate-button';
        rotateBtn.innerHTML = '&#8635;'; // Rotate symbol
        rotateBtn.title = 'Rotate Bubble';
        bubble.appendChild(rotateBtn);
        
        // Positioning logic based on panel
        // Assuming panels are laid out in a 2x2 grid
        // Adjust positions as needed
        switch(index) {
          case 0:
            bubble.style.top = '10%';
            bubble.style.left = '10%';
            break;
          case 1:
            bubble.style.top = '10%';
            bubble.style.right = '10%';
            break;
          case 2:
            bubble.style.bottom = '10%';
            bubble.style.left = '10%';
            break;
          case 3:
            bubble.style.bottom = '10%';
            bubble.style.right = '10%';
            break;
          default:
            bubble.style.top = '10%';
            bubble.style.left = '10%';
        }
        
        container.appendChild(bubble);
        
        // Make the bubble draggable, resizable, and rotatable
        makeInteractive(bubble);
        
        // Record the add action
        recordAction({ type: 'add', bubbleId: bubble.getAttribute('data-id'), bubbleHTML: bubble.outerHTML });
        
        // Event Listener for Delete Button
        deleteBtn.addEventListener('click', () => {
          // Record deletion before removing
          recordAction({ type: 'delete', bubble: bubble.outerHTML, index: index });
          bubble.remove();
        });
        
        // Event Listener for Rotate Button
        rotateBtn.addEventListener('click', () => {
          const previousRotation = getRotation(bubble);
          rotateBubble(bubble);
          const newRotation = getRotation(bubble);
          // Record the rotate action with previous and new rotation
          recordAction({ type: 'rotate', bubble: bubble, previousRotation: previousRotation, newRotation: newRotation });
        });
        
        // Event Listener for Selecting a Bubble
        bubble.addEventListener('click', (e) => {
          e.stopPropagation(); // Prevent event bubbling
          document.querySelectorAll('.dialogue-bubble').forEach(b => b.classList.remove('selected'));
          bubble.classList.add('selected');
          openCustomizationPanel(bubble);
        });
        
        // Optional: Double-Click to Open Customization Panel
        bubble.addEventListener('dblclick', (e) => {
          e.stopPropagation();
          openCustomizationPanel(bubble);
        });
      });
    };

    // Function to Make Dialogue Bubbles Interactive
    const makeInteractive = (element) => {
      interact(element)
        .draggable({
          inertia: true,
          modifiers: [
            interact.modifiers.restrictRect({
              restriction: 'parent',
              endOnly: true
            })
          ],
          autoScroll: true,
          listeners: {
            move: dragMoveListener,
            end: () => {
              // Record action for Undo after moving
              recordAction({ 
                type: 'move', 
                bubble: element, 
                previousPosition: { 
                  x: parseFloat(element.getAttribute('data-x')) || 0, 
                  y: parseFloat(element.getAttribute('data-y')) || 0 
                }, 
                newPosition: { 
                  x: parseFloat(element.getAttribute('data-x')) || 0, 
                  y: parseFloat(element.getAttribute('data-y')) || 0 
                } 
              });
            }
          }
        })
        .resizable({
          edges: { left: true, right: true, bottom: true, top: true },
          inertia: true,
          modifiers: [
            interact.modifiers.restrictSize({
              min: { width: 80, height: 40 },
              max: { width: 400, height: 200 }
            })
          ],
          listeners: {
            move: resizeMoveListener,
            end: () => {
              // Record action for Undo after resizing
              recordAction({ 
                type: 'resize', 
                bubble: element, 
                previousSize: { 
                  width: element.style.width, 
                  height: element.style.height 
                }, 
                newSize: { 
                  width: element.style.width, 
                  height: element.style.height 
                } 
              });
            }
          }
        })
        .gesturable({
          listeners: {
            move: rotateMoveListener,
            end: () => {
              // Rotation is handled via Rotate Button
            }
          }
        });
    };

    // Drag Move Listener
    const dragMoveListener = (event) => {
      const target = event.target;
      // Keep the dragged position in the data attribute
      const x = (parseFloat(target.getAttribute('data-x')) || 0) + event.dx;
      const y = (parseFloat(target.getAttribute('data-y')) || 0) + event.dy;

      // Translate the element
      target.style.transform = `translate(${x}px, ${y}px) rotate(${getRotation(target)}deg)`;
      
      // Update the position attributes
      target.setAttribute('data-x', x);
      target.setAttribute('data-y', y);
    };

    // Resize Move Listener
    const resizeMoveListener = (event) => {
      const target = event.target;
      
      // Update the element's style
      let { width, height } = event.rect;
      target.style.width = `${width}px`;
      target.style.height = `${height}px`;
      
      // Adjust the transform to keep the element's top-left corner in place
      const x = (parseFloat(target.getAttribute('data-x')) || 0);
      const y = (parseFloat(target.getAttribute('data-y')) || 0);
      target.style.transform = `translate(${x}px, ${y}px) rotate(${getRotation(target)}deg)`;
    };

    // Rotate Move Listener
    const rotateMoveListener = (event) => {
      const target = event.target;
      const currentRotation = getRotation(target);
      const newRotation = currentRotation + event.da;
      setRotation(target, newRotation);
    };

    // Get current rotation of an element
    const getRotation = (element) => {
      const style = window.getComputedStyle(element, null);
      const transform = style.getPropertyValue('transform');
      if (transform === 'none') return 0;
      const matrix = transform.match(/^matrix\((.+)\)$/);
      if (matrix) {
        const values = matrix[1].split(', ');
        const a = parseFloat(values[0]);
        const b = parseFloat(values[1]);
        const angle = Math.round(Math.atan2(b, a) * (180/Math.PI));
        return angle;
      }
      return 0;
    };

    // Set rotation of an element
    const setRotation = (element, angle) => {
      const x = element.getAttribute('data-x') || 0;
      const y = element.getAttribute('data-y') || 0;
      element.style.transform = `translate(${x}px, ${y}px) rotate(${angle}deg)`;
    };

    // Rotate Bubble Function (on rotate button click)
    const rotateBubble = (element) => {
      const currentRotation = getRotation(element);
      const newRotation = currentRotation + 90; // Rotate by 90 degrees each click
      setRotation(element, newRotation);

      // Adjust arrow direction based on rotation
      if ((newRotation / 90) % 4 === 0) {
        element.classList.remove('rotate-90', 'rotate-180', 'rotate-270', 'thought');
        element.classList.add('speech'); // Reset to speech
      } else if ((newRotation / 90) % 4 === 1) {
        element.classList.remove('rotate-180', 'rotate-270', 'speech');
        element.classList.add('rotate-90');
      } else if ((newRotation / 90) % 4 === 2) {
        element.classList.remove('rotate-90', 'rotate-270', 'speech');
        element.classList.add('rotate-180');
      } else if ((newRotation / 90) % 4 === 3) {
        element.classList.remove('rotate-90', 'rotate-180', 'speech');
        element.classList.add('rotate-270');
      }
    };

    // Function to Undo Actions
    const undo = () => {
      if (undoStack.length === 0) return;
      
      const action = undoStack.pop();
      redoStack.push(action);
      
      switch(action.type) {
        case 'add':
          // Remove the added bubble
          const addedBubble = document.querySelector(`.dialogue-bubble[data-id="${action.bubbleId}"]`);
          if (addedBubble) addedBubble.remove();
          break;
          
        case 'delete':
          // Re-add the deleted bubble at the original position
          const container = document.getElementById('combined-image-container');
          container.insertAdjacentHTML('beforeend', action.bubble);
          // Reinitialize interact.js on the restored bubble
          makeInteractive(container.lastElementChild);
          break;
          
        case 'move':
          // Revert to previous position
          const movedBubble = action.bubble;
          movedBubble.style.transform = `translate(${action.previousPosition.x}px, ${action.previousPosition.y}px) rotate(${getRotation(movedBubble)}deg)`;
          movedBubble.setAttribute('data-x', action.previousPosition.x);
          movedBubble.setAttribute('data-y', action.previousPosition.y);
          break;
          
        case 'resize':
          // Revert to previous size
          const resizedBubble = action.bubble;
          resizedBubble.style.width = action.previousSize.width;
          resizedBubble.style.height = action.previousSize.height;
          break;
          
        case 'rotate':
          // Revert rotation
          const rotatedBubble = action.bubble;
          setRotation(rotatedBubble, action.previousRotation);
          break;
          
        case 'customize':
          // Revert to previous styles
          const customizedBubble = action.bubble;
          customizedBubble.style.fontFamily = action.previousStyles.fontFamily;
          customizedBubble.style.fontSize = action.previousStyles.fontSize;
          customizedBubble.style.color = action.previousStyles.color;
          customizedBubble.style.backgroundColor = action.previousStyles.backgroundColor;
          customizedBubble.className = `dialogue-bubble ${action.previousStyles.bubbleStyle}`;
          customizedBubble.setAttribute('data-bubble-style', action.previousStyles.bubbleStyle);
          break;
          
        case 'reset':
          // Reapply customization before reset
          const resetBubble = action.bubble;
          resetBubble.style.fontFamily = `'Poppins', sans-serif`;
          resetBubble.style.fontSize = `14px`;
          resetBubble.style.color = `#000000`;
          resetBubble.style.backgroundColor = `#FFFFFF`;
          
          resetBubble.className = 'dialogue-bubble speech';
          resetBubble.setAttribute('data-bubble-style', 'speech');
          break;
          
        default:
          break;
      }
      
      updateUndoRedoButtons();
    };

    // Function to Redo Actions
    const redo = () => {
      if (redoStack.length === 0) return;
      
      const action = redoStack.pop();
      undoStack.push(action);
      
      switch(action.type) {
        case 'add':
          // Re-add the bubble
          const container = document.getElementById('combined-image-container');
          container.insertAdjacentHTML('beforeend', action.bubbleHTML);
          // Reinitialize interact.js on the added bubble
          makeInteractive(container.lastElementChild);
          break;
          
        case 'delete':
          // Remove the bubble again
          const deletedBubble = document.querySelector(`.dialogue-bubble[data-id="${action.index}"]`);
          if (deletedBubble) deletedBubble.remove();
          break;
          
        case 'move':
          // Re-apply the move
          const movedBubble = action.bubble;
          movedBubble.style.transform = `translate(${action.newPosition.x}px, ${action.newPosition.y}px) rotate(${getRotation(movedBubble)}deg)`;
          movedBubble.setAttribute('data-x', action.newPosition.x);
          movedBubble.setAttribute('data-y', action.newPosition.y);
          break;
          
        case 'resize':
          // Re-apply the resize
          const resizedBubble = action.bubble;
          resizedBubble.style.width = action.newSize.width;
          resizedBubble.style.height = action.newSize.height;
          break;
          
        case 'rotate':
          // Re-apply the rotation
          const rotatedBubble = action.bubble;
          setRotation(rotatedBubble, action.newRotation);
          break;
          
        case 'customize':
          // Re-apply customization
          const customizedBubble = action.bubble;
          customizedBubble.style.fontFamily = action.newStyles.fontFamily;
          customizedBubble.style.fontSize = action.newStyles.fontSize;
          customizedBubble.style.color = action.newStyles.color;
          customizedBubble.style.backgroundColor = action.newStyles.backgroundColor;
          customizedBubble.classList.add(action.newStyles.bubbleStyle);
          customizedBubble.setAttribute('data-bubble-style', action.newStyles.bubbleStyle);
          break;
          
        case 'reset':
          // Revert the reset
          const resetBubble = action.bubble;
          resetBubble.style.fontFamily = action.previousStyles.fontFamily;
          resetBubble.style.fontSize = action.previousStyles.fontSize;
          resetBubble.style.color = action.previousStyles.color;
          resetBubble.style.backgroundColor = action.previousStyles.backgroundColor;
          resetBubble.className = `dialogue-bubble ${action.previousStyles.bubbleStyle}`;
          resetBubble.setAttribute('data-bubble-style', action.previousStyles.bubbleStyle);
          break;
          
        default:
          break;
      }
      
      updateUndoRedoButtons();
    };

    // Event Listener for Undo and Redo Buttons
    document.getElementById('undo-action').addEventListener('click', undo);
    document.getElementById('redo-action').addEventListener('click', redo);

    // Function to Apply Customization
    document.getElementById('apply-customization').addEventListener('click', () => {
      if (!selectedBubble) return;
      
      const fontStyle = document.getElementById('font-style').value;
      const fontSize = document.getElementById('font-size').value;
      const textColor = document.getElementById('text-color').value;
      const bubbleColor = document.getElementById('bubble-color').value;
      const bubbleStyle = document.getElementById('bubble-style').value;
      
      // Record previous styles for Undo
      const previousStyles = {
        fontFamily: selectedBubble.style.fontFamily || '',
        fontSize: selectedBubble.style.fontSize || '',
        color: selectedBubble.style.color || '',
        backgroundColor: selectedBubble.style.backgroundColor || '',
        bubbleStyle: selectedBubble.getAttribute('data-bubble-style') || 'speech',
      };
      
      // Apply new styles
      selectedBubble.style.fontFamily = fontStyle;
      selectedBubble.style.fontSize = `${fontSize}px`;
      selectedBubble.style.color = textColor;
      selectedBubble.style.backgroundColor = bubbleColor;
      
      // Update bubble style
      selectedBubble.className = `dialogue-bubble ${bubbleStyle}`;
      selectedBubble.setAttribute('data-bubble-style', bubbleStyle);
      
      // Record action for Undo
      recordAction({ 
        type: 'customize', 
        bubble: selectedBubble, 
        previousStyles: previousStyles, 
        newStyles: { 
          fontFamily: fontStyle, 
          fontSize: `${fontSize}px`, 
          color: textColor, 
          backgroundColor: bubbleColor, 
          bubbleStyle: bubbleStyle 
        } 
      });
      
      // Close the customization panel
      closeCustomizationPanel();
    });

    // Function to Reset Customization
    document.getElementById('reset-customization').addEventListener('click', () => {
      if (!selectedBubble) return;
      
      // Record previous styles for Undo
      const previousStyles = {
        fontFamily: selectedBubble.style.fontFamily || '',
        fontSize: selectedBubble.style.fontSize || '',
        color: selectedBubble.style.color || '',
        backgroundColor: selectedBubble.style.backgroundColor || '',
        bubbleStyle: selectedBubble.getAttribute('data-bubble-style') || 'speech',
      };
      
      // Reset to default styles
      selectedBubble.style.fontFamily = `'Poppins', sans-serif`;
      selectedBubble.style.fontSize = `14px`;
      selectedBubble.style.color = `#000000`;
      selectedBubble.style.backgroundColor = `#FFFFFF`;
      
      selectedBubble.className = 'dialogue-bubble speech';
      selectedBubble.setAttribute('data-bubble-style', 'speech');
      
      // Record action for Undo
      recordAction({ 
        type: 'reset', 
        bubble: selectedBubble, 
        previousStyles: previousStyles 
      });
      
      // Close the customization panel
      closeCustomizationPanel();
    });

    // Close Customization Panel when Clicking Outside
    document.addEventListener('click', (e) => {
      const customizationPanel = document.getElementById('customization-panel');
      if (!customizationPanel.contains(e.target) && !e.target.classList.contains('dialogue-bubble') && !e.target.closest('.dialogue-bubble')) {
        closeCustomizationPanel();
      }
    });

    // Generate Story Function Integration
    const generateStoryFunction = async () => {
      const generateStoryButton = document.getElementById('generate-story');
      generateStoryButton.addEventListener('click', generateStory);
    };

    // Generate Combined Image Function Integration
    const generateCombinedImageFunction = () => {
      const generateImageButton = document.getElementById('generate-combined-image');
      generateImageButton.addEventListener('click', generateCombinedImage);
    };

    // Generate Dialogues Function Integration
    const generateDialoguesFunction = () => {
      const generateDialoguesButton = document.getElementById('generate-dialogues');
      generateDialoguesButton.addEventListener('click', generateDialogues);
    };

    // Download Image Function Integration
    const downloadImageWithDialogues = async () => {
      const container = document.getElementById('combined-image-container');
      const loadingMessage = document.getElementById('loading-image');
      const errorMessage = document.getElementById('error-message');
      
      try {
        loadingMessage.style.display = 'block';
        loadingMessage.setAttribute('data-message', 'Preparing download...');
        
        // Ensure html2canvas is available
        if (typeof html2canvas === 'undefined') {
          throw new Error('html2canvas library not loaded');
        }
        
        const canvas = await html2canvas(container, {
          useCORS: true,
          allowTaint: true,
          logging: false,
          backgroundColor: null
        });
        
        const link = document.createElement('a');
        link.href = canvas.toDataURL('image/png');
        link.download = 'comic_strip_with_dialogues.png';
        document.body.appendChild(link);
        link.click();
        document.body.removeChild(link);
        
      } catch (error) {
        console.error('Download error:', error);
        displayMessage(errorMessage, 'Failed to download the comic strip. Please try again.', 'error');
      } finally {
        loadingMessage.style.display = 'none';
      }
    };

    // Event Listener for Download Button
    document.getElementById('download-image').addEventListener('click', downloadImageWithDialogues);

    // Initialize All Event Listeners
    const initializeApp = () => {
      generateStoryFunction();
      generateCombinedImageFunction();
      generateDialoguesFunction();
    };

    // Call Initialize Function on Page Load
    window.onload = initializeApp;

    // Function to Rotate Bubble (already defined above)
    // function rotateBubble(element) { ... }

    // Function to Handle Selection and Opening of Customization Panel
    // Already handled in the renderDialogues function

    // Note: Ensure that all referenced elements and IDs exist in the HTML structure.
  </script>
</body>
</html>