diff --git a/extension/content.js b/extension/content.js index 4e05c1c..fdf0d23 100644 --- a/extension/content.js +++ b/extension/content.js @@ -292,9 +292,11 @@ video.pause(); video.currentTime = payload.targetTime; pollSeekReady(payload.targetTime).then((ready) => { + chrome.runtime.sendMessage({ type: 'FORCE_SYNC_ACK' }); if (ready) { - chrome.runtime.sendMessage({ type: 'FORCE_SYNC_ACK' }); scheduleProactiveHeartbeat(); + } else { + reportLog('Force Sync: Seek ready timeout, proceeding anyway', 'warn'); } }); } @@ -397,8 +399,12 @@ const eventState = action === EVENTS.PLAY ? 'playing' : (action === EVENTS.PAUSE ? 'paused' : (action === EVENTS.SEEK ? 'seek' : null)); if (eventState && expectedEvents.has(eventState)) { - expectedEvents.delete(eventState); // Consume the match - return; // Ignore event caused by our programmatic action + expectedEvents.delete(eventState); + if (expectedTimeouts[eventState]) { + clearTimeout(expectedTimeouts[eventState]); + delete expectedTimeouts[eventState]; + } + return; } chrome.runtime.sendMessage({ @@ -430,7 +436,11 @@ // Step 1: Check expectedEvents (programmatic seek suppression) if (expectedEvents.has('seek')) { expectedEvents.delete('seek'); - lastReportedSeekTime = current; // Update baseline so next user seek is relative to here + if (expectedTimeouts['seek']) { + clearTimeout(expectedTimeouts['seek']); + delete expectedTimeouts['seek']; + } + lastReportedSeekTime = current; // No log — this is routine programmatic behavior (Force Sync, lobby, peer command) return; } diff --git a/extension/popup.js b/extension/popup.js index 3dbf920..d588bdb 100644 --- a/extension/popup.js +++ b/extension/popup.js @@ -182,7 +182,7 @@ function updateLastActionUI(state, peers) { grid.style.cssText = 'display:grid; grid-template-columns: repeat(auto-fill, minmax(36px, 1fr)); gap: 5px;'; peers.forEach(peer => { - const pId = typeof peer === 'object' ? peer.peerId : p; + const pId = typeof peer === 'object' ? peer.peerId : peer; if (pId === localPeerId) return; const pName = (typeof peer === 'object' && peer.username) ? peer.username : pId.substring(0, 4); const isAcked = state.acks.includes(pId) || pId === state.senderId; @@ -423,16 +423,17 @@ async function populateTabs(providedPeers = null, providedTargetTabId = null) { return true; }); + // Smart Matching Logic — exclude own tabTitle to prevent self-match (computed once) + const peerTitles = peerIds + .filter(p => (typeof p === 'object' ? p.peerId : p) !== localPeerId) + .map(p => (typeof p === 'object' ? p.tabTitle : null)) + .filter(t => t && t.length > 3); + filteredTabs.forEach(tab => { const option = document.createElement('option'); option.value = tab.id; const title = (tab.title || 'Loading...'); - // Smart Matching Logic — exclude own tabTitle to prevent self-match - const peerTitles = peerIds - .filter(p => (typeof p === 'object' ? p.peerId : p) !== localPeerId) - .map(p => (typeof p === 'object' ? p.tabTitle : null)) - .filter(t => t && t.length > 3); const isMatch = peerTitles.some(pt => { const t1 = title.toLowerCase(); const t2 = pt.toLowerCase(); @@ -914,6 +915,9 @@ async function refreshLogs() { chrome.runtime.onMessage.addListener((msg) => { if (msg.type === 'LOG_UPDATE') { refreshLogs(); + if (msg.log && msg.log.type === 'error') { + showError(msg.log.message); + } } else if (msg.type === 'ACTION_UPDATE') { chrome.runtime.sendMessage({ type: 'GET_STATUS' }, (res) => { if (res && res.peers) updateLastActionUI(msg.state, res.peers); @@ -936,8 +940,6 @@ chrome.runtime.onMessage.addListener((msg) => { updateHistory(msg.history); } else if (msg.type === 'ROOM_LIST') { updateRoomList(msg.rooms); - } else if (msg.type === 'LOG_UPDATE' && msg.log && msg.log.type === 'error') { - showError(msg.log.message); } else if (msg.type === 'JOIN_STATUS') { if (msg.success) { // Final confirmation of join from background