feat(server,frontend): real-time sync architecture with message accumulator

- Add message-accumulator.js: cascades diff-based message accumulation
- Add 3-second cascade polling with broadcastToAll (was undefined!)
- Add /api/bridge/approve endpoint: tries accept/reject Step→Command→Terminal
- Add persistent approve/reject buttons in chat header toolbar
- Frontend: loadSessionMessages (trajectory + accumulated), applyNewMessages (WS push)
- Status change detection: _prevStatusKey tracking prevents unnecessary re-renders
- actionInProgress flag prevents DOM replacement during button fetch
- Known issues: Trajectory 341 hard limit, Cascade no command-approval state
This commit is contained in:
2026-03-08 14:05:59 +09:00
parent e7521433cb
commit 1060476113
16 changed files with 940 additions and 209 deletions

View File

@@ -1,5 +1,6 @@
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
@@ -7,9 +8,12 @@
<meta name="description" content="Antigravity IDE 원격 제어 대시보드">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
<link
href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap"
rel="stylesheet">
<link rel="stylesheet" href="css/style.css">
</head>
<body>
<div id="app">
<!-- Header -->
@@ -17,12 +21,12 @@
<div class="header-left">
<div class="logo">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none">
<circle cx="12" cy="12" r="10" stroke="url(#grad)" stroke-width="2"/>
<circle cx="12" cy="12" r="4" fill="url(#grad)"/>
<circle cx="12" cy="12" r="10" stroke="url(#grad)" stroke-width="2" />
<circle cx="12" cy="12" r="4" fill="url(#grad)" />
<defs>
<linearGradient id="grad" x1="0" y1="0" x2="24" y2="24">
<stop offset="0%" stop-color="#818cf8"/>
<stop offset="100%" stop-color="#6366f1"/>
<stop offset="0%" stop-color="#818cf8" />
<stop offset="100%" stop-color="#6366f1" />
</linearGradient>
</defs>
</svg>
@@ -45,7 +49,8 @@
<h2>세션</h2>
<button class="btn-icon" id="addSessionBtn" title="세션 추가">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/>
<line x1="12" y1="5" x2="12" y2="19" />
<line x1="5" y1="12" x2="19" y2="12" />
</svg>
</button>
</div>
@@ -59,8 +64,9 @@
<!-- No session selected state -->
<div class="empty-state" id="emptyState">
<div class="empty-icon">
<svg width="64" height="64" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1" opacity="0.3">
<path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/>
<svg width="64" height="64" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1"
opacity="0.3">
<path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" />
</svg>
</div>
<h3>세션을 선택하세요</h3>
@@ -79,6 +85,10 @@
<button class="view-tab active" data-view="chat" id="tabChat">💬 채팅</button>
<button class="view-tab" data-view="mirror" id="tabMirror">🖥️ 미러</button>
</div>
<div class="approve-controls" id="approveControls">
<button class="btn-approve" id="btnApprove" title="현재 요청 승인">✅ 승인</button>
<button class="btn-reject" id="btnReject" title="현재 요청 거절">❌ 거절</button>
</div>
<button class="btn-sm" id="screenshotBtn" title="스크린샷">📷</button>
<button class="btn-sm" id="reconnectBtn" title="재연결">🔄</button>
</div>
@@ -89,14 +99,11 @@
</div>
</div>
<div class="chat-input-area">
<textarea
id="chatInput"
placeholder="Antigravity에 보낼 메시지를 입력하세요..."
rows="1"
></textarea>
<textarea id="chatInput" placeholder="Antigravity에 보낼 메시지를 입력하세요..." rows="1"></textarea>
<button class="btn-send" id="sendBtn" title="전송">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<line x1="22" y1="2" x2="11" y2="13"/><polygon points="22 2 15 22 11 13 2 9 22 2"/>
<line x1="22" y1="2" x2="11" y2="13" />
<polygon points="22 2 15 22 11 13 2 9 22 2" />
</svg>
</button>
</div>
@@ -115,7 +122,7 @@
<span>스크린샷 미리보기</span>
<button class="btn-icon" id="closeScreenshot"></button>
</div>
<img id="screenshotImage" alt="Antigravity Screenshot"/>
<img id="screenshotImage" alt="Antigravity Screenshot" />
</div>
</div>
</section>
@@ -158,4 +165,5 @@
<script src="js/mirror-panel.js"></script>
<script src="js/app.js"></script>
</body>
</html>
</html>