diff --git a/server/cdp-client.js b/server/cdp-client.js index 745a499..406161f 100644 --- a/server/cdp-client.js +++ b/server/cdp-client.js @@ -282,7 +282,8 @@ class CDPClient { /** * Antigravity 채팅 입력창에 메시지를 전송 * - * Antigravity는 textarea가 아닌 contenteditable div를 사용함 + * 단일 Runtime.evaluate로 요소 찾기 + 텍스트 입력 수행 + * (2단계 분리 시 셀렉터 이스케이프 손실 버그 방지) */ async sendMessage(text) { if (!this.connected || !this.client) { @@ -290,11 +291,10 @@ class CDPClient { } try { - // 1) contenteditable 입력창 찾기 및 포커스 - const { result: focusResult } = await this.client.Runtime.evaluate({ + const safeText = JSON.stringify(text); + const { result } = await this.client.Runtime.evaluate({ expression: ` (function() { - // Antigravity 입력창 셀렉터 (우선순위 순) const selectors = [ '#antigravity\\\\.agentSidePanelInputBox [contenteditable="true"][role="textbox"]', '.antigravity-agent-side-panel [contenteditable="true"][role="textbox"]', @@ -302,47 +302,34 @@ class CDPClient { '[contenteditable="true"][role="textbox"]', ]; + let el = null; for (const sel of selectors) { - const el = document.querySelector(sel); - if (el) { - el.focus(); - return { found: true, sel: sel }; - } + el = document.querySelector(sel); + if (el) break; } - return { found: false }; - })() - `, - returnByValue: true, - }); + if (!el) return { success: false, error: 'input not found' }; - if (!focusResult.value?.found) { - return { success: false, error: '채팅 입력창을 찾을 수 없습니다' }; - } - - // 2) 텍스트 입력 (contenteditable용 — execCommand 방식) - await this.client.Runtime.evaluate({ - expression: ` - (function() { - const el = document.querySelector('${focusResult.value.sel}'); - if (!el) return; - el.focus(); - - // 기존 내용 선택 후 삭제 + const selection = window.getSelection(); const range = document.createRange(); range.selectNodeContents(el); selection.removeAllRanges(); selection.addRange(range); - - // 텍스트 삽입 (React/Preact 호환) - document.execCommand('insertText', false, ${JSON.stringify(text)}); + + document.execCommand('insertText', false, ${safeText}); + + return { success: true, text: el.textContent.substring(0, 50) }; })() `, returnByValue: true, }); - // 3) Enter 키 전송 + if (!result.value?.success) { + return { success: false, error: result.value?.error || '입력 실패' }; + } + + // Enter 키 전송 await this.client.Input.dispatchKeyEvent({ type: 'keyDown', key: 'Enter', @@ -358,7 +345,7 @@ class CDPClient { windowsVirtualKeyCode: 13, }); - console.log(`[CDP] 메시지 전송: "${text.substring(0, 50)}..."`); + console.log(`[CDP] 메시지 전송: "${text.substring(0, 50)}"`); return { success: true }; } catch (err) { console.error('[CDP] 메시지 전송 오류:', err.message); @@ -378,11 +365,12 @@ class CDPClient { return; } - const html = await this.scrapeChatDOM(); - if (html && html !== this.lastChatHTML) { - this.lastChatHTML = html; + const messages = await this.scrapeChatDOM(); + const hash = JSON.stringify(messages); + if (messages && messages.length > 0 && hash !== this.lastChatHTML) { + this.lastChatHTML = hash; if (this.onChatUpdate) { - this.onChatUpdate(html); + this.onChatUpdate(messages); } } }, intervalMs);