2026-01-20 17:36:33 +08:00
|
|
|
/**
|
|
|
|
|
* Message Protocol for PageAgentExt
|
|
|
|
|
*
|
2026-01-26 19:17:52 +08:00
|
|
|
* MV3 Compliant Architecture:
|
2026-01-24 19:29:27 +08:00
|
|
|
* - SidePanel hosts the agent, all state lives there
|
|
|
|
|
* - Background (SW) is a stateless message relay
|
|
|
|
|
* - Content Script runs PageController
|
|
|
|
|
*
|
|
|
|
|
* Message flows:
|
2026-01-26 19:17:52 +08:00
|
|
|
* 1. RPC: SidePanel → SW → ContentScript → sendResponse (PageController calls)
|
2026-01-24 19:29:27 +08:00
|
|
|
* 2. Query: ContentScript → SW → SidePanel → SW → ContentScript (mask state check)
|
|
|
|
|
* 3. Events: SW → SidePanel (tab events from chrome.tabs API)
|
2026-01-20 17:36:33 +08:00
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
// ============================================================================
|
2026-01-24 19:29:27 +08:00
|
|
|
// Shared Types
|
2026-01-20 17:36:33 +08:00
|
|
|
// ============================================================================
|
|
|
|
|
|
|
|
|
|
/** Action result from PageController operations */
|
|
|
|
|
export interface ActionResult {
|
|
|
|
|
success: boolean
|
|
|
|
|
message: string
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** Browser state for LLM consumption */
|
|
|
|
|
export interface BrowserState {
|
|
|
|
|
url: string
|
|
|
|
|
title: string
|
|
|
|
|
header: string
|
|
|
|
|
content: string
|
|
|
|
|
footer: string
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** Scroll options */
|
|
|
|
|
export interface ScrollOptions {
|
|
|
|
|
down: boolean
|
|
|
|
|
numPages: number
|
|
|
|
|
pixels?: number
|
|
|
|
|
index?: number
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** Horizontal scroll options */
|
|
|
|
|
export interface ScrollHorizontallyOptions {
|
|
|
|
|
right: boolean
|
|
|
|
|
pixels: number
|
|
|
|
|
index?: number
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ============================================================================
|
2026-01-24 19:29:27 +08:00
|
|
|
// Message Types
|
2026-01-20 17:36:33 +08:00
|
|
|
// ============================================================================
|
|
|
|
|
|
2026-01-24 19:29:27 +08:00
|
|
|
/** Message type identifier */
|
|
|
|
|
type MessageType =
|
2026-01-26 19:17:52 +08:00
|
|
|
| 'rpc:call' // SidePanel → SW: RPC call to content script (response via sendResponse)
|
2026-01-24 19:29:27 +08:00
|
|
|
| 'cs:rpc' // SW → ContentScript: Forwarded RPC call
|
|
|
|
|
| 'cs:query' // ContentScript → SW: Query to sidepanel
|
|
|
|
|
| 'query:response' // SW → ContentScript: Query response
|
|
|
|
|
| 'tab:event' // SW → SidePanel: Tab event notification
|
|
|
|
|
|
|
|
|
|
/** Base message structure */
|
|
|
|
|
interface BaseMessage {
|
|
|
|
|
type: MessageType
|
|
|
|
|
id: string // Unique message ID for request-response matching
|
2026-01-20 17:36:33 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ============================================================================
|
2026-01-24 19:29:27 +08:00
|
|
|
// RPC Messages (SidePanel ↔ SW ↔ ContentScript)
|
2026-01-20 17:36:33 +08:00
|
|
|
// ============================================================================
|
|
|
|
|
|
2026-01-24 19:29:27 +08:00
|
|
|
/** SidePanel → SW: Request to call PageController method */
|
|
|
|
|
export interface RPCCallMessage extends BaseMessage {
|
|
|
|
|
type: 'rpc:call'
|
|
|
|
|
tabId: number
|
2026-01-26 18:55:54 +08:00
|
|
|
method: string
|
2026-01-24 19:29:27 +08:00
|
|
|
args: unknown[]
|
|
|
|
|
}
|
2026-01-20 17:36:33 +08:00
|
|
|
|
2026-01-24 19:29:27 +08:00
|
|
|
/** SW → ContentScript: Forwarded RPC call */
|
|
|
|
|
export interface CSRPCMessage extends BaseMessage {
|
|
|
|
|
type: 'cs:rpc'
|
2026-01-26 18:55:54 +08:00
|
|
|
method: string
|
2026-01-24 19:29:27 +08:00
|
|
|
args: unknown[]
|
2026-01-20 17:36:33 +08:00
|
|
|
}
|
|
|
|
|
|
2026-01-21 18:46:50 +08:00
|
|
|
// ============================================================================
|
2026-01-24 19:29:27 +08:00
|
|
|
// Query Messages (ContentScript → SW → SidePanel)
|
2026-01-21 18:46:50 +08:00
|
|
|
// ============================================================================
|
|
|
|
|
|
2026-01-24 19:29:27 +08:00
|
|
|
/** Query types that content script can ask */
|
|
|
|
|
export type QueryType = 'shouldShowMask'
|
|
|
|
|
|
|
|
|
|
/** ContentScript → SW: Query to sidepanel */
|
|
|
|
|
export interface CSQueryMessage extends BaseMessage {
|
|
|
|
|
type: 'cs:query'
|
|
|
|
|
queryType: QueryType
|
|
|
|
|
tabId: number
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** SW → ContentScript: Query response */
|
|
|
|
|
export interface QueryResponseMessage extends BaseMessage {
|
|
|
|
|
type: 'query:response'
|
|
|
|
|
result: unknown
|
2026-01-21 18:46:50 +08:00
|
|
|
}
|
|
|
|
|
|
2026-01-20 17:36:33 +08:00
|
|
|
// ============================================================================
|
2026-01-24 19:29:27 +08:00
|
|
|
// Tab Event Messages (SW → SidePanel)
|
2026-01-20 17:36:33 +08:00
|
|
|
// ============================================================================
|
|
|
|
|
|
2026-01-24 19:29:27 +08:00
|
|
|
/** Tab event types */
|
2026-01-24 19:37:46 +08:00
|
|
|
export type TabEventType = 'removed' | 'updated' | 'activated' | 'windowFocusChanged'
|
2026-01-24 19:29:27 +08:00
|
|
|
|
|
|
|
|
/** SW → SidePanel: Tab event notification */
|
|
|
|
|
export interface TabEventMessage extends BaseMessage {
|
|
|
|
|
type: 'tab:event'
|
|
|
|
|
eventType: TabEventType
|
|
|
|
|
tabId: number
|
|
|
|
|
data?: {
|
|
|
|
|
// For 'updated' events
|
|
|
|
|
status?: string
|
|
|
|
|
url?: string
|
2026-01-24 19:37:46 +08:00
|
|
|
// For 'activated' events
|
|
|
|
|
windowId?: number
|
|
|
|
|
// For 'windowFocusChanged' events
|
|
|
|
|
focused?: boolean
|
2026-01-24 19:29:27 +08:00
|
|
|
}
|
2026-01-20 17:36:33 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ============================================================================
|
2026-01-24 19:29:27 +08:00
|
|
|
// Union Types
|
2026-01-20 17:36:33 +08:00
|
|
|
// ============================================================================
|
|
|
|
|
|
2026-01-24 19:29:27 +08:00
|
|
|
/** All message types */
|
|
|
|
|
export type ExtensionMessage =
|
|
|
|
|
| RPCCallMessage
|
|
|
|
|
| CSRPCMessage
|
|
|
|
|
| CSQueryMessage
|
|
|
|
|
| QueryResponseMessage
|
|
|
|
|
| TabEventMessage
|
2026-01-20 17:36:33 +08:00
|
|
|
|
2026-01-24 19:29:27 +08:00
|
|
|
// ============================================================================
|
|
|
|
|
// Utility Functions
|
|
|
|
|
// ============================================================================
|
2026-01-20 17:36:33 +08:00
|
|
|
|
2026-01-24 19:29:27 +08:00
|
|
|
/** Generate unique message ID */
|
|
|
|
|
export function generateMessageId(): string {
|
|
|
|
|
return `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`
|
|
|
|
|
}
|
2026-01-21 18:46:50 +08:00
|
|
|
|
2026-01-26 19:17:52 +08:00
|
|
|
/** Known message types for type guard */
|
|
|
|
|
const MESSAGE_TYPES = new Set<string>([
|
|
|
|
|
'rpc:call',
|
|
|
|
|
'cs:rpc',
|
|
|
|
|
'cs:query',
|
|
|
|
|
'query:response',
|
|
|
|
|
'tab:event',
|
|
|
|
|
])
|
|
|
|
|
|
|
|
|
|
/** Type guard - checks if message has a known type */
|
2026-01-24 19:29:27 +08:00
|
|
|
export function isExtensionMessage(msg: unknown): msg is ExtensionMessage {
|
2026-01-26 19:17:52 +08:00
|
|
|
return typeof msg === 'object' && msg !== null && MESSAGE_TYPES.has((msg as any).type)
|
2026-01-24 19:29:27 +08:00
|
|
|
}
|