var ws = null; var charactersData = {}; var presets = []; function connectWebSocket() { var wsUrl = 'ws://' + window.location.hostname + ':{{WS_PORT}}/retargeting'; ws = new WebSocket(wsUrl); ws.onopen = function() { updateConnectionStatus(true); // 연결 즉시 캐릭터 목록 요청 send({ action: 'refresh' }); }; ws.onclose = function() { updateConnectionStatus(false); setTimeout(connectWebSocket, 3000); }; ws.onerror = function(e) { console.error('WS Error:', e); }; ws.onmessage = function(e) { handleMessage(JSON.parse(e.data)); }; } function updateConnectionStatus(c) { var s = document.getElementById('connectionStatus'); s.textContent = c ? '연결됨' : '연결 끊김'; s.className = 'connection-status ' + (c ? 'connected' : 'disconnected'); } function handleMessage(d) { if (d.type === 'characterList') updateCharacterList(d.characters); else if (d.type === 'characterData') updateCharacterData(d); else if (d.type === 'handPosePresets') { presets = d.presets; updateAllPresetGrids(); } else if (d.type === 'status') showToast(d.message, d.success ? 'success' : 'error'); } function updateCharacterList(chars) { var container = document.getElementById('charactersContainer'); container.innerHTML = ''; if (chars.length === 0) { container.innerHTML = '
'; return; } for (var i = 0; i < chars.length; i++) { var charId = chars[i]; container.appendChild(createCharacterPanel(charId)); send({ action: 'getCharacterData', characterId: charId }); } } function createCharacterPanel(charId) { var panel = document.createElement('div'); panel.className = 'character-panel'; panel.id = 'panel_' + charId; panel.innerHTML = '