| |
| document.addEventListener('DOMContentLoaded', function() { |
| const chatMessages = document.querySelector('.chat-messages'); |
| const textarea = document.querySelector('textarea'); |
| const sendButton = document.querySelector('.send-btn'); |
| const controlBtn = document.getElementById('controlBtn'); |
| const stopBtn = document.getElementById('stopBtn'); |
| const exportStoryBtn = document.getElementById('exportStoryBtn'); |
| |
| |
| const clientId = Math.random().toString(36).substring(7); |
| |
| |
| const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'; |
| const ws = new WebSocket(`${protocol}//${window.location.host}/ws/${clientId}`); |
| window.ws = ws |
|
|
| let isPlaying = false; |
| |
| let currentSceneFilter = null; |
| let startButtonText = translations[window.i18n.currentLang]['start']; |
| |
| controlBtn.addEventListener('click', function() { |
| if (!isPlaying) { |
| |
| ws.send(JSON.stringify({ |
| type: 'control', |
| action: 'start' |
| })); |
| startButtonText = translations[window.i18n.currentLang]['pause'] |
| controlBtn.innerHTML = `<i class="fas fa-pause"></i><span data-i18n="pause">${startButtonText}</span>`; |
| isPlaying = true; |
| } else { |
| |
| ws.send(JSON.stringify({ |
| type: 'control', |
| action: 'pause' |
| })); |
| startButtonText = translations[window.i18n.currentLang]['start'] |
| controlBtn.innerHTML = `<i class="fas fa-play"></i><span data-i18n="pause">${startButtonText}</span>`; |
| isPlaying = false; |
| } |
| }); |
|
|
| |
| stopBtn.addEventListener('click', function() { |
| ws.send(JSON.stringify({ |
| type: 'control', |
| action: 'stop' |
| })); |
| controlBtn.innerHTML = '<i class="fas fa-play"></i><span>开始</span>'; |
| isPlaying = false; |
| }); |
|
|
| |
| ws.onopen = function() { |
| console.log('WebSocket连接已建立'); |
| addSystemMessage('连接已建立'); |
| }; |
| |
| ws.onclose = function() { |
| console.log('WebSocket连接已关闭'); |
| addSystemMessage('连接已断开'); |
| }; |
| |
| ws.onerror = function(error) { |
| console.error('WebSocket错误:', error); |
| addSystemMessage('连接错误'); |
| }; |
| |
| ws.onmessage = function(event) { |
| const message = JSON.parse(event.data); |
| console.log('Received message:', message); |
| |
| const wsEvent = new CustomEvent('websocket-message', { |
| detail: message |
| }); |
| window.dispatchEvent(wsEvent); |
|
|
| |
| if (message.type === 'message') { |
| |
| const sceneNumber = message.data.scene; |
| if (sceneNumber !== undefined) { |
| |
| window.dispatchEvent(new CustomEvent('scene-update', { |
| detail: { scene: sceneNumber } |
| })); |
| } |
| if (message.data.type === 'system') { |
| addSystemMessage(message.data.text); |
| } |
| else if (message.data.type === 'story') { |
| |
| const messageElement = document.createElement('div'); |
| messageElement.className = 'message story-message'; |
| messageElement.innerHTML = ` |
| <div class="content"> |
| <div class="header"> |
| <span class="username">故事总结</span> |
| <span class="timestamp">${message.data.timestamp}</span> |
| </div> |
| <div class="text">${message.data.text}</div> |
| </div> |
| `; |
| chatMessages.appendChild(messageElement); |
| chatMessages.scrollTop = chatMessages.scrollHeight; |
| } |
| else { |
| renderMessage(message.data); |
| } |
| } |
| else if (message.type === 'initial_data') { |
| |
| if (message.data.history_messages) { |
| loadHistoryMessages(message.data.history_messages); |
| } |
| } |
| }; |
|
|
| function loadHistoryMessages(messages) { |
| |
| chatMessages.innerHTML = ''; |
| |
| messages.forEach(message => { |
| renderMessage(message); |
| }); |
|
|
| chatMessages.scrollTop = chatMessages.scrollHeight; |
| |
| console.log(`Loaded ${messages.length} historical messages`); |
| } |
|
|
| |
| function renderMessage(message) { |
| const messageElement = document.createElement('div'); |
| messageElement.className = 'message'; |
| messageElement.dataset.timestamp = message.timestamp; |
| messageElement.dataset.username = message.username; |
| |
| |
| if (message.scene !== undefined) { |
| messageElement.dataset.scene = message.scene; |
| console.log(`Rendering message for scene ${message.scene}`); |
| } |
| |
| messageElement.innerHTML = ` |
| <div class="icon"> |
| <img src="${message.icon}" alt="${message.username}"> |
| </div> |
| <div class="content"> |
| <div class="header"> |
| <span class="username">${message.username}</span> |
| <span class="timestamp">${message.timestamp}</span> |
| </div> |
| <div class="text-wrapper"> |
| <div class="text">${message.text}</div> |
| <button class="edit-icon"><i class="fas fa-pen"></i></button> |
| <div class="edit-buttons" style="display: none;"> |
| <button class="edit-btn save-btn">保存</button> |
| <button class="edit-btn cancel-btn">取消</button> |
| </div> |
| </div> |
| </div> |
| `; |
| |
| |
| const textElement = messageElement.querySelector('.text'); |
| const editButtons = messageElement.querySelector('.edit-buttons'); |
| const editIcon = messageElement.querySelector('.edit-icon'); |
| |
| |
| let originalText = message.text; |
| let isEditing = false; |
| |
| |
| editIcon.addEventListener('click', () => { |
| if (!isEditing) { |
| isEditing = true; |
| editButtons.style.display = 'flex'; |
| textElement.classList.add('editing'); |
| textElement.setAttribute('contenteditable', 'true'); |
| textElement.focus(); |
| } |
| }); |
| |
| |
| messageElement.querySelector('.save-btn').addEventListener('click', () => { |
| const newText = textElement.textContent.trim(); |
| if (newText !== originalText) { |
| |
| ws.send(JSON.stringify({ |
| type: 'edit_message', |
| data: { |
| uuid: message.uuid, |
| text: newText, |
| } |
| })); |
| originalText = newText; |
| } |
| exitEditMode(); |
| }); |
| |
| |
| messageElement.querySelector('.cancel-btn').addEventListener('click', () => { |
| textElement.textContent = originalText; |
| exitEditMode(); |
| }); |
| |
| |
| textElement.addEventListener('keydown', (e) => { |
| if (e.key === 'Enter' && !e.shiftKey) { |
| e.preventDefault(); |
| messageElement.querySelector('.save-btn').click(); |
| } |
| if (e.key === 'Escape') { |
| messageElement.querySelector('.cancel-btn').click(); |
| } |
| }); |
| |
| |
| document.addEventListener('click', function(event) { |
| if (isEditing && !messageElement.contains(event.target)) { |
| |
| exitEditMode(); |
| textElement.textContent = originalText; |
| } |
| }); |
| |
| |
| messageElement.addEventListener('click', function(event) { |
| event.stopPropagation(); |
| }); |
| |
| function exitEditMode() { |
| isEditing = false; |
| editButtons.style.display = 'none'; |
| textElement.classList.remove('editing'); |
| textElement.blur(); |
| } |
| |
| |
| if (currentSceneFilter !== null) { |
| messageElement.style.display = |
| (String(message.scene) === String(currentSceneFilter)) ? '' : 'none'; |
| } |
|
|
| chatMessages.appendChild(messageElement); |
| chatMessages.scrollTop = chatMessages.scrollHeight; |
| } |
| |
| function addSystemMessage(text) { |
| const messageElement = document.createElement('div'); |
| messageElement.className = 'message system'; |
| messageElement.innerHTML = ` |
| <div class="content"> |
| <div class="text">${text}</div> |
| </div> |
| `; |
| chatMessages.appendChild(messageElement); |
| chatMessages.scrollTop = chatMessages.scrollHeight; |
| } |
|
|
| |
| function sendMessage() { |
| const text = textarea.value.trim(); |
| if (text && ws.readyState === WebSocket.OPEN) { |
| const message = { |
| type: 'user_message', |
| text: text, |
| timestamp: new Date().toLocaleString() |
| }; |
| ws.send(JSON.stringify(message)); |
| textarea.value = ''; |
| } |
| } |
|
|
| |
| sendButton.addEventListener('click', sendMessage); |
|
|
| |
| textarea.addEventListener('keypress', function(e) { |
| if (e.key === 'Enter' && !e.shiftKey) { |
| e.preventDefault(); |
| sendMessage(); |
| } |
| }); |
| |
| |
| window.addEventListener('scene-selected', (event) => { |
| const selectedScene = event.detail.scene; |
| currentSceneFilter = selectedScene; |
| |
| |
| document.querySelectorAll('.message').forEach(msg => { |
| if (selectedScene === null) { |
| msg.style.display = ''; |
| } else { |
| msg.style.display = |
| (msg.dataset.scene === String(selectedScene)) ? '' : 'none'; |
| } |
| }); |
| |
| |
| const visibleMessages = document.querySelectorAll('.message[style=""]'); |
| if (visibleMessages.length > 0) { |
| visibleMessages[0].scrollIntoView({ behavior: 'smooth' }); |
| } |
| }); |
|
|
| |
| exportStoryBtn.addEventListener('click', function() { |
| ws.send(JSON.stringify({ |
| type: 'generate_story' |
| })); |
| }); |
| }); |
|
|