<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>GPT SSE Client</title>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/marked/4.0.2/marked.min.js"></script>
    <style>
        body {
            font-family: Arial, sans-serif;
            padding: 20px;
            max-width: 1400px;
            margin: auto;
        }
        #messages {
            border: 1px solid #ccc;
            padding: 10px;
            height: 800px;
            overflow-y: auto;
            margin-bottom: 20px;
        }
        .message {
            margin-bottom: 15px;
            padding: 10px;
            border-radius: 5px;
        }
        .user-message {
            background-color: #e6f3ff;
        }
        .assistant-message {
            background-color: #f0f0f0;
        }
        pre {
            background-color: #f4f4f4;
            padding: 10px;
            border-radius: 4px;
            overflow-x: auto;
        }
        code {
            font-family: 'Courier New', Courier, monospace;
        }
        #modeToggle {
            margin-bottom: 10px;
        }
        .generated-image {
            max-width: 100%;
            height: auto;
            margin-top: 10px;
        }
    </style>
</head>
<body>
    <h1>Chat with GPT</h1>
    <div id="modeToggle">
        <input type="radio" id="chatMode" name="mode" value="chat" checked>
        <label for="chatMode">Chat Mode</label>
        <input type="radio" id="imageMode" name="mode" value="image">
        <label for="imageMode">Image Generation Mode</label>
    </div>
    <div id="messages"></div>
    <form id="messageForm">
        <textarea id="userMessage" rows="4" placeholder="Type your message here..."></textarea>
        <br>
        <button type="submit">Send</button>
    </form>
    <script>
        const messagesDiv = document.getElementById('messages');
        let currentMessageElement = null;
        let messages = JSON.parse(localStorage.getItem('chatHistory') || '[]');
        let isImageMode = false;

        // Display existing messages on load
        messages.forEach(msg => displayMessage(msg.content, msg.role));

        document.getElementById('modeToggle').addEventListener('change', (event) => {
            isImageMode = event.target.value === 'image';
            if (isImageMode) {
                messages = []; // Clear history for image mode
                messagesDiv.innerHTML = ''; // Clear displayed messages
            } else {
                // Restore chat history from localStorage when switching back to chat mode
                messages = JSON.parse(localStorage.getItem('chatHistory') || '[]');
                messagesDiv.innerHTML = ''; // Clear displayed messages
                messages.forEach(msg => displayMessage(msg.content, msg.role));
            }
        });

        document.getElementById('messageForm').addEventListener('submit', async (event) => {
            event.preventDefault();
            const userMessage = document.getElementById('userMessage').value;
            if (!userMessage.trim()) return;
            
            // Add user message to history and display
            if (!isImageMode) {
                messages.push({ role: "user", content: userMessage });
            }
            displayMessage(userMessage, 'user');
            document.getElementById('userMessage').value = '';

            // Prepare for assistant's response
            currentMessageElement = document.createElement('div');
            currentMessageElement.classList.add('message', 'assistant-message');
            messagesDiv.appendChild(currentMessageElement);

            try {
                const response = await fetch('/api/gpt', {
                    method: 'POST',
                    headers: { 'Content-Type': 'application/json' },
                    body: JSON.stringify({
                        messages: isImageMode ? [{ role: "user", content: userMessage }] : messages,
                        stream: !isImageMode, // Don't use streaming for image mode
                        markdown: true,
                        model: isImageMode ? "dalle2" : "gpt-4o"
                    })
                });

                if (isImageMode) {
                    const data = await response.json();
                    if (data.images && data.images.length > 0) {
                        displayImage(data.images[0], data.prompt);
                    } else {
                        updateMessage('Error: No image generated.');
                    }
                } else {
                    const reader = response.body.pipeThrough(new TextDecoderStream()).getReader();
                    let buffer = '';
                    let assistantMessage = '';

                    while (true) {
                        const { value, done } = await reader.read();
                        if (done) break;
                        
                        buffer += value;
                        const lines = buffer.split('\n');
                        
                        for (let i = 0; i < lines.length - 1; i++) {
                            const line = lines[i].trim();
                            if (line.startsWith('data: ')) {
                                const jsonStr = line.slice(6);
                                if (jsonStr === '[DONE]') {
                                    break;
                                }
                                try {
                                    const data = JSON.parse(jsonStr);
                                    if (data.message !== null) {
                                        assistantMessage = data.message;
                                        updateMessage(assistantMessage);
                                    }
                                } catch (e) {
                                    console.error('Error parsing JSON:', e);
                                }
                            }
                        }
                        
                        buffer = lines[lines.length - 1];
                    }

                    // Add assistant's message to history if not in image mode
                    messages.push({ role: "assistant", content: assistantMessage });
                    // Save updated history to localStorage
                    localStorage.setItem('chatHistory', JSON.stringify(messages));
                }

            } catch (error) {
                console.error('Error:', error);
                updateMessage('Error occurred while fetching the response.');
            }

            currentMessageElement = null;
        });

        function displayMessage(content, role) {
            const messageElement = document.createElement('div');
            messageElement.classList.add('message', `${role}-message`);
            messageElement.innerHTML = role === 'assistant' ? marked.parse(content) : content;
            messagesDiv.appendChild(messageElement);
            messagesDiv.scrollTop = messagesDiv.scrollHeight;
        }

        function updateMessage(message) {
            if (currentMessageElement) {
                currentMessageElement.innerHTML = marked.parse(message);
                messagesDiv.scrollTop = messagesDiv.scrollHeight;
            }
        }

        function displayImage(imageUrl, prompt) {
            if (currentMessageElement) {
                const imageElement = document.createElement('img');
                imageElement.src = imageUrl;
                imageElement.alt = prompt;
                imageElement.classList.add('generated-image');
                
                currentMessageElement.innerHTML = `<p>Generated image for prompt: "${prompt}"</p>`;
                currentMessageElement.appendChild(imageElement);
                messagesDiv.scrollTop = messagesDiv.scrollHeight;
            }
        }
    </script>
</body>
</html>

перенеси это в preact