explained this application, pointed out possible errors in its operation, and suggested ideas to make it better: <html><head><base href="https://example.com/"><!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 {
      display: none; /* Initially hidden */
      margin-top: 20px; /* Space above button */
      background: var(--secondary-color);
    }

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

    #generate-dialogues {
      display: none; /* Initially hidden */
      margin-top: 20px; /* Space above button */
      background: #3498DB;
    }

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

    /* 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;
      text-align: center; /* Center the content */
    }

    #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;
    }

    /* Updated Dialogue Bubbles */
    .dialogue-bubble {
      position: absolute;
      min-width: 100px;
      max-width: 250px;
      padding: 15px;
      background: rgba(255, 255, 255, 0.85);
      border: 2.5px solid var(--primary-color);
      border-radius: 18px;
      cursor: move;
      box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
      user-select: text;
      font-size: 0.95em;
      transform-origin: top left;
      transition: border-color 0.3s, box-shadow 0.3s;
      backdrop-filter: blur(4px);
    }

    /* Speech Bubble Tail Styles */
    .dialogue-bubble.speech::after {
      content: '';
      position: absolute;
      bottom: -15px;
      left: 25px;
      width: 30px;
      height: 15px;
      background: inherit;
      border-right: 2.5px solid var(--primary-color);
      border-bottom: 2.5px solid var(--primary-color);
      transform: skew(-45deg);
      transform-origin: 0 0;
      transition: border-color 0.3s;
    }

    /* Thought Bubble Style */
    .dialogue-bubble.thought {
      border-radius: 24px;
      background: rgba(255, 255, 255, 0.85);
      border-style: dashed;
    }

    .dialogue-bubble.thought::after {
      content: '';
      position: absolute;
      bottom: -20px;
      left: 25px;
      width: 12px;
      height: 12px;
      background: inherit;
      border: 2.5px dashed var(--primary-color);
      border-radius: 50%;
    }

    .dialogue-bubble.thought::before {
      content: '';
      position: absolute;
      bottom: -35px;
      left: 15px;
      width: 8px;
      height: 8px;
      background: inherit;
      border: 2.5px dashed var(--primary-color);
      border-radius: 50%;
    }

    /* Hover and Selected States */
    .dialogue-bubble:hover {
      box-shadow: 0 6px 16px rgba(0, 0, 0, 0.2);
      border-color: var(--secondary-color);
    }

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

    /* Control Buttons */
    .dialogue-bubble .delete-button,
    .dialogue-bubble .rotate-button {
      position: absolute;
      width: 24px;
      height: 24px;
      border-radius: 50%;
      border: none;
      cursor: pointer;
      font-size: 14px;
      display: flex;
      align-items: center;
      justify-content: center;
      transition: all 0.3s ease;
      opacity: 0;
      transform: scale(0.8);
    }

    .dialogue-bubble:hover .delete-button,
    .dialogue-bubble:hover .rotate-button,
    .dialogue-bubble.selected .delete-button,
    .dialogue-bubble.selected .rotate-button {
      opacity: 1;
      transform: scale(1);
    }

    .dialogue-bubble .delete-button {
      top: -12px;
      right: -12px;
      background: var(--error);
      color: white;
    }

    .dialogue-bubble .rotate-button {
      bottom: -12px;
      right: -12px;
      background: var(--secondary-color);
      color: white;
    }

    .dialogue-bubble .delete-button:hover,
    .dialogue-bubble .rotate-button:hover {
      transform: scale(1.1);
    }

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

    /* Generate Comic Controls */
    .generate-comic-controls {
      display: flex;
      align-items: center;
      gap: 20px;
      justify-content: center;
      margin-top: 20px;
    }

    .generate-comic-controls .input-group {
      margin-bottom: 0;
      min-width: 200px;
    }

    /* 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>
    
        <!-- 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>
        </div>
      </form>
    </section>
    
    <!-- Story Preview -->
    <section id="story-preview" aria-live="polite">
      <h2>Generated Story Panels:</h2>
      <p></p>
      <div class="generate-comic-controls">
        <div class="input-group" style="display: none;" id="art-style-container">
          <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>
        <button type="button" id="generate-combined-image" style="display: none;">Generate Comic Strip</button>
      </div>
    </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 type="button" id="generate-dialogues" style="display: none;">Generate Dialogues</button>
      <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);
    }

    // Convert RGB color to Hex
    const rgbToHex = (rgb) => {
      // If already hex, return as is
      if (rgb.startsWith('#')) return rgb;
      
      // Extract RGB values
      const rgbMatch = rgb.match(/^rgba?\((\d+),\s*(\d+),\s*(\d+)/i);
      if (!rgbMatch) return '#000000'; // Default to black if invalid

      const r = parseInt(rgbMatch[1]);
      const g = parseInt(rgbMatch[2]);
      const b = parseInt(rgbMatch[3]);

      // Convert to hex
      const toHex = (n) => {
        const hex = n.toString(16);
        return hex.length === 1 ? '0' + hex : hex;
      };

      return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
    };

    // 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;
      }
    };

    // 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');

      errorMessage.style.display = 'none';
      successMessage.style.display = 'none';
      storyPreview.style.display = 'none';
      generateImageBtn.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 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}

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.`;

        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';
        
        // Hide generate story button and show generate comic strip button
        document.getElementById('generate-story').style.display = 'none';
        generateImageBtn.style.display = 'inline-block';
        document.getElementById('art-style-container').style.display = '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';
          
          // Hide generate comic strip button and art style selector
          document.getElementById('generate-combined-image').style.display = 'none';
          document.getElementById('art-style-container').style.display = 'none';
          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;

        // After successfully generating dialogues, hide the generate dialogues button
        document.getElementById('generate-dialogues').style.display = 'none';
        
        // Show customization panel
        const customizationPanel = document.getElementById('customization-panel');
        customizationPanel.style.display = 'block';

        // 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';
        bubble.style.backgroundColor = 'rgba(255, 255, 255, 0.85)'; // Set initial opacity
        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);
        
        // Event Listener for Delete Button
        deleteBtn.addEventListener('click', () => {
          bubble.remove();
        });
        
        // Event Listener for Rotate Button
        rotateBtn.addEventListener('click', () => {
          rotateBubble(bubble);
        });
        
        // 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,
          }
        })
        .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,
          }
        })
        .gesturable({
          listeners: {
            move: rotateMoveListener,
          }
        });
    };

    // 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 Open the Customization Panel
    const openCustomizationPanel = (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');
    };

    // Close the customization panel when clicking outside
    document.addEventListener('click', (e) => {
      const customizationPanel = document.getElementById('customization-panel');
      const dialogueBubbles = document.querySelectorAll('.dialogue-bubble');
      
      // If click is outside panel and not on a dialogue bubble, close panel
      if (!e.target.closest('.customization-panel') && !e.target.closest('.dialogue-bubble')) {
        customizationPanel.style.display = 'none';
        dialogueBubbles.forEach(bubble => bubble.classList.remove('selected'));
      }
    });

    // Prevent panel from closing when clicking inside it
    document.querySelector('.customization-panel').addEventListener('click', (e) => {
      e.stopPropagation();
    });

    // Event Listener for Download Button
    document.getElementById('download-image').addEventListener('click', 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';
      }
    });

    // 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);
    };

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

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

  </script>
</body>
</html>
</main>
</html>