feat(ext): handcraft the whole ext from scratch
AI coding doesn't work for MV3 extensions. Threading was an unfixable mess. Removed everything and rebuilt by hand.
This commit is contained in:
@@ -1,114 +1,44 @@
|
||||
/**
|
||||
* Background Script (Service Worker) - Stateless Message Relay
|
||||
*
|
||||
* Completely stateless. Only two responsibilities:
|
||||
* 1. Relay AGENT_TO_PAGE messages from SidePanel to ContentScript
|
||||
* 2. Broadcast TAB_CHANGE events to all extension pages
|
||||
*/
|
||||
import {
|
||||
type AgentToPageMessage,
|
||||
type TabChangeMessage,
|
||||
isExtensionMessage,
|
||||
} from '../agent/protocol'
|
||||
import { handlePageControlMessage } from '@/agent/RemotePageController.background'
|
||||
import { handleTabControlMessage } from '@/agent/TabsController.background'
|
||||
|
||||
// ============================================================================
|
||||
// Message Relay
|
||||
// ============================================================================
|
||||
function handleUtilsMessage(
|
||||
message: { type: 'UTILS'; action: string; payload: any },
|
||||
sender: chrome.runtime.MessageSender,
|
||||
sendResponse: (response: unknown) => void
|
||||
): boolean {
|
||||
const { action, payload } = message
|
||||
|
||||
chrome.runtime.onMessage.addListener(
|
||||
(
|
||||
message: unknown,
|
||||
_sender: chrome.runtime.MessageSender,
|
||||
sendResponse: (response?: unknown) => void
|
||||
): boolean => {
|
||||
if (!isExtensionMessage(message)) {
|
||||
switch (action) {
|
||||
case 'get_tab_info': {
|
||||
chrome.tabs
|
||||
.get(payload.tabId)
|
||||
.then((tab) => {
|
||||
const result = { title: tab.title || '', url: tab.url || '' }
|
||||
sendResponse(result)
|
||||
})
|
||||
.catch((error) => {
|
||||
sendResponse({ error: error instanceof Error ? error.message : String(error) })
|
||||
})
|
||||
return true // async response
|
||||
}
|
||||
|
||||
default:
|
||||
sendResponse({ error: `Unknown TAB_CONTROL action: ${action}` })
|
||||
return false
|
||||
}
|
||||
|
||||
if (message.type === 'AGENT_TO_PAGE') {
|
||||
handleAgentToPage(message as AgentToPageMessage, sendResponse)
|
||||
return true // Async response
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
|
||||
if (message.type === 'TAB_CONTROL') {
|
||||
return handleTabControlMessage(message, sender, sendResponse)
|
||||
} else if (message.type === 'PAGE_CONTROL') {
|
||||
return handlePageControlMessage(message, sender, sendResponse)
|
||||
} else if (message.type !== 'UTILS') {
|
||||
return handleUtilsMessage(message, sender, sendResponse)
|
||||
} else {
|
||||
sendResponse({ error: 'Unknown message type' })
|
||||
return false
|
||||
}
|
||||
)
|
||||
|
||||
/**
|
||||
* Forward RPC call from SidePanel to ContentScript
|
||||
*/
|
||||
async function handleAgentToPage(
|
||||
msg: AgentToPageMessage,
|
||||
sendResponse: (response: { success: boolean; result?: unknown; error?: string }) => void
|
||||
): Promise<void> {
|
||||
const { tabId, method, args } = msg
|
||||
|
||||
try {
|
||||
// Forward directly to content script, same message format
|
||||
const result = await chrome.tabs.sendMessage(tabId, msg)
|
||||
sendResponse({ success: true, result })
|
||||
} catch (error) {
|
||||
sendResponse({
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : String(error),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Tab Event Broadcasting
|
||||
// ============================================================================
|
||||
|
||||
function broadcastTabChange(message: TabChangeMessage): void {
|
||||
chrome.runtime.sendMessage(message).catch(() => {
|
||||
// No listeners (sidepanel not open)
|
||||
})
|
||||
}
|
||||
|
||||
chrome.tabs.onRemoved.addListener((tabId) => {
|
||||
broadcastTabChange({
|
||||
type: 'TAB_CHANGE',
|
||||
eventType: 'removed',
|
||||
tabId,
|
||||
})
|
||||
})
|
||||
|
||||
chrome.tabs.onUpdated.addListener((tabId, changeInfo) => {
|
||||
if (!changeInfo.status) return
|
||||
|
||||
broadcastTabChange({
|
||||
type: 'TAB_CHANGE',
|
||||
eventType: 'updated',
|
||||
tabId,
|
||||
data: {
|
||||
status: changeInfo.status,
|
||||
url: changeInfo.url,
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
chrome.tabs.onActivated.addListener((activeInfo) => {
|
||||
broadcastTabChange({
|
||||
type: 'TAB_CHANGE',
|
||||
eventType: 'activated',
|
||||
tabId: activeInfo.tabId,
|
||||
data: {
|
||||
windowId: activeInfo.windowId,
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
chrome.windows.onFocusChanged.addListener((windowId) => {
|
||||
const focused = windowId !== chrome.windows.WINDOW_ID_NONE
|
||||
broadcastTabChange({
|
||||
type: 'TAB_CHANGE',
|
||||
eventType: 'windowFocusChanged',
|
||||
tabId: -1,
|
||||
data: {
|
||||
windowId: focused ? windowId : undefined,
|
||||
focused,
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
// ============================================================================
|
||||
|
||||
Reference in New Issue
Block a user