explain this app:     <script>
        document.addEventListener('DOMContentLoaded', () => {
            // Elements
            const characterPresets = document.getElementById('characterPresets');
            const storyPresets = document.getElementById('storyPresets');
            const testScenes = document.getElementById('testScenes');
            const generateStoryBtn = document.getElementById('generateStoryBtn');
            const generateImageBtn = document.getElementById('generateImageBtn');
            const generateDialogueBtn = document.getElementById('generateDialogueBtn');
            const loading = document.getElementById('loading');
            const comicPreview = document.getElementById('comicPreview');

            // Preset Data
            const characterData = {
                'superheroes': {
                    char1Name: 'Captain Thunder',
                    char1Desc: 'A confident superhero with electric powers, wears a blue and gold costume',
                    char2Name: 'Dr. Shadow',
                    char2Desc: 'A mysterious anti-hero with dark matter powers, wears a black cloak'
                },
                'wizards': {
                    char1Name: 'Merlin Jr.',
                    char1Desc: 'A young wizard apprentice with wild red hair and star-covered robes',
                    char2Name: 'Professor Spellbound',
                    char2Desc: 'An elderly wizard teacher with a long silver beard and crystal spectacles'
                },
                'robots': {
                    char1Name: 'B33P',
                    char1Desc: 'A curious little robot with expressive LED eyes and retractable arms',
                    char2Name: 'Professor Jones',
                    char2Desc: 'A brilliant roboticist with messy hair and a lab coat'
                }
            };

            const storyOutlines = {
                'hero': "A new superhero learns to control their powers with help from a mentor, but things go hilariously wrong during training.",
                'magic': "A young wizard attempts their first major spell in class, but accidentally turns their teacher into a talking teapot.",
                'robot': "A newly activated robot explores the world for the first time with their inventor, discovering the joy and confusion of human customs."
            };

            // Event Listeners
            characterPresets.addEventListener('change', () => {
                const preset = characterPresets.value;
                if (characterData[preset]) {
                    document.getElementById('char1Name').value = characterData[preset].char1Name;
                    document.getElementById('char1Description').value = characterData[preset].char1Desc;
                    document.getElementById('char2Name').value = characterData[preset].char2Name;
                    document.getElementById('char2Description').value = characterData[preset].char2Desc;
                } else {
                    document.getElementById('char1Name').value = '';
                    document.getElementById('char1Description').value = '';
                    document.getElementById('char2Name').value = '';
                    document.getElementById('char2Description').value = '';
                }
            });

            storyPresets.addEventListener('change', () => {
                const preset = storyPresets.value;
                document.getElementById('storyOutline').value = storyOutlines[preset] || '';
            });

            testScenes.addEventListener('change', () => {
                const scene = testScenes.value;
                document.getElementById('sceneDescription').value = scene || '';
            });

            generateStoryBtn.addEventListener('click', generateStoryScene);
            generateImageBtn.addEventListener('click', generateImages);
            generateDialogueBtn.addEventListener('click', generateDialogues);

            // Functions

            function showLoading() {
                loading.style.display = 'block';
            }

            function hideLoading() {
                loading.style.display = 'none';
            }

            async function generateStoryScene() {
                const char1Name = document.getElementById('char1Name').value.trim();
                const char1Desc = document.getElementById('char1Description').value.trim();
                const char2Name = document.getElementById('char2Name').value.trim();
                const char2Desc = document.getElementById('char2Description').value.trim();
                const storyOutline = document.getElementById('storyOutline').value.trim();
                const sceneDescription = document.getElementById('sceneDescription').value.trim();

                if (!char1Name || !char2Name || !storyOutline || !sceneDescription) {
                    alert('Please fill in all required fields.');
                    return;
                }

                generateStoryBtn.disabled = true;
                showLoading();

                try {
                    // Construct the prompt for the story generation
                    const prompt = `Generate a short story based on the following details:\nCharacters:\n1. ${char1Name}: ${char1Desc}\n2. ${char2Name}: ${char2Desc}\nStory Outline: ${storyOutline}\nScene Description: ${sceneDescription}\n\nProvide a coherent narrative that unfolds across four panels.`;

                    // Make POST request to text.pollinations.ai with the specified parameters
                    const response = await fetch('https://text.pollinations.ai/', {
                        method: 'POST',
                        headers: {
                            'Content-Type': 'application/json'
                        },
                        body: JSON.stringify({
                            messages: [
                                { role: 'system', content: 'You are a creative storyteller.' },
                                { role: 'user', content: prompt }
                            ],
                            model: 'openai', // You can switch to 'mistral' if desired
                            seed: 42,
                            jsonMode: true
                        })
                    });

                    if (!response.ok) {
                        throw new Error('Failed to generate story.');
                    }

                    const data = await response.json();
                    const story = data.choices && data.choices[0] && data.choices[0].message && data.choices[0].message.content
                        ? data.choices[0].message.content
                        : 'A mysterious story unfolds...';

                    // Display Story in Preview
                    comicPreview.innerHTML = `
                        <div class="comic-story">
                            <h3>Story Scene</h3>
                            <p>${story.replace(/\n/g, '<br>')}</p>
                        </div>`;

                    // Show Generate Image Button
                    generateImageBtn.style.display = 'inline-block';
                } catch (error) {
                    console.error('Story generation error:', error);
                    alert('Error generating story. Please try again.');

                    // Provide a fallback story instead of showing error
                    const fallbackStory = `In a delightful turn of events, ${char1Name} and ${char2Name} find themselves in ${sceneDescription}. 
Following the outline of "${storyOutline}", their adventure unfolds across four memorable scenes:

Panel 1: ${char1Name} (${char1Desc}) initiates the action.
Panel 2: ${char2Name} (${char2Desc}) responds with unexpected flair.
Panel 3: The situation intensifies as both characters interact.
Panel 4: A satisfying resolution brings their story to a close.`;

                    comicPreview.innerHTML = `
                        <div class="comic-story">
                            <h3>Story Scene</h3>
                            <p>${fallbackStory.replace(/\n/g, '<br>')}</p>
                            <small>(Using fallback story due to API unavailability)</small>
                        </div>`;

                    // Show Generate Image Button
                    generateImageBtn.style.display = 'inline-block';
                } finally {
                    hideLoading();
                    generateStoryBtn.disabled = false;
                }
            }

            async function generateImages() {
                const char1Name = document.getElementById('char1Name').value.trim();
                const char1Desc = document.getElementById('char1Description').value.trim();
                const char2Name = document.getElementById('char2Name').value.trim();
                const char2Desc = document.getElementById('char2Description').value.trim();
                const storyOutline = document.getElementById('storyOutline').value.trim();
                const sceneDescription = document.getElementById('sceneDescription').value.trim();
                const style = document.getElementById('artStyle').value;

                showLoading();
                generateImageBtn.disabled = true;

                try {
                    // Check if story is generated
                    const storyElement = comicPreview.querySelector('.comic-story');
                    if (!storyElement) {
                        throw new Error('Please generate the story first.');
                    }

                    // Create single container for 4 panels
                    const panelContainer = document.createElement('div');
                    panelContainer.style.cssText = `
                        display: grid;
                        grid-template-columns: repeat(2, 1fr);
                        grid-template-rows: repeat(2, 1fr);
                        gap: 10px;
                        width: 100%;
                        min-height: 800px;
                    `;

                    // Clear previous panels
                    comicPreview.innerHTML = '';
                    comicPreview.appendChild(panelContainer);

                    // Generate images for each panel
                    for (let i = 0; i < 4; i++) {
                        const panel = document.createElement('div');
                        panel.className = 'comic-panel';
                        panel.style.margin = '0'; // Remove default margin
                        panelContainer.appendChild(panel);

                        // Construct image generation prompt
                        const panelPrompts = [
                            "Scene 1: Beginning of the story",
                            "Scene 2: Action develops",
                            "Scene 3: Climax moment",
                            "Scene 4: Resolution"
                        ];
                        
                        const imagePrompt = `${style} style comic scene: ${panelPrompts[i]}`;

                        // Image generation parameters
                        const width = 512; // Smaller size for better layout
                        const height = 512;
                        const model = 'default';
                        const seed = Math.floor(Math.random() * 100000);
                        const imageUrl = `https://image.pollinations.ai/prompt/${encodeURIComponent(imagePrompt)}?width=${width}&height=${height}&model=${model}&seed=${seed}&nologo=true`;

                        // Create and load image
                        const img = document.createElement('img');
                        img.alt = `Panel ${i + 1}`;
                        img.src = imageUrl;

                        img.onload = () => {
                            panel.innerHTML = `
                                <div class="panel-number" style="position:absolute; top:5px; left:5px; background: rgba(0,0,0,0.1); padding:2px 5px; border-radius:3px; font-size: 12px;">Panel ${i + 1}</div>
                            `;
                            panel.appendChild(img);
                        };

                        img.onerror = () => {
                            panel.innerHTML = `
                                <div class="panel-number" style="position:absolute; top:5px; left:5px; background: rgba(0,0,0,0.1); padding:2px 5px; border-radius:3px; font-size: 12px;">Panel ${i + 1}</div>
                                <p style="color: red; text-align: center;">Image failed to load.</p>
                            `;
                        };
                    }

                    // Show Generate Dialogue Button after images are loaded
                    generateDialogueBtn.style.display = 'inline-block';
                } catch (error) {
                    console.error('Image generation error:', error);
                    alert('Error generating images. Please try again.');
                } finally {
                    hideLoading();
                    generateImageBtn.disabled = false;
                }
            }

            async function generateDialogues() {
                const char1Name = document.getElementById('char1Name').value.trim();
                const char2Name = document.getElementById('char2Name').value.trim();

                showLoading();
                generateDialogueBtn.disabled = true;

                try {
                    // Generate default dialogue templates based on characters
                    const defaultPrompt = `Generate a conversation between ${char1Name} and ${char2Name}`;
                    
                    // Fetch dialogues for both characters
                    const [dialogue1, dialogue2] = await Promise.all([
                        generateDialogue(`${defaultPrompt} for ${char1Name}`, 'char1'),
                        generateDialogue(`${defaultPrompt} for ${char2Name}`, 'char2')
                    ]);

                    // Add speech bubbles to each panel
                    const panels = document.querySelectorAll('.comic-panel');
                    panels.forEach((panel, index) => {
                        // Character 1 Dialogue
                        const bubble1 = document.createElement('div');
                        bubble1.className = 'speech-bubble char1-bubble';
                        bubble1.style.top = `${randomPercentage()}%`;
                        bubble1.style.left = `${randomPercentage()}%`;
                        bubble1.textContent = dialogue1[index] || `${char1Name} says something...`;

                        // Character 2 Dialogue
                        const bubble2 = document.createElement('div');
                        bubble2.className = 'speech-bubble char2-bubble';
                        bubble2.style.top = `${randomPercentage()}%`;
                        bubble2.style.left = `${randomPercentage()}%`;
                        bubble2.textContent = dialogue2[index] || `${char2Name} responds...`;

                        // Add edit buttons to both bubbles
                        [bubble1, bubble2].forEach(bubble => {
                            const editBtn = document.createElement('span');
                            editBtn.className = 'edit-bubble';
                            editBtn.innerHTML = '✎';
                            editBtn.onclick = (e) => {
                                e.stopPropagation();
                                const newText = prompt('Edit dialogue:', bubble.textContent);
                                if (newText) bubble.textContent = newText;
                                bubble.appendChild(editBtn); // Re-append edit button
                            };
                            bubble.appendChild(editBtn);
                            makeDraggable(bubble);
                        });

                        panel.appendChild(bubble1);
                        panel.appendChild(bubble2);
                    });

                } catch (error) {
                    console.error('Dialogue generation error:', error);
                    alert('Error generating dialogues. Please try again.');

                    // Provide fallback dialogues
                    const fallbackDialogues = {
                        char1: [
                            "This is incredible!",
                            "What just occurred?",
                            "Watch me handle this!",
                            "That was something else..."
                        ],
                        char2: [
                            "Prove it then!",
                            "Interesting development...",
                            "I'm not so sure about this...",
                            "Great teamwork!"
                        ]
                    };

                    const panels = document.querySelectorAll('.comic-panel');
                    panels.forEach((panel, index) => {
                        // Character 1 Dialogue
                        const bubble1 = document.createElement('div');
                        bubble1.className = 'speech-bubble char1-bubble';
                        bubble1.style.top = `${randomPercentage()}%`;
                        bubble1.style.left = `${randomPercentage()}%`;
                        bubble1.textContent = fallbackDialogues.char1[index] || `${char1Name} says something...`;

                        // Character 2 Dialogue
                        const bubble2 = document.createElement('div');
                        bubble2.className = 'speech-bubble char2-bubble';
                        bubble2.style.top = `${randomPercentage()}%`;
                        bubble2.style.left = `${randomPercentage()}%`;
                        bubble2.textContent = fallbackDialogues.char2[index] || `${char2Name} responds...`;

                        // Add edit buttons to both bubbles
                        [bubble1, bubble2].forEach(bubble => {
                            const editBtn = document.createElement('span');
                            editBtn.className = 'edit-bubble';
                            editBtn.innerHTML = '✎';
                            editBtn.onclick = (e) => {
                                e.stopPropagation();
                                const newText = prompt('Edit dialogue:', bubble.textContent);
                                if (newText) bubble.textContent = newText;
                                bubble.appendChild(editBtn); // Re-append edit button
                            };
                            bubble.appendChild(editBtn);
                            makeDraggable(bubble);
                        });

                        panel.appendChild(bubble1);
                        panel.appendChild(bubble2);
                    });
                } finally {
                    hideLoading();
                    generateDialogueBtn.disabled = false;
                }
            }

            // Function to generate dialogue using Pollinations AI
            async function generateDialogue(prompt, characterStyle) {
                try {
                    const response = await fetch('https://text.pollinations.ai/', {
                        method: 'POST',
                        headers: {
                            'Content-Type': 'application/json'
                        },
                        body: JSON.stringify({
                            messages: [
                                { role: 'system', content: 'You are a creative dialogue generator for comic strips.' },
                                { role: 'user', content: prompt }
                            ],
                            model: 'openai', // You can switch to 'mistral' if desired
                            seed: Math.floor(Math.random() * 100000), // Random seed for variability
                            jsonMode: true
                        })
                    });

                    if (!response.ok) {
                        throw new Error('Failed to generate dialogue.');
                    }

                    const data = await response.json();
                    const dialogue = data.choices && data.choices[0] && data.choices[0].message && data.choices[0].message.content
                        ? data.choices[0].message.content
                        : '...';

                    // Split dialogues by lines for multiple panels
                    const dialogues = dialogue.split('\n').map(line => line.trim()).filter(line => line.length > 0);

                    return dialogues.length === 4 ? dialogues : Array(4).fill(dialogue);
                } catch (error) {
                    console.error(`Dialogue generation error for ${characterStyle}:`, error);
                    throw error;
                }
            }

            function makeDraggable(element) {
                let isDragging = false;
                let offsetX, offsetY;

                element.addEventListener('mousedown', (e) => {
                    isDragging = true;
                    const rect = element.getBoundingClientRect();
                    offsetX = e.clientX - rect.left;
                    offsetY = e.clientY - rect.top;
                    document.addEventListener('mousemove', onMouseMove);
                    document.addEventListener('mouseup', onMouseUp);
                });

                function onMouseMove(e) {
                    if (!isDragging) return;
                    const panelRect = element.parentElement.getBoundingClientRect();
                    let x = e.clientX - panelRect.left - offsetX;
                    let y = e.clientY - panelRect.top - offsetY;

                    // Constrain within panel
                    x = Math.max(0, Math.min(x, panelRect.width - element.offsetWidth));
                    y = Math.max(0, Math.min(y, panelRect.height - element.offsetHeight));

                    element.style.left = `${x}px`;
                    element.style.top = `${y}px`;
                }

                function onMouseUp() {
                    isDragging = false;
                    document.removeEventListener('mousemove', onMouseMove);
                    document.removeEventListener('mouseup', onMouseUp);
                }
            }

            function randomPercentage() {
                return Math.floor(Math.random() * 70) + 15; // Random percentage between 15% and 85%
            }

            // Initialize
        });
    </script>