// Claude Code Bridge - UI with MCP Server Polling
const SERVER_URL = 'http://localhost:3001';
const API_KEY = 'dev-secret-key';
const POLL_INTERVAL = 500; // ms
let isPolling = false;
let pollTimer: number | null = null;
// UI Elements
const statusEl = document.getElementById('status') as HTMLParagraphElement;
const commandCountEl = document.getElementById('command-count') as HTMLSpanElement;
const toggleBtn = document.getElementById('toggle-btn') as HTMLButtonElement;
const serverUrlEl = document.getElementById('server-url') as HTMLSpanElement;
let commandsExecuted = 0;
function updateStatus(message: string, isError = false) {
if (statusEl) {
statusEl.textContent = message;
statusEl.style.color = isError ? '#e74c3c' : '#27ae60';
}
}
function updateCommandCount() {
if (commandCountEl) {
commandCountEl.textContent = String(commandsExecuted);
}
}
async function pollServer() {
if (!isPolling) return;
try {
const response = await fetch(`${SERVER_URL}/commands`, {
method: 'GET',
headers: {
'Accept': 'application/json',
'X-API-Key': API_KEY
}
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
const commands = await response.json();
if (commands.length > 0) {
updateStatus(`${commands.length}개 명령 처리 중...`);
for (const cmd of commands) {
// 이미지 명령은 특별 처리 - URL에서 이미지 데이터 가져오기
if (cmd.params.imageUrl) {
try {
const response = await fetch(cmd.params.imageUrl);
const blob = await response.blob();
const arrayBuffer = await blob.arrayBuffer();
const imageData = Array.from(new Uint8Array(arrayBuffer));
parent.postMessage({
pluginMessage: {
type: cmd.type,
commandId: cmd.id,
imageData: imageData,
...cmd.params
}
}, '*');
} catch (err: any) {
updateStatus('이미지 로드 실패: ' + err.message, true);
}
} else {
// 일반 명령
parent.postMessage({
pluginMessage: {
type: cmd.type,
commandId: cmd.id,
...cmd.params
}
}, '*');
}
commandsExecuted++;
updateCommandCount();
}
}
} catch (error: any) {
updateStatus(`서버 오류: ${error.message}`, true);
}
// Schedule next poll
if (isPolling) {
pollTimer = window.setTimeout(pollServer, POLL_INTERVAL);
}
}
function startPolling() {
isPolling = true;
updateStatus('연결됨 - 명령 대기 중...');
if (toggleBtn) {
toggleBtn.textContent = '중지';
toggleBtn.style.background = '#e74c3c';
}
pollServer();
}
function stopPolling() {
isPolling = false;
if (pollTimer) {
clearTimeout(pollTimer);
pollTimer = null;
}
updateStatus('중지됨');
if (toggleBtn) {
toggleBtn.textContent = '시작';
toggleBtn.style.background = '#18a0fb';
}
}
// Toggle button handler
if (toggleBtn) {
toggleBtn.onclick = () => {
if (isPolling) {
stopPolling();
} else {
startPolling();
}
};
}
// Display server URL
if (serverUrlEl) {
serverUrlEl.textContent = SERVER_URL;
}
// Listen for results from main plugin code
window.onmessage = async (event) => {
const msg = event.data.pluginMessage;
if (msg && msg.type === 'result') {
if (msg.success) {
updateStatus(`명령 실행 완료${msg.nodeId ? ` (ID: ${msg.nodeId})` : ''}`);
} else {
updateStatus(`오류: ${msg.error}`, true);
}
// Send result back to MCP server
if (msg.commandId) {
try {
await fetch(`${SERVER_URL}/commands/${msg.commandId}/result`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-API-Key': API_KEY
},
body: JSON.stringify({
nodeId: msg.nodeId || null,
error: msg.error || null
})
});
} catch (error: any) {
console.error('Failed to send result to server:', error);
}
}
}
};
// Auto-start polling when plugin opens
startPolling();