240 lines
6.8 KiB
JavaScript
240 lines
6.8 KiB
JavaScript
// Background service worker - manages WebSocket connections
|
|
|
|
import { TabManager } from './modules/tab-manager.js';
|
|
import {ConsoleLogEntry} from "./modules/models.js";
|
|
|
|
// Single source of truth for all tab state
|
|
const tabManager = new TabManager();
|
|
|
|
// Helper function to send connection state to content script
|
|
function sendConnectionStateToTab(tabId, connectionState) {
|
|
chrome.tabs.sendMessage(tabId, {
|
|
command: '_connectionStateChanged',
|
|
params: {
|
|
status: connectionState.status,
|
|
connected: connectionState.connected
|
|
}
|
|
}).catch(err => {
|
|
// Content script might not be injected yet, ignore error
|
|
console.debug('Could not send connection state to tab:', err);
|
|
});
|
|
}
|
|
|
|
// Listen for tab state changes
|
|
tabManager.addListener((tabId, event, tabState, data) => {
|
|
switch (event) {
|
|
case 'stateChanged':
|
|
// Broadcast to all ports for this tab
|
|
tabState.broadcastToPorts({
|
|
type: 'state',
|
|
tabId,
|
|
...tabState.getConnectionState()
|
|
});
|
|
|
|
// Send connection state to content script
|
|
const connectionState = tabState.getConnectionState();
|
|
sendConnectionStateToTab(tabId, connectionState);
|
|
|
|
// Update action badge for active tab
|
|
chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
|
|
if (tabs[0]?.id === tabId) {
|
|
updateActionBadge(tabState.getConnectionState());
|
|
}
|
|
});
|
|
break;
|
|
|
|
case 'messageReceived':
|
|
case 'messageSent':
|
|
// Broadcast updated messages to all ports
|
|
tabState.broadcastToPorts({
|
|
type: 'messages',
|
|
tabId,
|
|
messages: tabState.getMessages()
|
|
});
|
|
break;
|
|
|
|
case 'consoleLogAdded':
|
|
// Broadcast updated console count
|
|
tabState.broadcastToPorts({
|
|
type: 'consoleCount',
|
|
tabId,
|
|
count: tabState.getConsoleLogCount()
|
|
});
|
|
break;
|
|
|
|
case 'messagesCleared':
|
|
tabState.broadcastToPorts({
|
|
type: 'messages',
|
|
tabId,
|
|
messages: []
|
|
});
|
|
break;
|
|
|
|
case 'consoleLogsCleared':
|
|
tabState.broadcastToPorts({
|
|
type: 'consoleCount',
|
|
tabId,
|
|
count: 0
|
|
});
|
|
break;
|
|
}
|
|
});
|
|
|
|
// Update action badge based on connection status
|
|
function updateActionBadge(state) {
|
|
switch (state.status) {
|
|
case 'connected':
|
|
chrome.action.setBadgeText({ text: '✓' });
|
|
chrome.action.setBadgeBackgroundColor({ color: '#4caf50' });
|
|
break;
|
|
|
|
case 'retrying':
|
|
chrome.action.setBadgeText({ text: '↻' });
|
|
chrome.action.setBadgeBackgroundColor({ color: '#ff9800' });
|
|
break;
|
|
|
|
case 'disconnected':
|
|
default:
|
|
chrome.action.setBadgeText({ text: '' });
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Handle UI connections
|
|
chrome.runtime.onConnect.addListener((port) => {
|
|
port.onMessage.addListener((msg) => {
|
|
if (msg.type === 'subscribe' && msg.tabId) {
|
|
tabManager.addPort(msg.tabId, port);
|
|
} else if (msg.type === 'clearMessages' && msg.tabId) {
|
|
tabManager.clearMessages(msg.tabId);
|
|
} else if (msg.type === 'clearConsoleLogs' && msg.tabId) {
|
|
tabManager.clearConsoleLogs(msg.tabId);
|
|
}
|
|
});
|
|
|
|
port.onDisconnect.addListener(() => {
|
|
// Remove port from all tabs
|
|
tabManager.getAllTabs().forEach(tabState => {
|
|
tabState.removePort(port);
|
|
});
|
|
});
|
|
});
|
|
|
|
// Handle all messages
|
|
chrome.runtime.onMessage.addListener(async (request, sender, sendResponse) => {
|
|
// Handle content script messages
|
|
if (sender.tab) {
|
|
if (request.type === 'contentScriptReady') {
|
|
console.log(`Content script ready in tab ${sender.tab.id}`);
|
|
|
|
// Send current connection state to the newly ready content script
|
|
const tabState = tabManager.getTab(sender.tab.id);
|
|
if (tabState) {
|
|
const connectionState = tabState.getConnectionState();
|
|
sendConnectionStateToTab(sender.tab.id, connectionState);
|
|
}
|
|
|
|
sendResponse({ acknowledged: true });
|
|
return false;
|
|
}
|
|
|
|
if (request.type === 'connect') {
|
|
const result = await tabManager.connect(sender.tab.id);
|
|
sendResponse(result);
|
|
return true; // Keep message channel open for async response
|
|
}
|
|
|
|
if (request.type === 'disconnect') {
|
|
const result = tabManager.disconnect(sender.tab.id);
|
|
sendResponse(result);
|
|
return false;
|
|
}
|
|
|
|
if (request.type === 'openPopup') {
|
|
chrome.action.openPopup();
|
|
sendResponse({ ok: true });
|
|
return false;
|
|
}
|
|
|
|
if (request.type === 'mousePosition') {
|
|
// Store mouse position for the tab
|
|
const tabState = tabManager.getTab(sender.tab.id);
|
|
if (tabState) {
|
|
tabState.setMousePosition({ x: request.x, y: request.y });
|
|
}
|
|
return false;
|
|
}
|
|
|
|
if (request.type === 'consoleLog') {
|
|
// Handle console log from content script
|
|
const tabState = tabManager.getTab(sender.tab.id);
|
|
if (tabState) {
|
|
if (request.level === 'clear') {
|
|
// Clear console logs
|
|
tabManager.clearConsoleLogs(sender.tab.id);
|
|
} else {
|
|
// Add console log
|
|
tabManager.addConsoleLog(sender.tab.id, new ConsoleLogEntry(
|
|
request.level,
|
|
request.args,
|
|
request.stackTrace
|
|
));
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Handle messages from popup/panel (not from content scripts)
|
|
if (!sender.tab) {
|
|
if (request.type === 'connect' && request.tabId) {
|
|
const result = await tabManager.connect(request.tabId);
|
|
sendResponse(result);
|
|
return true; // Keep message channel open for async response
|
|
}
|
|
|
|
if (request.type === 'disconnect' && request.tabId) {
|
|
const result = tabManager.disconnect(request.tabId);
|
|
sendResponse(result);
|
|
return false;
|
|
}
|
|
|
|
if (request.type === 'getState' && request.tabId) {
|
|
const tabState = tabManager.getTab(request.tabId);
|
|
sendResponse(tabState ? tabState.getConnectionState() : { connected: false, status: 'disconnected' });
|
|
return false;
|
|
}
|
|
}
|
|
});
|
|
|
|
// Update badge when active tab changes
|
|
chrome.tabs.onActivated.addListener((activeInfo) => {
|
|
const tabState = tabManager.getTab(activeInfo.tabId);
|
|
if (tabState) {
|
|
updateActionBadge(tabState.getConnectionState());
|
|
} else {
|
|
updateActionBadge({ status: 'disconnected' });
|
|
}
|
|
});
|
|
|
|
// Clean up when tabs are closed
|
|
chrome.tabs.onRemoved.addListener((tabId) => {
|
|
tabManager.removeTab(tabId);
|
|
});
|
|
|
|
chrome.runtime.onInstalled.addListener((details) => {
|
|
if (details.reason === 'install') {
|
|
const destination = { url: 'https://to.kap.co/kapture-welcome' };
|
|
// Navigate the current active tab instead of creating a new one
|
|
chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
|
|
if (tabs[0]) {
|
|
chrome.tabs.update(tabs[0].id, destination);
|
|
}
|
|
else {
|
|
// Fallback: create a new tab if no active tab is found
|
|
chrome.tabs.create(destination);
|
|
}
|
|
});
|
|
}
|
|
});
|