import { Platform } from "react-native"; import { getElectronHost } from "@/desktop/electron/host"; import type { BrowserKeyboardPolicy } from "@/desktop/browser/shortcuts"; import type { SessionInboundMessage, SessionOutboundMessage } from "@getpaseo/protocol/messages"; type BrowserAutomationExecuteRequest = Extract< SessionOutboundMessage, { type: "browser.automation.execute.request" } >; type BrowserAutomationExecuteResponse = Extract< SessionInboundMessage, { type: "browser.automation.execute.response" } >; export type DesktopNotificationPermission = "granted" | "denied" | "default"; export interface DesktopDialogAskOptions { title?: string; okLabel?: string; cancelLabel?: string; kind?: "info" | "warning" | "error"; } export interface DesktopDialogOpenOptions { title?: string; defaultPath?: string; directory?: boolean; createDirectory?: boolean; multiple?: boolean; filters?: Array<{ name: string; extensions: string[]; }>; } export interface DesktopDialogAskWithCheckboxOptions extends DesktopDialogAskOptions { checkboxLabel: string; checkboxChecked?: boolean; } export interface DesktopDialogAskWithCheckboxResult { confirmed: boolean; dontAskAgain: boolean; } export interface DesktopDialogBridge { ask?: (message: string, options?: DesktopDialogAskOptions) => Promise; askWithCheckbox?: ( message: string, options: DesktopDialogAskWithCheckboxOptions, ) => Promise; open?: (options?: DesktopDialogOpenOptions) => Promise; } export interface DesktopNotificationBridge { isSupported?: () => Promise; sendNotification?: ( payload: string | { title: string; body?: string; data?: Record }, ) => Promise; } export interface DesktopOpenerBridge { openUrl?: (url: string) => Promise; } export interface DesktopEditorTargetDescriptor { id: string; label: string; kind: "editor" | "file-manager"; icon: { kind: "image"; dataUrl: string } | { kind: "symbol"; name: "folder" | "terminal" }; } export interface DesktopEditorOpenTargetInput { editorId: string; workspacePath: string; filePath?: string; line?: number; column?: number; } export interface DesktopEditorBridge { listTargets?: () => Promise; openTarget?: (input: DesktopEditorOpenTargetInput) => Promise; } export interface DesktopWebUtilsBridge { getPathForFile?: (file: File) => string; } export interface DesktopMenuBridge { showContextMenu?: (input?: { kind?: "terminal"; hasSelection?: boolean }) => Promise; setCapturingShortcut?: (capturing: boolean) => Promise; } export interface DesktopWindowControlsOverlayUpdate { height?: number; backgroundColor?: string; foregroundColor?: string; trafficLightOffsetY?: number; } export interface DesktopWindowBridge { label?: string; toggleMaximize?: () => Promise; setFullscreen?: (fullscreen: boolean) => Promise; isFullscreen?: () => Promise; updateWindowControls?: (update: DesktopWindowControlsOverlayUpdate) => Promise; onResized?: ( handler: (event: TEvent) => void, ) => Promise<() => void> | (() => void); setBadgeCount?: (count?: number) => Promise; onDragDropEvent?: ( handler: (event: TEvent) => void, ) => Promise<() => void> | (() => void); } export interface DesktopWindowModuleBridge { openNew?: (options?: { pendingOpenProjectPath?: string | null }) => Promise; getCurrentWindow?: () => DesktopWindowBridge; } export interface DesktopEventsBridge { on?: (event: string, handler: (payload: unknown) => void) => Promise<() => void> | (() => void); } export interface DesktopAgentNavigationBridge { ready?: () => Promise<{ serverId: string; agentId: string } | null>; } export type DesktopBrowserShortcutEvent = | { browserId?: string; action: "focus-url" } | { browserId: string; action: "new-tab" }; export interface DesktopBrowserNewTabRequestEvent { sourceBrowserId: string; url: string; } export interface DesktopAttachedBrowserRegistration { browserId: string; workspaceId: string; webContentsId: number; } export interface DesktopBrowserBridge { setShortcutPolicy?: (input: BrowserKeyboardPolicy) => Promise; readonly profilePartition?: string; registerAttachedBrowser?: (input: DesktopAttachedBrowserRegistration) => Promise; unregisterWorkspaceBrowser?: (browserId: string) => Promise; setWorkspaceActiveBrowser?: (input: { workspaceId: string; browserId: string | null; }) => Promise; openDevTools?: (browserId: string) => Promise; clearProfile?: (legacyBrowserIds: string[]) => Promise; executeAutomationCommand?: ( request: BrowserAutomationExecuteRequest, ) => Promise; /** Capture a PNG screenshot of the guest viewport cropped to `rect`. */ captureElement?: ( browserId: string, rect: { x: number; y: number; width: number; height: number }, ) => Promise; /** Copy element text and/or an image to the system clipboard from main. */ copyElement?: (payload: { text?: string; imageDataUrl?: string }) => Promise; } export interface DesktopInvokeBridge { invoke?: (command: string, args?: Record) => Promise; } export interface DesktopHostBridge { platform?: string; invoke?: DesktopInvokeBridge["invoke"]; getPendingOpenProject?: () => Promise; agentNavigation?: DesktopAgentNavigationBridge; events?: DesktopEventsBridge; window?: DesktopWindowModuleBridge; dialog?: DesktopDialogBridge; notification?: DesktopNotificationBridge; opener?: DesktopOpenerBridge; editor?: DesktopEditorBridge; webUtils?: DesktopWebUtilsBridge; menu?: DesktopMenuBridge; browser?: DesktopBrowserBridge; } declare global { interface Window { paseoDesktop?: DesktopHostBridge; } } export function getDesktopHost(): DesktopHostBridge | null { if (Platform.OS !== "web") { return null; } return getElectronHost(); } export function isElectronRuntime(): boolean { return getDesktopHost() !== null; } export function isElectronRuntimeMac(): boolean { if (!isElectronRuntime()) { return false; } if (typeof navigator === "undefined") { return false; } const hostPlatform = getDesktopHost()?.platform?.toLowerCase(); if (hostPlatform === "darwin" || hostPlatform === "mac" || hostPlatform === "macos") { return true; } const ua = navigator.userAgent; return ua.includes("Mac OS") || ua.includes("Macintosh"); }