import type { VcsCreateRefInput, VcsCreateRefResult, VcsCreateWorktreeInput, VcsCreateWorktreeResult, VcsInitInput, VcsListRefsInput, VcsListRefsResult, VcsPullInput, VcsPullResult, VcsRemoveWorktreeInput, VcsSwitchRefInput, VcsSwitchRefResult, GitPreparePullRequestThreadInput, GitPreparePullRequestThreadResult, GitPullRequestRefInput, GitResolvePullRequestResult, VcsStatusInput, VcsStatusResult, } from "./git.ts"; import type { ReviewDiffFileContentsInput, ReviewDiffFileContentsResult, ReviewDiffPreviewInput, ReviewDiffPreviewResult, } from "./review.ts"; import type { FilesystemBrowseInput, FilesystemBrowseResult } from "./filesystem.ts"; import type { AssetCreateUrlInput, AssetCreateUrlResult } from "./assets.ts"; import type { ProjectListEntriesInput, ProjectListEntriesResult, ProjectReadFileInput, ProjectReadFileResult, ProjectSearchEntriesInput, ProjectSearchEntriesResult, ProjectWriteFileInput, ProjectWriteFileResult, } from "./project.ts"; import type { TerminalAttachInput, TerminalAttachStreamEvent, TerminalClearInput, TerminalCloseInput, TerminalMetadataStreamEvent, TerminalOpenInput, TerminalResizeInput, TerminalRestartInput, TerminalSessionSnapshot, TerminalWriteInput, } from "./terminal.ts"; import * as Schema from "effect/Schema"; import type { DiscoveredLocalServerList, PreviewCloseInput, PreviewEvent, PreviewListInput, PreviewListResult, PreviewNavigateInput, PreviewOpenInput, PreviewRefreshInput, PreviewReportStatusInput, PreviewResizeInput, PreviewSessionSnapshot, } from "./preview.ts"; import { PreviewAutomationClickInput, PreviewAutomationEvaluateInput, PreviewAutomationHost, PreviewAutomationHostFocus, PreviewAutomationPressInput, PreviewAutomationResponse, PreviewAutomationScrollInput, PreviewAutomationSnapshot, PreviewAutomationStatus, PreviewAutomationStreamEvent, PreviewAutomationTypeInput, PreviewAutomationWaitForInput, } from "./previewAutomation.ts"; import type { ClientOrchestrationCommand, OrchestrationGetFullThreadDiffInput, OrchestrationGetFullThreadDiffResult, OrchestrationGetTurnDiffInput, OrchestrationGetTurnDiffResult, OrchestrationShellSnapshot, OrchestrationShellStreamItem, OrchestrationSubscribeThreadInput, OrchestrationThreadStreamItem, } from "./orchestration.ts"; import { SnapShotSource } from "./orchestration.ts"; import { EnvironmentId, TrimmedNonEmptyString } from "./baseSchemas.ts"; import { BrowserProfileId } from "./browserProfile.ts"; import type { BrowserImportResult, BrowserImportSource, BrowserImportSourceId, } from "./browserImport.ts"; import { AuthAccessTokenResult, AuthSessionState, AuthWebSocketTicketResult } from "./auth.ts"; import { AdvertisedEndpoint } from "./remoteAccess.ts"; import { ExecutionEnvironmentDescriptor } from "./environment.ts"; import { type ClientSettings, type QuitConfirmationMode, SnapShotShortcut } from "./settings.ts"; import type { EditorId } from "./editor.ts"; import type { SourceControlCloneRepositoryInput, SourceControlCloneRepositoryResult, SourceControlPublishRepositoryInput, SourceControlPublishRepositoryResult, SourceControlRepositoryInfo, SourceControlRepositoryLookupInput, } from "./sourceControl.ts"; import type { DesktopAppActivationRequest, DesktopAppActivationResponse, } from "./desktopAppActivation.ts"; export interface ContextMenuItem { id: T; label: string; destructive?: boolean; disabled?: boolean; /** Renders as a non-interactive section header label. Web fallback only — stripped on desktop native menus. */ header?: boolean; /** Icon keyword resolved by the web fallback. Stripped on desktop native menus. */ icon?: string; /** Inserts a visual section divider immediately before this item. */ separatorBefore?: boolean; children?: readonly ContextMenuItem[]; } export type QuitShortcutHintEvent = | { readonly state: "down"; readonly mode: Exclude } | { readonly state: "up" }; export interface ContextMenuItemSchemaType { readonly id: string; readonly label: string; readonly destructive?: boolean; readonly disabled?: boolean; readonly header?: boolean; readonly icon?: string; readonly separatorBefore?: boolean; readonly children?: readonly ContextMenuItemSchemaType[]; } export const ContextMenuItemSchema: Schema.Codec = Schema.Struct({ id: Schema.String, label: Schema.String, destructive: Schema.optionalKey(Schema.Boolean), disabled: Schema.optionalKey(Schema.Boolean), header: Schema.optionalKey(Schema.Boolean), icon: Schema.optionalKey(Schema.String), separatorBefore: Schema.optionalKey(Schema.Boolean), children: Schema.optionalKey( Schema.Array( Schema.suspend((): Schema.Codec => ContextMenuItemSchema), ), ), }); export type DesktopUpdateStatus = | "disabled" | "idle" | "checking" | "up-to-date" | "available" | "downloading" | "downloaded" | "error"; export type DesktopRuntimeArch = "arm64" | "x64" | "other"; export type DesktopTheme = "light" | "dark" | "system"; export type DesktopUpdateChannel = "latest" | "nightly"; export type DesktopAppStageLabel = "Alpha" | "Dev" | "Nightly"; export const DesktopUpdateStatusSchema = Schema.Literals([ "disabled", "idle", "checking", "up-to-date", "available", "downloading", "downloaded", "error", ]); export const DesktopRuntimeArchSchema = Schema.Literals(["arm64", "x64", "other"]); export const DesktopThemeSchema = Schema.Literals(["light", "dark", "system"]); export const DesktopUpdateChannelSchema = Schema.Literals(["latest", "nightly"]); export const DesktopAppStageLabelSchema = Schema.Literals(["Alpha", "Dev", "Nightly"]); export interface DesktopAppBranding { baseName: string; stageLabel: DesktopAppStageLabel; displayName: string; } export const DesktopAppBrandingSchema = Schema.Struct({ baseName: Schema.String, stageLabel: DesktopAppStageLabelSchema, displayName: Schema.String, }); export const DesktopSnapShotMode = Schema.Literals(["direct", "portal", "unavailable"]); export type DesktopSnapShotMode = typeof DesktopSnapShotMode.Type; export const DesktopCaptureExtensionState = Schema.Struct({ status: Schema.Literals([ "not-installed", "disabled", "enabled", "restart-required", "update-required", "extensions-disabled", "unsupported", "error", ]), message: Schema.String, }); export type DesktopCaptureExtensionState = typeof DesktopCaptureExtensionState.Type; export const DesktopCaptureHelperState = Schema.Struct({ status: Schema.Literals(["not-installed", "update-required", "ready", "error"]), message: Schema.String, feedbackAvailable: Schema.optional(Schema.Boolean), }); export type DesktopCaptureHelperState = typeof DesktopCaptureHelperState.Type; export const DesktopSnapShotSetupAction = Schema.Literals([ "install-extension", "enable-extension", "disable-extension", "install-kde-helper", "remove-kde-helper", "install-hyprland-helper", "remove-hyprland-helper", "test-mac-capture", "allow-screen-recording", "allow-accessibility", "retry-shortcut", ]); export type DesktopSnapShotSetupAction = typeof DesktopSnapShotSetupAction.Type; export const DesktopCaptureConfigRequest = Schema.Struct({ operation: Schema.Literals(["install", "remove"]), chooseFile: Schema.Boolean, shortcut: Schema.optional(Schema.String.check(Schema.isMaxLength(80))), }); export type DesktopCaptureConfigRequest = typeof DesktopCaptureConfigRequest.Type; export const DesktopCaptureConfigPreview = Schema.Struct({ id: Schema.String, path: Schema.String, resolvedPath: Schema.String, before: Schema.String, after: Schema.String, shortcut: Schema.String, operation: Schema.Literals(["install", "remove"]), }); export type DesktopCaptureConfigPreview = typeof DesktopCaptureConfigPreview.Type; export const DesktopCaptureConfigApplied = Schema.Struct({ backupPath: Schema.NullOr(Schema.String), warning: Schema.NullOr(Schema.String), }); export type DesktopCaptureConfigApplied = typeof DesktopCaptureConfigApplied.Type; export const DesktopSnapShotState = Schema.Struct({ mode: DesktopSnapShotMode, windows: Schema.optional(Schema.Boolean), linuxDesktop: Schema.optional(Schema.Literals(["gnome", "kde", "niri", "hyprland"])), linuxBackend: Schema.optional( Schema.Literals(["screenshot-portal", "gnome-extension", "niri", "kde", "hyprland", "picker"]), ), linuxFeedbackAvailable: Schema.optional(Schema.Boolean), shortcut: SnapShotShortcut, shortcutRegistered: Schema.Boolean, shortcutPending: Schema.optional(Schema.Boolean), shortcutCanRetry: Schema.optional(Schema.Boolean), shortcutLabel: Schema.optional(Schema.String), shortcutMessage: Schema.NullOr(Schema.String), shortcutBinding: Schema.optional(Schema.String), shortcutConfigPath: Schema.optional(Schema.String), shortcutActionRegistered: Schema.optional(Schema.Boolean), gnomeExtension: Schema.optional(DesktopCaptureExtensionState), kdeHelper: Schema.optional(DesktopCaptureHelperState), hyprlandHelper: Schema.optional(DesktopCaptureHelperState), macPermissions: Schema.optional( Schema.Struct({ screenRecording: Schema.Boolean, accessibility: Schema.Boolean }), ), shortcutVerified: Schema.optional(Schema.Boolean), message: Schema.NullOr(Schema.String), }); export type DesktopSnapShotState = typeof DesktopSnapShotState.Type; export const DesktopSnapShotShortcutAvailability = Schema.Struct({ available: Schema.Boolean, message: Schema.NullOr(Schema.String), }); export type DesktopSnapShotShortcutAvailability = typeof DesktopSnapShotShortcutAvailability.Type; export const DesktopSnapShotId = TrimmedNonEmptyString.check( Schema.isMaxLength(64), Schema.isPattern(/^[a-f0-9-]+$/i), ); export type DesktopSnapShotId = typeof DesktopSnapShotId.Type; /** Main-process capture lifecycle pushes. `id` is absent for failures before a capture exists. */ export const DesktopSnapShotEvent = Schema.Union([ Schema.Struct({ type: Schema.Literal("requested"), id: DesktopSnapShotId }), Schema.Struct({ type: Schema.Literal("started"), id: DesktopSnapShotId }), Schema.Struct({ type: Schema.Literal("ready"), id: DesktopSnapShotId }), Schema.Struct({ type: Schema.Literal("failed"), id: Schema.optional(DesktopSnapShotId) }), Schema.Struct({ type: Schema.Literal("shortcut-changed") }), ]); export type DesktopSnapShotEvent = typeof DesktopSnapShotEvent.Type; export const DesktopPendingSnapShot = Schema.Struct({ id: DesktopSnapShotId, name: Schema.String, mimeType: Schema.Literal("image/png"), sizeBytes: Schema.Int, source: SnapShotSource, }); export type DesktopPendingSnapShot = typeof DesktopPendingSnapShot.Type; export const DesktopSnapShot = Schema.Struct({ ...DesktopPendingSnapShot.fields, dataUrl: Schema.String, }); export type DesktopSnapShot = typeof DesktopSnapShot.Type; export const DesktopSnapShotAnimationDestination = Schema.Struct({ id: DesktopSnapShotId, viewportFrame: Schema.Struct({ x: Schema.Number, y: Schema.Number, width: Schema.Number, height: Schema.Number, }), backgroundColor: Schema.String, borderColor: Schema.String, borderWidth: Schema.Number, cornerRadius: Schema.Number, details: Schema.optional( Schema.Struct({ appName: SnapShotSource.fields.appName, windowTitle: SnapShotSource.fields.windowTitle, appIconDataUrl: SnapShotSource.fields.appIconDataUrl, }), ), }); export type DesktopSnapShotAnimationDestination = typeof DesktopSnapShotAnimationDestination.Type; export interface DesktopRuntimeInfo { hostArch: DesktopRuntimeArch; appArch: DesktopRuntimeArch; runningUnderArm64Translation: boolean; } export interface DesktopUpdateState { enabled: boolean; status: DesktopUpdateStatus; channel: DesktopUpdateChannel; currentVersion: string; hostArch: DesktopRuntimeArch; appArch: DesktopRuntimeArch; runningUnderArm64Translation: boolean; availableVersion: string | null; downloadedVersion: string | null; releaseNotes: ReadonlyArray; omittedReleaseCount: number; downloadPercent: number | null; checkedAt: string | null; message: string | null; errorContext: "check" | "download" | "install" | null; canRetry: boolean; } export interface DesktopUpdateReleaseNote { version: string; items: ReadonlyArray; totalItems: number; } export const DesktopUpdateReleaseNoteSchema = Schema.Struct({ version: Schema.String, items: Schema.Array(Schema.String), totalItems: Schema.Number, }); export const DesktopUpdateStateSchema = Schema.Struct({ enabled: Schema.Boolean, status: DesktopUpdateStatusSchema, channel: DesktopUpdateChannelSchema, currentVersion: Schema.String, hostArch: DesktopRuntimeArchSchema, appArch: DesktopRuntimeArchSchema, runningUnderArm64Translation: Schema.Boolean, availableVersion: Schema.NullOr(Schema.String), downloadedVersion: Schema.NullOr(Schema.String), releaseNotes: Schema.Array(DesktopUpdateReleaseNoteSchema), omittedReleaseCount: Schema.Number, downloadPercent: Schema.NullOr(Schema.Number), checkedAt: Schema.NullOr(Schema.String), message: Schema.NullOr(Schema.String), errorContext: Schema.NullOr(Schema.Literals(["check", "download", "install"])), canRetry: Schema.Boolean, }); export interface DesktopUpdateActionResult { accepted: boolean; completed: boolean; state: DesktopUpdateState; } export const DesktopUpdateActionResultSchema = Schema.Struct({ accepted: Schema.Boolean, completed: Schema.Boolean, state: DesktopUpdateStateSchema, }); export interface DesktopUpdateCheckResult { checked: boolean; state: DesktopUpdateState; } export const DesktopUpdateCheckResultSchema = Schema.Struct({ checked: Schema.Boolean, state: DesktopUpdateStateSchema, }); // Stable id for the Windows-native primary backend. Desktop side wraps // this with a brand inside DesktopBackendManager; web side keeps it as // a plain string so the env-runtime can compare against it without // importing brand machinery from the desktop package. export const PRIMARY_LOCAL_ENVIRONMENT_ID = "primary"; export interface DesktopEnvironmentBootstrap { // Stable backend instance id (e.g. "primary" or "wsl:ubuntu"). The // web env runtime keys local environments off this so projects // routed to a specific backend reopen against the same one. id: string; label: string; // Concrete WSL distro used by the current backend run. This stays separate // from id because a default-tracking instance keeps the stable // "wsl:default" IPC target while each run launches a specific distro. runningDistro?: string | null; httpBaseUrl: string | null; wsBaseUrl: string | null; bootstrapToken?: string; } export const DesktopEnvironmentBootstrapSchema = Schema.Struct({ id: Schema.String, label: Schema.String, runningDistro: Schema.optionalKey(Schema.NullOr(Schema.String)), httpBaseUrl: Schema.NullOr(Schema.String), wsBaseUrl: Schema.NullOr(Schema.String), bootstrapToken: Schema.optionalKey(Schema.String), }); export const DesktopSshEnvironmentTargetSchema = Schema.Struct({ alias: Schema.String, hostname: Schema.String, username: Schema.NullOr(Schema.String), port: Schema.NullOr(Schema.Number), }); export type DesktopSshEnvironmentTarget = typeof DesktopSshEnvironmentTargetSchema.Type; export type DesktopSshHostSource = "ssh-config" | "known-hosts"; export const DesktopSshHostSourceSchema = Schema.Literals(["ssh-config", "known-hosts"]); export interface DesktopDiscoveredSshHost extends DesktopSshEnvironmentTarget { source: DesktopSshHostSource; } export const DesktopDiscoveredSshHostSchema = Schema.Struct({ alias: Schema.String, hostname: Schema.String, username: Schema.NullOr(Schema.String), port: Schema.NullOr(Schema.Number), source: DesktopSshHostSourceSchema, }); export interface DesktopSshEnvironmentBootstrap { target: DesktopSshEnvironmentTarget; httpBaseUrl: string; wsBaseUrl: string; pairingToken: string | null; remotePort?: number; remoteServerKind?: "external" | "managed"; } export const DesktopSshEnvironmentBootstrapSchema = Schema.Struct({ target: DesktopSshEnvironmentTargetSchema, httpBaseUrl: Schema.String, wsBaseUrl: Schema.String, pairingToken: Schema.NullOr(Schema.String), remotePort: Schema.optionalKey(Schema.Number), remoteServerKind: Schema.optionalKey(Schema.Literals(["external", "managed"])), }); export interface DesktopSshPasswordPromptRequest { requestId: string; destination: string; username: string | null; prompt: string; expiresAt: string; } export const DesktopSshPasswordPromptCancelledType = "ssh-password-prompt-cancelled" as const; export const DesktopSshPasswordPromptCancelledResultSchema = Schema.Struct({ type: Schema.Literal(DesktopSshPasswordPromptCancelledType), message: Schema.String, }); export const DesktopSshEnvironmentEnsureOptionsSchema = Schema.Struct({ issuePairingToken: Schema.optionalKey(Schema.Boolean), }); export const DesktopSshEnvironmentEnsureInputSchema = Schema.Struct({ target: DesktopSshEnvironmentTargetSchema, options: Schema.optionalKey(DesktopSshEnvironmentEnsureOptionsSchema), }); export const DesktopSshEnvironmentEnsureResultSchema = Schema.Union([ DesktopSshEnvironmentBootstrapSchema, DesktopSshPasswordPromptCancelledResultSchema, ]); export const DesktopSshHttpBaseUrlInputSchema = Schema.Struct({ httpBaseUrl: Schema.String, }); export const DesktopSshBearerRequestInputSchema = Schema.Struct({ httpBaseUrl: Schema.String, bearerToken: Schema.String, }); export const DesktopSshBearerBootstrapInputSchema = Schema.Struct({ httpBaseUrl: Schema.String, credential: Schema.String, }); export const DesktopSshPasswordPromptResolutionInputSchema = Schema.Struct({ requestId: Schema.String, password: Schema.NullOr(Schema.String), }); export const PersistedSavedEnvironmentRecordSchema = Schema.Struct({ environmentId: EnvironmentId, label: Schema.String, wsBaseUrl: Schema.String, httpBaseUrl: Schema.String, createdAt: Schema.String, lastConnectedAt: Schema.NullOr(Schema.String), desktopSsh: Schema.optionalKey(DesktopSshEnvironmentTargetSchema), relayManaged: Schema.optionalKey( Schema.Struct({ relayUrl: Schema.String, }), ), }); export type PersistedSavedEnvironmentRecord = typeof PersistedSavedEnvironmentRecordSchema.Type; export type DesktopServerExposureMode = "local-only" | "network-accessible"; export const DesktopServerExposureModeSchema = Schema.Literals([ "local-only", "network-accessible", ]); export interface DesktopServerExposureState { mode: DesktopServerExposureMode; endpointUrl: string | null; advertisedHost: string | null; tailscaleServeEnabled: boolean; tailscaleServePort: number; } export const DesktopServerExposureStateSchema = Schema.Struct({ mode: DesktopServerExposureModeSchema, endpointUrl: Schema.NullOr(Schema.String), advertisedHost: Schema.NullOr(Schema.String), tailscaleServeEnabled: Schema.Boolean, tailscaleServePort: Schema.Number, }); export interface PickFolderOptions { initialPath?: string | null; // When set, the desktop dialog opens against the named backend's // filesystem instead of the primary's. Used by callers that already // know which local environment they're targeting (e.g. opening a // project that lives inside WSL). Omitting it keeps the historical // behavior so non-WSL users never see a different picker. targetEnvironmentId?: string; } export const PickFolderOptionsSchema = Schema.Struct({ initialPath: Schema.optionalKey(Schema.NullOr(Schema.String)), targetEnvironmentId: Schema.optionalKey(Schema.String), }); /** * A file returned by the desktop theme-file picker. Oversized files carry an * empty text so the renderer can reject them by size without the main * process ever holding their contents. */ export interface PickedThemeFile { name: string; size: number; text: string; } export const PickedThemeFileSchema = Schema.Struct({ name: Schema.String, size: Schema.Number, text: Schema.String, }); export interface DesktopWslDistro { name: string; isDefault: boolean; version: 1 | 2; } export const DesktopWslDistroSchema = Schema.Struct({ name: Schema.String, isDefault: Schema.Boolean, version: Schema.Literals([1, 2]), }); export interface DesktopWslState { // True when the user has opted the WSL backend in; the actual backend // process is registered with the desktop pool independently of this // flag and may take a moment to come up after the user enables it. enabled: boolean; // null means "track the current WSL default distro". distro: string | null; available: boolean; // When true (and `enabled` is also true) the desktop runs only the // WSL backend as the primary; the Windows-side Node backend is not // started. Toggling this requires an app restart because the // primary backend's spec is captured once at layer init. wslOnly: boolean; distros: readonly DesktopWslDistro[]; // Reason the dual-mode WSL backend last failed preflight (no node, wrong // version, missing build tools), or null. Surfaced inline in Connections // settings. Always null in wsl-only mode — that path shows a dialog and // falls back to Windows instead. preflightError: string | null; } export const DesktopWslStateSchema = Schema.Struct({ enabled: Schema.Boolean, distro: Schema.NullOr(Schema.String), available: Schema.Boolean, wslOnly: Schema.Boolean, distros: Schema.Array(DesktopWslDistroSchema), preflightError: Schema.NullOr(Schema.String), }); /** * Renderer-facing snapshot of a desktop preview tab. Mirrors the main-process * PreviewTabState shape but uses serialisable primitives only. */ export type DesktopPreviewNavStatus = | { kind: "Idle" } | { kind: "Loading"; url: string; title: string } | { kind: "Success"; url: string; title: string } | { kind: "LoadFailed"; url: string; title: string; code: number; description: string; }; /** * Emulated `prefers-color-scheme` for the guest page. "system" clears the * override so the page follows the OS appearance. */ export type DesktopPreviewColorScheme = "system" | "light" | "dark"; export const DesktopPreviewColorSchemeSchema: Schema.Codec = Schema.Literals(["system", "light", "dark"]); export const FAVICON_DATA_URL_MAX_LENGTH = 8192; export const FAVICON_CAPTURED_AT_MAX = 8_640_000_000_000_000; export interface DesktopPreviewFavicon { dataUrl: string; pageUrl: string; capturedAt: number; } export const DesktopPreviewFaviconSchema: Schema.Codec = Schema.Struct({ dataUrl: Schema.String.check( Schema.isMaxLength(FAVICON_DATA_URL_MAX_LENGTH), Schema.isPattern(/^data:image\/png;base64,[a-z0-9+/]+={0,2}$/i), ), pageUrl: Schema.String.check(Schema.isMaxLength(2_048)), capturedAt: Schema.Number.check( Schema.isFinite(), Schema.isGreaterThanOrEqualTo(0), Schema.isLessThanOrEqualTo(FAVICON_CAPTURED_AT_MAX), ), }); export interface DesktopPreviewTabState { tabId: string; webContentsId: number | null; navStatus: DesktopPreviewNavStatus; canGoBack: boolean; canGoForward: boolean; /** Current zoom factor (1.0 = 100%). */ zoomFactor: number; /** Whether this tab is currently mirrored into a desktop picture-in-picture window. */ pictureInPicture: boolean; colorScheme: DesktopPreviewColorScheme; /** * Whether the user has silenced this tab. Per tab rather than per origin, so * two tabs on the same site mute independently. Survives navigation and * webview swaps, but is dropped when the tab closes. */ audioMuted: boolean; /** * Whether the guest is currently emitting audio. Observed from Chromium, and * independent of {@link audioMuted}: a muted tab that is playing still reports * `true`, which is what lets the tab strip distinguish "muted and making * sound" from "muted and silent". */ audible: boolean; controller: "human" | "agent" | "none"; favicon?: DesktopPreviewFavicon; updatedAt: string; } export const DesktopPreviewTabIdSchema = Schema.String.check(Schema.isTrimmed()).check( Schema.isNonEmpty(), ); export const DesktopPreviewAutomationStatusSchema = Schema.Struct({ ...PreviewAutomationStatus.fields, tabId: Schema.NullOr(DesktopPreviewTabIdSchema), }); export type DesktopPreviewAutomationStatus = typeof DesktopPreviewAutomationStatusSchema.Type; export const DesktopPreviewNavStatusSchema = Schema.Union([ Schema.Struct({ kind: Schema.Literal("Idle") }), Schema.Struct({ kind: Schema.Literal("Loading"), url: Schema.String, title: Schema.String, }), Schema.Struct({ kind: Schema.Literal("Success"), url: Schema.String, title: Schema.String, }), Schema.Struct({ kind: Schema.Literal("LoadFailed"), url: Schema.String, title: Schema.String, code: Schema.Number, description: Schema.String, }), ]); export interface DesktopPreviewPointerEvent { tabId: string; phase: "move" | "click"; x: number; y: number; sequence: number; createdAt: string; } /** * Static config a renderer needs to mount a preview ``. Returned * atomically by `DesktopPreviewBridge.getPreviewConfig()` so the renderer * doesn't have to wait on three separate IPC round-trips before the webview * can attach. */ export interface DesktopPreviewWebviewConfig { /** `persist:t3code-preview` (or whatever the desktop chose). */ partition: string; /** * Canonical `` string. Encodes the security * posture (sandboxed but contextIsolation off so the picker preload can * read the page's React DevTools hook). Always present. */ webPreferences: string; /** * Absolute `file://`-style URL to the picker preload bundle. Set to null * when the bundle isn't present (older builds, broken install) — the * renderer must then disable element-pick affordances. */ preloadUrl: string | null; } export const DesktopPreviewWebviewConfigSchema: Schema.Codec = Schema.Struct({ partition: Schema.String, webPreferences: Schema.String, preloadUrl: Schema.NullOr(Schema.String), }); export interface DesktopPreviewAnnotationTheme { colorScheme: "light" | "dark"; radius: string; background: string; foreground: string; popover: string; popoverForeground: string; primary: string; primaryForeground: string; muted: string; mutedForeground: string; accent: string; accentForeground: string; border: string; input: string; ring: string; fontSans: string; fontMono: string; } export const DesktopPreviewAnnotationThemeSchema: Schema.Codec = Schema.Struct({ colorScheme: Schema.Literals(["light", "dark"]), radius: Schema.String, background: Schema.String, foreground: Schema.String, popover: Schema.String, popoverForeground: Schema.String, primary: Schema.String, primaryForeground: Schema.String, muted: Schema.String, mutedForeground: Schema.String, accent: Schema.String, accentForeground: Schema.String, border: Schema.String, input: Schema.String, ring: Schema.String, fontSans: Schema.String, fontMono: Schema.String, }); export interface DesktopPreviewRecordingFrame { tabId: string; data: string; width: number; height: number; receivedAt: string; } export interface DesktopPreviewRecordingArtifact { id: string; tabId: string; path: string; mimeType: string; sizeBytes: number; createdAt: string; } export const DesktopPreviewRecordingArtifactSchema: Schema.Codec = Schema.Struct({ id: Schema.String, tabId: DesktopPreviewTabIdSchema, path: Schema.String, mimeType: Schema.String, sizeBytes: Schema.Int, createdAt: Schema.String, }); export interface DesktopPreviewScreenshotArtifact { id: string; tabId: string; path: string; mimeType: "image/png"; sizeBytes: number; createdAt: string; } export const DesktopPreviewScreenshotArtifactSchema: Schema.Codec = Schema.Struct({ id: Schema.String, tabId: DesktopPreviewTabIdSchema, path: Schema.String, mimeType: Schema.Literal("image/png"), sizeBytes: Schema.Int, createdAt: Schema.String, }); /** * Single stack frame captured by react-grab's `getElementContext`. We surface * the source file/line so coding agents can jump straight to the JSX that * produced the picked DOM node. */ export interface PickedElementStackFrame { functionName: string | null; fileName: string | null; lineNumber: number | null; columnNumber: number | null; } export const PickedElementStackFrameSchema: Schema.Codec = Schema.Struct({ functionName: Schema.NullOr(Schema.String), fileName: Schema.NullOr(Schema.String), lineNumber: Schema.NullOr(Schema.Number), columnNumber: Schema.NullOr(Schema.Number), }); /** * A successful element pick from the preview webview. All fields are * best-effort — pages that don't ship a React fiber tree (or aren't running * in dev) will still produce a usable payload (selector + html preview), * just without component / source attribution. */ export interface PickedElementPayload { /** URL of the page the element was picked on. */ pageUrl: string; /** Optional `` of that page (best-effort). */ pageTitle: string | null; /** Lowercase tag name, e.g. `"button"`. */ tagName: string; /** CSS selector resolving back to the element on a re-render. */ selector: string | null; /** Truncated outer-HTML preview (matches react-grab's `htmlPreview`). */ htmlPreview: string; /** Nearest React component display name, or null when unavailable. */ componentName: string | null; /** First source-mapped stack frame (file + line of the JSX source). */ source: PickedElementStackFrame | null; /** Full owner-stack frames; can be empty. Useful for richer context. */ stack: ReadonlyArray<PickedElementStackFrame>; /** Author CSS only (UA defaults stripped) — react-grab's `styles`. */ styles: string; /** Wall-clock pick time as ISO-8601 string. */ pickedAt: string; } export const PickedElementPayloadSchema: Schema.Codec<PickedElementPayload> = Schema.Struct({ pageUrl: Schema.String, pageTitle: Schema.NullOr(Schema.String), tagName: Schema.String, selector: Schema.NullOr(Schema.String), htmlPreview: Schema.String, componentName: Schema.NullOr(Schema.String), source: Schema.NullOr(PickedElementStackFrameSchema), stack: Schema.Array(PickedElementStackFrameSchema), styles: Schema.String, pickedAt: Schema.String, }); export interface PreviewAnnotationRect { x: number; y: number; width: number; height: number; } export const PreviewAnnotationRectSchema: Schema.Codec<PreviewAnnotationRect> = Schema.Struct({ x: Schema.Number, y: Schema.Number, width: Schema.Number, height: Schema.Number, }); export interface PreviewAnnotationPoint { x: number; y: number; } export const PreviewAnnotationPointSchema: Schema.Codec<PreviewAnnotationPoint> = Schema.Struct({ x: Schema.Number, y: Schema.Number, }); export interface PreviewAnnotationElementTarget { id: string; element: PickedElementPayload; rect: PreviewAnnotationRect; } export const PreviewAnnotationElementTargetSchema: Schema.Codec<PreviewAnnotationElementTarget> = Schema.Struct({ id: Schema.String, element: PickedElementPayloadSchema, rect: PreviewAnnotationRectSchema, }); export interface PreviewAnnotationRegionTarget { id: string; rect: PreviewAnnotationRect; } export const PreviewAnnotationRegionTargetSchema: Schema.Codec<PreviewAnnotationRegionTarget> = Schema.Struct({ id: Schema.String, rect: PreviewAnnotationRectSchema, }); export interface PreviewAnnotationStrokeTarget { id: string; color: string; width: number; points: ReadonlyArray<PreviewAnnotationPoint>; bounds: PreviewAnnotationRect; } export const PreviewAnnotationStrokeTargetSchema: Schema.Codec<PreviewAnnotationStrokeTarget> = Schema.Struct({ id: Schema.String, color: Schema.String, width: Schema.Number, points: Schema.Array(PreviewAnnotationPointSchema), bounds: PreviewAnnotationRectSchema, }); export interface PreviewAnnotationStyleChange { targetId: string; selector: string | null; property: string; previousValue: string; value: string; } export const PreviewAnnotationStyleChangeSchema: Schema.Codec<PreviewAnnotationStyleChange> = Schema.Struct({ targetId: Schema.String, selector: Schema.NullOr(Schema.String), property: Schema.String, previousValue: Schema.String, value: Schema.String, }); export interface PreviewAnnotationScreenshot { dataUrl: string; width: number; height: number; cropRect: PreviewAnnotationRect; } export const PreviewAnnotationScreenshotSchema: Schema.Codec<PreviewAnnotationScreenshot> = Schema.Struct({ dataUrl: Schema.String, width: Schema.Number, height: Schema.Number, cropRect: PreviewAnnotationRectSchema, }); /** * A submitted preview annotation. One annotation may reference multiple DOM * elements, freeform regions, and ink strokes. The desktop main process adds * the screenshot after the guest preload submits the structured draft. */ export interface PreviewAnnotationPayload { id: string; pageUrl: string; pageTitle: string | null; comment: string; elements: ReadonlyArray<PreviewAnnotationElementTarget>; regions: ReadonlyArray<PreviewAnnotationRegionTarget>; strokes: ReadonlyArray<PreviewAnnotationStrokeTarget>; styleChanges: ReadonlyArray<PreviewAnnotationStyleChange>; screenshot: PreviewAnnotationScreenshot | null; createdAt: string; } export const PreviewAnnotationPayloadSchema: Schema.Codec<PreviewAnnotationPayload> = Schema.Struct( { id: Schema.String, pageUrl: Schema.String, pageTitle: Schema.NullOr(Schema.String), comment: Schema.String, elements: Schema.Array(PreviewAnnotationElementTargetSchema), regions: Schema.Array(PreviewAnnotationRegionTargetSchema), strokes: Schema.Array(PreviewAnnotationStrokeTargetSchema), styleChanges: Schema.Array(PreviewAnnotationStyleChangeSchema), screenshot: Schema.NullOr(PreviewAnnotationScreenshotSchema), createdAt: Schema.String, }, ); export type PreviewAnnotationSubmission = "attach" | "send"; export const PreviewAnnotationSubmissionSchema: Schema.Codec<PreviewAnnotationSubmission> = Schema.Literals(["attach", "send"]); export interface PreviewAnnotationSubmissionResult { annotation: PreviewAnnotationPayload; submission: PreviewAnnotationSubmission; /** The crop was requested but failed or timed out, so `annotation.screenshot` is null. */ screenshotFailed?: boolean; } export const PreviewAnnotationSubmissionResultSchema: Schema.Codec<PreviewAnnotationSubmissionResult> = Schema.Struct({ annotation: PreviewAnnotationPayloadSchema, submission: PreviewAnnotationSubmissionSchema, screenshotFailed: Schema.optionalKey(Schema.Boolean), }); export const DesktopPreviewTabInputSchema = Schema.Struct({ tabId: DesktopPreviewTabIdSchema, }); /** * Tab creation carries the client's configured browser defaults so the guest * is born already zoomed and color-scheme-emulated. Applying them after * creation instead would paint one frame at 100%/system first, which reads as * a flash on every tab open. Both fields are optional so an older renderer * still gets the historical defaults. */ export const DesktopPreviewCreateTabInputSchema = Schema.Struct({ tabId: DesktopPreviewTabIdSchema, zoomFactor: Schema.optional(Schema.Number.check(Schema.isGreaterThan(0))), colorScheme: Schema.optional(DesktopPreviewColorSchemeSchema), }); export interface DesktopPreviewTabDefaults { readonly zoomFactor?: number | undefined; readonly colorScheme?: DesktopPreviewColorScheme | undefined; } export const DesktopPreviewRegisterWebviewInputSchema = Schema.Struct({ tabId: DesktopPreviewTabIdSchema, webContentsId: Schema.Int.check(Schema.isGreaterThan(0)), }); export const DesktopPreviewNavigateInputSchema = Schema.Struct({ tabId: DesktopPreviewTabIdSchema, url: Schema.String, }); export const DesktopPreviewConfigInputSchema = Schema.Struct({ environmentId: EnvironmentId, /** * Browser profile the partition is derived from. Derivation stays in main: * `will-attach-webview` only prefix-checks the partition string, so a * renderer-supplied partition could attach to a session that never had the * UA rewrite or permission handlers installed. */ profileId: Schema.optional(BrowserProfileId), }); export const DesktopPreviewClearDataInputSchema = Schema.Struct({ environmentId: EnvironmentId, /** Omit to clear every profile; otherwise only this profile's partition. */ profileId: Schema.optional(BrowserProfileId), }); export const DesktopPreviewSetColorSchemeInputSchema = Schema.Struct({ tabId: DesktopPreviewTabIdSchema, colorScheme: DesktopPreviewColorSchemeSchema, }); export const DesktopPreviewSetAudioMutedInputSchema = Schema.Struct({ tabId: DesktopPreviewTabIdSchema, audioMuted: Schema.Boolean, }); export const DesktopPreviewAnnotationThemeInputSchema = Schema.Struct({ theme: DesktopPreviewAnnotationThemeSchema, }); export const DesktopPreviewArtifactInputSchema = Schema.Struct({ path: Schema.String.check(Schema.isTrimmed()).check(Schema.isNonEmpty()), }); export const DesktopPreviewRecordingSaveInputSchema = Schema.Struct({ tabId: DesktopPreviewTabIdSchema, mimeType: Schema.String.check(Schema.isTrimmed()).check(Schema.isNonEmpty()), data: Schema.Uint8Array, }); export const DesktopPreviewAutomationClickInputSchema = Schema.Struct({ tabId: DesktopPreviewTabIdSchema, input: PreviewAutomationClickInput, }); export const DesktopPreviewAutomationTypeInputSchema = Schema.Struct({ tabId: DesktopPreviewTabIdSchema, input: PreviewAutomationTypeInput, }); export const DesktopPreviewAutomationPressInputSchema = Schema.Struct({ tabId: DesktopPreviewTabIdSchema, input: PreviewAutomationPressInput, }); export const DesktopPreviewAutomationScrollInputSchema = Schema.Struct({ tabId: DesktopPreviewTabIdSchema, input: PreviewAutomationScrollInput, }); export const DesktopPreviewAutomationEvaluateInputSchema = Schema.Struct({ tabId: DesktopPreviewTabIdSchema, input: PreviewAutomationEvaluateInput, }); export const DesktopPreviewAutomationWaitForInputSchema = Schema.Struct({ tabId: DesktopPreviewTabIdSchema, input: PreviewAutomationWaitForInput, }); /** * A System Settings pane the app can deep-link to. The identifier crosses IPC * rather than a URL, so the renderer can only reach these known destinations. */ export const SystemSettingsPaneSchema = Schema.Literals(["full-disk-access"]); export type SystemSettingsPane = typeof SystemSettingsPaneSchema.Type; export interface DesktopBridge { getAppBranding: () => DesktopAppBranding | null; /** The desktop client's OS platform, read from Electron's preload process. */ getClientPlatform?: () => string; /** * The OS locale as a BCP-47 tag, which the renderer cannot read for itself: * the packaged app ships only the `en-US` Chromium locale pak, so * `navigator.language` and the default `Intl` locale are pinned to `en-US` * regardless of OS settings. */ getSystemLocale?: () => string | null; // One bootstrap per pool instance currently registered with bootstrap // info (omits instances whose backend hasn't produced a config yet). // The primary backend is identified by id === PRIMARY_LOCAL_ENVIRONMENT_ID. getLocalEnvironmentBootstraps: () => readonly DesktopEnvironmentBootstrap[]; getLocalEnvironmentBearerToken: () => Promise<string>; getClientSettings: () => Promise<ClientSettings | null>; setClientSettings: (settings: ClientSettings) => Promise<void>; getConnectionCatalog?: () => Promise<string | null>; setConnectionCatalog?: (catalog: string) => Promise<boolean>; clearConnectionCatalog?: () => Promise<void>; discoverSshHosts: () => Promise<readonly DesktopDiscoveredSshHost[]>; /** Resolves a suggested SSH alias before populating the connection form. */ resolveSshHost: (alias: string) => Promise<DesktopSshEnvironmentTarget>; requestSnapShotPermissions?: (includeAccessibility: boolean) => Promise<void>; getSnapShotState?: () => Promise<DesktopSnapShotState>; setupSnapShot?: (action: DesktopSnapShotSetupAction) => Promise<void>; previewSnapShotConfig?: ( request: DesktopCaptureConfigRequest, ) => Promise<DesktopCaptureConfigPreview | null>; applySnapShotConfig?: (previewId: string) => Promise<DesktopCaptureConfigApplied>; checkSnapShotShortcut?: ( shortcut: SnapShotShortcut, ) => Promise<DesktopSnapShotShortcutAvailability>; setSnapShotShortcutSuppressed?: (suppressed: boolean) => Promise<void>; listPendingSnapShots?: () => Promise<readonly DesktopPendingSnapShot[]>; readSnapShot?: (id: string) => Promise<DesktopSnapShot>; setSnapShotAnimationDestination?: ( destination: DesktopSnapShotAnimationDestination, ) => Promise<void>; dismissSnapShotAnimation?: (id: DesktopSnapShotId) => Promise<void>; acknowledgeSnapShot?: (id: string) => Promise<void>; ensureSshEnvironment: ( target: DesktopSshEnvironmentTarget, options?: { issuePairingToken?: boolean }, ) => Promise<DesktopSshEnvironmentBootstrap>; disconnectSshEnvironment: (target: DesktopSshEnvironmentTarget) => Promise<void>; fetchSshEnvironmentDescriptor: (httpBaseUrl: string) => Promise<ExecutionEnvironmentDescriptor>; bootstrapSshBearerSession: ( httpBaseUrl: string, credential: string, ) => Promise<AuthAccessTokenResult>; fetchSshSessionState: (httpBaseUrl: string, bearerToken: string) => Promise<AuthSessionState>; issueSshWebSocketTicket: ( httpBaseUrl: string, bearerToken: string, ) => Promise<AuthWebSocketTicketResult>; onSshPasswordPrompt: (listener: (request: DesktopSshPasswordPromptRequest) => void) => () => void; resolveSshPasswordPrompt: (requestId: string, password: string | null) => Promise<void>; getServerExposureState: () => Promise<DesktopServerExposureState>; setServerExposureMode: (mode: DesktopServerExposureMode) => Promise<DesktopServerExposureState>; setTailscaleServeEnabled: (input: { readonly enabled: boolean; readonly port?: number; }) => Promise<DesktopServerExposureState>; getAdvertisedEndpoints: () => Promise<readonly AdvertisedEndpoint[]>; getWslState: () => Promise<DesktopWslState>; setWslBackendEnabled: (enabled: boolean) => Promise<DesktopWslState>; setWslDistro: (distro: string | null) => Promise<DesktopWslState>; setWslOnly: (enabled: boolean) => Promise<DesktopWslState>; pickFolder: (options?: PickFolderOptions) => Promise<string | null>; /** Optional while older desktop shells can host a newer web client. */ pickProjectFavicon?: (initialPath?: string) => Promise<string | null>; /** * Multi-select JSON file picker that opens in the VS Code extensions * directory when one exists. Optional: older desktop builds lack it, and * web callers fall back to a plain file input. */ pickThemeFiles?: () => Promise<readonly PickedThemeFile[] | null>; setTheme: (theme: DesktopTheme) => Promise<void>; showContextMenu: <T extends string>( items: readonly ContextMenuItem<T>[], position?: { x: number; y: number }, ) => Promise<T | null>; openExternal: (url: string) => Promise<boolean>; /** * Open a System Settings pane by identifier. Optional: older desktop builds * lack it, and callers no-op when it is missing. */ openSystemSettings?: (pane: SystemSettingsPane) => Promise<boolean>; /** * Probe this desktop machine for installed remote-capable editor CLIs * (used for remote open-in-editor deep links). Optional: older desktop * builds lack it; callers fall back to VS Code only. */ probeRemoteEditors?: () => Promise<readonly EditorId[]>; onMenuAction: (listener: (action: string) => void) => () => void; onSnapShotEvent?: (listener: (event: DesktopSnapShotEvent) => void) => () => void; /** * Quit-confirmation hint pushes. Optional: older desktop builds never emit * them. */ onQuitShortcut?: (listener: (event: QuitShortcutHintEvent) => void) => () => void; getWindowFullscreenState: () => boolean; onWindowFullscreenStateChange: (listener: (fullscreen: boolean) => void) => () => void; getUpdateState: () => Promise<DesktopUpdateState>; setUpdateChannel: (channel: DesktopUpdateChannel) => Promise<DesktopUpdateState>; checkForUpdate: () => Promise<DesktopUpdateCheckResult>; downloadUpdate: () => Promise<DesktopUpdateActionResult>; installUpdate: () => Promise<DesktopUpdateActionResult>; onUpdateState: (listener: (state: DesktopUpdateState) => void) => () => void; /** Present when the desktop shell accepts `t3 app` activation requests. */ appActivation?: { setReady: (ready: boolean) => Promise<void>; complete: (response: DesktopAppActivationResponse) => Promise<void>; onRequest: (listener: (request: DesktopAppActivationRequest) => void) => () => void; }; /** * Desktop-only preview surface. Present iff the renderer is hosted by the * Electron desktop build; web builds have `preview === undefined`. */ preview?: DesktopPreviewBridge; } /** Renderer callback invoked by Electron with a fresh user gesture before display-media capture. */ export const DESKTOP_PREVIEW_RECORDING_CAPTURE_TRIGGER = "__t3DesktopPreviewRecordingCapture"; export interface DesktopPreviewBridge { createTab: (tabId: string, defaults?: DesktopPreviewTabDefaults) => Promise<void>; closeTab: (tabId: string) => Promise<void>; registerWebview: (tabId: string, webContentsId: number) => Promise<void>; navigate: (tabId: string, url: string) => Promise<void>; goBack: (tabId: string) => Promise<void>; goForward: (tabId: string) => Promise<void>; refresh: (tabId: string) => Promise<void>; zoomIn: (tabId: string) => Promise<void>; zoomOut: (tabId: string) => Promise<void>; resetZoom: (tabId: string) => Promise<void>; /** Reload bypassing the HTTP cache. */ hardReload: (tabId: string) => Promise<void>; /** * Emulate `prefers-color-scheme` on the guest page ("system" clears the * override). Persists per tab and is re-applied across webview swaps. */ setColorScheme: (tabId: string, colorScheme: DesktopPreviewColorScheme) => Promise<void>; /** * Silence the tab's audio output. Persists per tab and is re-applied across * webview swaps, but is dropped when the tab closes. Muting a silent tab is * allowed; it simply takes effect once the page plays something. */ setAudioMuted: (tabId: string, audioMuted: boolean) => Promise<void>; /** Open the guest webview's DevTools (detached). */ openDevTools: (tabId: string) => Promise<void>; /** Drop cookies + storage data for the preview partition (all tabs). */ clearCookies: (environmentId: EnvironmentId, profileId?: string) => Promise<void>; /** Drop the HTTP cache for the preview partition (all tabs). */ clearCache: (environmentId: EnvironmentId, profileId?: string) => Promise<void>; /** * One-shot config for mounting a preview `<webview>`. Replaces three * earlier round-trip calls (`getBrowserPartition`, `getWebviewPreferences`, * `getPickPreloadPath`) so adding a new field here only requires touching * the contract + main, not the renderer's mount logic. */ getPreviewConfig: ( environmentId: EnvironmentId, profileId?: string, ) => Promise<DesktopPreviewWebviewConfig>; /** Browsers on this machine whose cookies can be imported. */ listBrowserImportSources: () => Promise<ReadonlyArray<BrowserImportSource>>; importBrowserCookies: (input: { readonly environmentId: EnvironmentId; readonly sourceId: BrowserImportSourceId; readonly sourceProfileDirectory: string; readonly targetProfileId: string; }) => Promise<BrowserImportResult>; setAnnotationTheme: (theme: DesktopPreviewAnnotationTheme) => Promise<void>; /** * Activate the in-page element picker for the given tab. Resolves with * the picked annotation and its attach/send intent, or `null` when the * user cancels (Escape / nav). The promise rejects if the picker can't be * activated (no webview, etc.). */ pickElement: (tabId: string) => Promise<PreviewAnnotationSubmissionResult | null>; /** Cancel an in-flight preview annotation session. */ cancelPickElement: (tabId: string) => Promise<void>; captureScreenshot: (tabId: string) => Promise<DesktopPreviewScreenshotArtifact>; revealArtifact: (path: string) => Promise<void>; copyArtifactToClipboard: (path: string) => Promise<void>; pictureInPicture: { open: (tabId: string) => Promise<void>; close: (tabId: string) => Promise<void>; }; recording: { startScreencast: (tabId: string) => Promise<void>; stopScreencast: (tabId: string) => Promise<void>; save: ( tabId: string, mimeType: string, data: Uint8Array, ) => Promise<DesktopPreviewRecordingArtifact>; onFrame: (listener: (frame: DesktopPreviewRecordingFrame) => void) => () => void; }; automation: { status: (tabId: string) => Promise<DesktopPreviewAutomationStatus>; snapshot: (tabId: string) => Promise<PreviewAutomationSnapshot>; click: (tabId: string, input: PreviewAutomationClickInput) => Promise<void>; type: (tabId: string, input: PreviewAutomationTypeInput) => Promise<void>; press: (tabId: string, input: PreviewAutomationPressInput) => Promise<void>; scroll: (tabId: string, input: PreviewAutomationScrollInput) => Promise<void>; evaluate: (tabId: string, input: PreviewAutomationEvaluateInput) => Promise<unknown>; waitFor: (tabId: string, input: PreviewAutomationWaitForInput) => Promise<void>; }; onStateChange: (listener: (tabId: string, state: DesktopPreviewTabState) => void) => () => void; onPointerEvent: (listener: (event: DesktopPreviewPointerEvent) => void) => () => void; } export type ConfirmDialogVariant = "default" | "destructive"; export interface ConfirmDialogOptions { readonly variant?: ConfirmDialogVariant; } /** * APIs bound to the local app shell, not to any particular backend environment. * * These capabilities describe the desktop/browser host that the user is * currently running: dialogs, external-link opening, context menus, and * app-level settings/config access. They must not be used as a proxy for * "whatever environment the user is targeting", because in a multi-environment * world the local shell and a selected backend environment are distinct * concepts. */ export interface LocalApi { dialogs: { pickFolder: (options?: PickFolderOptions) => Promise<string | null>; confirm: (message: string, options?: ConfirmDialogOptions) => Promise<boolean>; }; shell: { openExternal: (url: string) => Promise<void>; /** Opens a known System Settings pane; no-ops outside the desktop app. */ openSystemSettings: (pane: SystemSettingsPane) => Promise<void>; }; contextMenu: { show: <T extends string>( items: readonly ContextMenuItem<T>[], position?: { x: number; y: number }, ) => Promise<T | null>; close: () => Promise<void>; }; persistence: { getClientSettings: () => Promise<ClientSettings | null>; setClientSettings: (settings: ClientSettings) => Promise<void>; }; } /** * APIs bound to a specific backend environment connection. * * These operations must always be routed with explicit environment context. * They represent remote stateful capabilities such as orchestration, terminal, * project, VCS, and provider operations. In multi-environment mode, each environment gets * its own instance of this surface, and callers should resolve it by * `environmentId` rather than reaching through the local desktop bridge. */ export interface EnvironmentApi { terminal: { open: (input: typeof TerminalOpenInput.Encoded) => Promise<TerminalSessionSnapshot>; attach: ( input: typeof TerminalAttachInput.Encoded, callback: (event: TerminalAttachStreamEvent) => void, options?: { onResubscribe?: () => void; }, ) => () => void; write: (input: typeof TerminalWriteInput.Encoded) => Promise<void>; resize: (input: typeof TerminalResizeInput.Encoded) => Promise<void>; clear: (input: typeof TerminalClearInput.Encoded) => Promise<void>; restart: (input: typeof TerminalRestartInput.Encoded) => Promise<TerminalSessionSnapshot>; close: (input: typeof TerminalCloseInput.Encoded) => Promise<void>; onMetadata: ( callback: (event: TerminalMetadataStreamEvent) => void, options?: { onResubscribe?: () => void; }, ) => () => void; }; projects: { listEntries: (input: ProjectListEntriesInput) => Promise<ProjectListEntriesResult>; readFile: (input: ProjectReadFileInput) => Promise<ProjectReadFileResult>; searchEntries: (input: ProjectSearchEntriesInput) => Promise<ProjectSearchEntriesResult>; writeFile: (input: ProjectWriteFileInput) => Promise<ProjectWriteFileResult>; }; filesystem: { browse: (input: FilesystemBrowseInput) => Promise<FilesystemBrowseResult>; }; assets: { createUrl: (input: AssetCreateUrlInput) => Promise<AssetCreateUrlResult>; }; sourceControl: { lookupRepository: ( input: SourceControlRepositoryLookupInput, ) => Promise<SourceControlRepositoryInfo>; cloneRepository: ( input: SourceControlCloneRepositoryInput, ) => Promise<SourceControlCloneRepositoryResult>; publishRepository: ( input: SourceControlPublishRepositoryInput, ) => Promise<SourceControlPublishRepositoryResult>; }; vcs: { listRefs: (input: VcsListRefsInput) => Promise<VcsListRefsResult>; createWorktree: (input: VcsCreateWorktreeInput) => Promise<VcsCreateWorktreeResult>; removeWorktree: (input: VcsRemoveWorktreeInput) => Promise<void>; createRef: (input: VcsCreateRefInput) => Promise<VcsCreateRefResult>; switchRef: (input: VcsSwitchRefInput) => Promise<VcsSwitchRefResult>; init: (input: VcsInitInput) => Promise<void>; pull: (input: VcsPullInput) => Promise<VcsPullResult>; refreshStatus: (input: VcsStatusInput) => Promise<VcsStatusResult>; onStatus: ( input: VcsStatusInput, callback: (status: VcsStatusResult) => void, options?: { onResubscribe?: () => void; }, ) => () => void; }; git: { resolvePullRequest: (input: GitPullRequestRefInput) => Promise<GitResolvePullRequestResult>; preparePullRequestThread: ( input: GitPreparePullRequestThreadInput, ) => Promise<GitPreparePullRequestThreadResult>; }; review: { getDiffPreview: (input: ReviewDiffPreviewInput) => Promise<ReviewDiffPreviewResult>; getDiffFileContents: ( input: ReviewDiffFileContentsInput, ) => Promise<ReviewDiffFileContentsResult>; }; orchestration: { dispatchCommand: (command: ClientOrchestrationCommand) => Promise<{ sequence: number }>; getTurnDiff: (input: OrchestrationGetTurnDiffInput) => Promise<OrchestrationGetTurnDiffResult>; getFullThreadDiff: ( input: OrchestrationGetFullThreadDiffInput, ) => Promise<OrchestrationGetFullThreadDiffResult>; getArchivedShellSnapshot: () => Promise<OrchestrationShellSnapshot>; subscribeShell: ( callback: (event: OrchestrationShellStreamItem) => void, options?: { onResubscribe?: () => void; }, ) => () => void; subscribeThread: ( input: OrchestrationSubscribeThreadInput, callback: (event: OrchestrationThreadStreamItem) => void, options?: { onResubscribe?: () => void; }, ) => () => void; }; preview: { open: (input: typeof PreviewOpenInput.Encoded) => Promise<PreviewSessionSnapshot>; navigate: (input: typeof PreviewNavigateInput.Encoded) => Promise<PreviewSessionSnapshot>; resize: (input: typeof PreviewResizeInput.Encoded) => Promise<PreviewSessionSnapshot>; refresh: (input: typeof PreviewRefreshInput.Encoded) => Promise<void>; close: (input: typeof PreviewCloseInput.Encoded) => Promise<void>; list: (input: typeof PreviewListInput.Encoded) => Promise<PreviewListResult>; reportStatus: (input: typeof PreviewReportStatusInput.Encoded) => Promise<void>; automation: { connect: ( input: PreviewAutomationHost, callback: (event: PreviewAutomationStreamEvent) => void, options?: { onResubscribe?: () => void }, ) => () => void; respond: (response: PreviewAutomationResponse) => Promise<void>; focusHost: (input: PreviewAutomationHostFocus) => Promise<void>; }; onEvent: ( callback: (event: PreviewEvent) => void, options?: { onResubscribe?: () => void }, ) => () => void; subscribePorts: ( callback: (servers: DiscoveredLocalServerList) => void, options?: { onResubscribe?: () => void }, ) => () => void; }; }