import { EventEmitter } from 'events'; import type { Frame, Page } from 'puppeteer'; import type { BrowserTab, BrowserSelectInfo, BrowserSelectResponse, BrowserContextMenuInfo, BrowserContextMenuResponse, BrowserContextMenuItem, BrowserNativePickerInfo } from './types'; import { resolveFrameTarget } from './frame-targeting'; import { debug } from '$shared/utils/logger'; import { nanoid } from 'nanoid'; /** * Read the current text selection from wherever it actually lives. * * A selection inside an iframe is invisible to the top document, so asking only * the main frame returns an empty string for exactly the pages most likely to * be embedding content. `cut` additionally deletes it in the frame that owns it. */ async function readSelection(page: Page, deleteAfter = false): Promise { for (const frame of page.frames()) { try { const text = await frame.evaluate((remove: boolean) => { const selected = window.getSelection()?.toString() ?? ''; if (selected && remove) document.execCommand('delete'); return selected; }, deleteAfter); if (text) return text; } catch { // Detached or cross-origin mid-navigation; keep looking. } } return ''; } /** * Browser Native UI Handler * * Handles OS-native UI elements that cannot be rendered in headless browser: * - and extract options */ async checkForSelect(sessionId: string, page: Page, x: number, y: number): Promise { try { // Generate unique select ID const selectId = nanoid(10); // Resolve which frame owns the point first. Evaluating against the main // frame would find the `