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