mirror of
https://github.com/stablyai/orca.git
synced 2026-09-22 08:02:28 +00:00
fix(window): keep automated Electron launches out of the foreground (#17347)
This commit is contained in:
@@ -520,7 +520,11 @@ async function main() {
|
||||
|
||||
if (!options.dryRun) {
|
||||
const mainPath = buildAppIfNeeded(repoRoot, options.skipBuild)
|
||||
app = await electron.launch({ args: [mainPath], env: launchEnv })
|
||||
app = await electron.launch({
|
||||
args: [mainPath],
|
||||
// Why: a validation run must not pull the window over the developer's work.
|
||||
env: { ...launchEnv, ORCA_BACKGROUND_LAUNCH: '1' }
|
||||
})
|
||||
report.electronPaths = await app.evaluate(({ app: electronApp }) => ({
|
||||
home: electronApp.getPath('home'),
|
||||
userData: electronApp.getPath('userData'),
|
||||
|
||||
@@ -624,6 +624,11 @@ const isHelpOrVersion = forwardedRaw.some((a) => a === '--help' || a === '-h' ||
|
||||
if (!isHelpOrVersion && process.env.ORCA_DEV_INSTANCE_LABEL) {
|
||||
console.error(`[orca-dev] Instance: ${process.env.ORCA_DEV_INSTANCE_LABEL}`)
|
||||
}
|
||||
// Why: automation launches this app while someone is working; announce that the
|
||||
// window will come up without taking the foreground so the mode is visible in logs.
|
||||
if (!isHelpOrVersion && process.env.ORCA_BACKGROUND_LAUNCH === '1') {
|
||||
console.error('[orca-dev] Background launch: window shows without stealing focus')
|
||||
}
|
||||
let forwardedExtras = []
|
||||
if (!userPassedPort && !isHelpOrVersion) {
|
||||
const envPortRaw = process.env.REMOTE_DEBUGGING_PORT
|
||||
|
||||
@@ -191,7 +191,10 @@ async function runInsideSession(evidenceDir) {
|
||||
env: {
|
||||
...process.env,
|
||||
ORCA_E2E_FORWARD_APP_LOGS: '1',
|
||||
ORCA_E2E_NATIVE_IBUS_HANGUL: '1'
|
||||
ORCA_E2E_NATIVE_IBUS_HANGUL: '1',
|
||||
// Why: native IBus key injection only reaches a window the window manager
|
||||
// has focused, so this run opts out of the background-launch policy.
|
||||
ORCA_E2E_FOREGROUND: '1'
|
||||
},
|
||||
stdio: 'inherit'
|
||||
}
|
||||
|
||||
@@ -216,6 +216,7 @@ async function runValidation(mode) {
|
||||
env: {
|
||||
...env,
|
||||
NODE_ENV: 'development',
|
||||
ORCA_BACKGROUND_LAUNCH: '1',
|
||||
ORCA_DEV_USER_DATA_PATH: userDataPath,
|
||||
HOME: isolatedHome,
|
||||
USERPROFILE: isolatedHome,
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
"../src/main/startup/single-instance-lock.ts",
|
||||
"../src/main/startup/startup-diagnostics.ts",
|
||||
"../src/main/window/focus-existing-window.ts",
|
||||
"../src/main/window/foreground-activation-policy.ts",
|
||||
"../src/main/window/macos-app-activation.ts"
|
||||
],
|
||||
"compilerOptions": {
|
||||
|
||||
+5
-6
@@ -260,7 +260,8 @@ import {
|
||||
type SystemTrayOptions
|
||||
} from './tray/system-tray'
|
||||
import { createMacAppActivationHandler } from './window/macos-app-activation'
|
||||
import { focusExistingMainWindow } from './window/focus-existing-window'
|
||||
import { focusExistingMainWindow, safelyRevealWindow } from './window/focus-existing-window'
|
||||
import { applyBackgroundActivationPolicy } from './window/foreground-activation-policy'
|
||||
import { notifyMainWindowBecameVisible } from './window/main-window-visibility'
|
||||
import { CodexAccountService } from './codex-accounts/service'
|
||||
import { CodexRuntimeHomeService } from './codex-accounts/runtime-home-service'
|
||||
@@ -1390,11 +1391,7 @@ async function prepareCodexSessionResumeForLaunch(args: {
|
||||
// Why: restore the window the close handler may have hidden to tray, or reopen it (dock-reactivation style) if fully torn down.
|
||||
function showMainWindowFromTray(): void {
|
||||
if (mainWindow && !mainWindow.isDestroyed()) {
|
||||
if (mainWindow.isMinimized()) {
|
||||
mainWindow.restore()
|
||||
}
|
||||
mainWindow.show()
|
||||
mainWindow.focus()
|
||||
safelyRevealWindow(mainWindow)
|
||||
return
|
||||
}
|
||||
if (!isQuittingForUpdate()) {
|
||||
@@ -2362,6 +2359,8 @@ function shouldSuppressCodexAutoApprovalSyntheticTitleFromHook(args: {
|
||||
|
||||
void app.whenReady().then(async () => {
|
||||
logStartupMilestone('app-ready')
|
||||
// Why: a headless automated run must not claim a macOS Dock tile or the menu bar.
|
||||
applyBackgroundActivationPolicy({ warn: console.warn })
|
||||
installMainThreadHangWatchdog({ userDataPath: getCanonicalUserDataPath() })
|
||||
const hangDetection = consumeHangDetectionMarker(
|
||||
hangDetectionMarkerPath(getCanonicalUserDataPath())
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
onDashboardPopoutOpenChanged
|
||||
} from '../window/dashboard-popout-window'
|
||||
import { safelyRevealWindow } from '../window/focus-existing-window'
|
||||
import { isBackgroundLaunch } from '../window/foreground-activation-policy'
|
||||
import { getTrustedUIRendererWindow, isTrustedUIRenderer, sendToTrustedUIRenderer } from './ui'
|
||||
import {
|
||||
admitDashboardSnapshot,
|
||||
@@ -142,10 +143,12 @@ export function registerDashboardPopoutHandlers(
|
||||
}
|
||||
safelyRevealWindow(mainWindow)
|
||||
mainWindow.webContents.send('ui:revealDashboardAgent', args)
|
||||
try {
|
||||
app.focus({ steal: true })
|
||||
} catch {
|
||||
// Best-effort; the per-window focus above may still bring it forward.
|
||||
if (!isBackgroundLaunch()) {
|
||||
try {
|
||||
app.focus({ steal: true })
|
||||
} catch {
|
||||
// Best-effort; the per-window focus above may still bring it forward.
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@@ -4,6 +4,8 @@ import type {
|
||||
NotificationDispatchResult,
|
||||
NotificationSettings
|
||||
} from '../../shared/notification-settings-types'
|
||||
import { safelyRevealWindow } from '../window/focus-existing-window'
|
||||
import { isBackgroundLaunch } from '../window/foreground-activation-policy'
|
||||
import { getRepoIdFromWorktreeId } from '../../shared/worktree/id'
|
||||
import { parsePaneKey } from '../../shared/stable-pane-id'
|
||||
import type { buildNotificationOptions } from './notification-options'
|
||||
@@ -79,14 +81,10 @@ export function deliverNativeNotification(
|
||||
if (!win || win.isDestroyed()) {
|
||||
return
|
||||
}
|
||||
if (process.platform === 'darwin') {
|
||||
if (process.platform === 'darwin' && !isBackgroundLaunch()) {
|
||||
app.focus({ steal: true })
|
||||
}
|
||||
if (win.isMinimized()) {
|
||||
win.restore()
|
||||
}
|
||||
win.show()
|
||||
win.focus()
|
||||
safelyRevealWindow(win)
|
||||
win.webContents.send('ui:activateWorktree', {
|
||||
repoId,
|
||||
worktreeId: args.worktreeId
|
||||
|
||||
@@ -2,6 +2,7 @@ import { app, BrowserWindow, nativeTheme, type WebContents } from 'electron'
|
||||
import { join } from 'node:path'
|
||||
import { is } from '@electron-toolkit/utils'
|
||||
import type { Store } from '../persistence'
|
||||
import { isBackgroundLaunch, showWindowWithoutStealingFocus } from './foreground-activation-policy'
|
||||
import { rectHasVisibleAreaOnAnyDisplay } from './window-bounds-validation'
|
||||
import { sendToTrustedUIRenderer } from '../ipc/ui'
|
||||
import { installPrivilegedWindowNavigationPolicy } from './privileged-window-navigation'
|
||||
@@ -145,7 +146,9 @@ export function createOrFocusDashboardPopout(
|
||||
if (dashboardPopoutWindow.isMinimized()) {
|
||||
dashboardPopoutWindow.restore()
|
||||
}
|
||||
dashboardPopoutWindow.focus()
|
||||
if (!isBackgroundLaunch()) {
|
||||
dashboardPopoutWindow.focus()
|
||||
}
|
||||
if (view) {
|
||||
dashboardPopoutWindow.webContents.send('dashboard:viewRequested', view)
|
||||
}
|
||||
@@ -242,9 +245,7 @@ export function createOrFocusDashboardPopout(
|
||||
})
|
||||
|
||||
window.once('ready-to-show', () => {
|
||||
if (!window.isDestroyed()) {
|
||||
window.show()
|
||||
}
|
||||
showWindowWithoutStealingFocus(window)
|
||||
})
|
||||
|
||||
// Bounds persistence — mirrors the main window's debounced/frozen approach so
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { App, BrowserWindow } from 'electron'
|
||||
import { isBackgroundLaunch, showWindowWithoutStealingFocus } from './foreground-activation-policy'
|
||||
|
||||
type FocusTimer = (callback: () => void, ms: number) => unknown
|
||||
|
||||
@@ -14,6 +15,11 @@ export type FocusExistingMainWindowOptions = {
|
||||
}
|
||||
|
||||
function safelyFocusApp(app: Pick<App, 'focus'>): void {
|
||||
// Why: stealing the foreground is the whole point of this path for a real
|
||||
// second-instance launch, and exactly what an automated run must never do.
|
||||
if (isBackgroundLaunch()) {
|
||||
return
|
||||
}
|
||||
try {
|
||||
app.focus({ steal: true })
|
||||
} catch {
|
||||
@@ -32,8 +38,10 @@ export function safelyRevealWindow(window: BrowserWindow): void {
|
||||
if (window.isMinimized()) {
|
||||
window.restore()
|
||||
}
|
||||
window.show()
|
||||
window.focus()
|
||||
showWindowWithoutStealingFocus(window)
|
||||
if (!isBackgroundLaunch()) {
|
||||
window.focus()
|
||||
}
|
||||
}
|
||||
|
||||
function pulseAlwaysOnTop(window: BrowserWindow, setTimer: FocusTimer): void {
|
||||
@@ -74,7 +82,9 @@ function activateWindow(
|
||||
): void {
|
||||
safelyFocusApp(app)
|
||||
safelyRevealWindow(window)
|
||||
if (platform === 'win32') {
|
||||
// Why: moveTop/always-on-top/refocus are foreground reinforcement; in a
|
||||
// background launch they would drag the window over the developer's work.
|
||||
if (platform === 'win32' && !isBackgroundLaunch()) {
|
||||
try {
|
||||
window.moveTop()
|
||||
} catch {
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
import type { BrowserWindow } from 'electron'
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
|
||||
vi.mock('electron', () => ({ app: { dock: { hide: vi.fn() }, setActivationPolicy: vi.fn() } }))
|
||||
|
||||
const {
|
||||
applyBackgroundActivationPolicy,
|
||||
isBackgroundLaunch,
|
||||
isWindowlessLaunch,
|
||||
showWindowWithoutStealingFocus
|
||||
} = await import('./foreground-activation-policy')
|
||||
|
||||
function makeWindow(destroyed = false): BrowserWindow & {
|
||||
show: ReturnType<typeof vi.fn>
|
||||
showInactive: ReturnType<typeof vi.fn>
|
||||
} {
|
||||
return {
|
||||
isDestroyed: () => destroyed,
|
||||
show: vi.fn(() => {}),
|
||||
showInactive: vi.fn(() => {})
|
||||
} as unknown as BrowserWindow & {
|
||||
show: ReturnType<typeof vi.fn>
|
||||
showInactive: ReturnType<typeof vi.fn>
|
||||
}
|
||||
}
|
||||
|
||||
describe('isBackgroundLaunch', () => {
|
||||
it('covers headless and headful E2E plus opted-in dev launches', () => {
|
||||
expect(isBackgroundLaunch({ ORCA_E2E_HEADLESS: '1' })).toBe(true)
|
||||
expect(isBackgroundLaunch({ ORCA_E2E_HEADFUL: '1' })).toBe(true)
|
||||
expect(isBackgroundLaunch({ ORCA_BACKGROUND_LAUNCH: '1' })).toBe(true)
|
||||
expect(isBackgroundLaunch({})).toBe(false)
|
||||
})
|
||||
|
||||
it('lets native-focus specs opt back into the foreground', () => {
|
||||
expect(isBackgroundLaunch({ ORCA_E2E_HEADFUL: '1', ORCA_E2E_FOREGROUND: '1' })).toBe(false)
|
||||
expect(isWindowlessLaunch({ ORCA_E2E_HEADLESS: '1', ORCA_E2E_FOREGROUND: '1' })).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe('isWindowlessLaunch', () => {
|
||||
it('is headless-only; a headful run still paints', () => {
|
||||
expect(isWindowlessLaunch({ ORCA_E2E_HEADLESS: '1' })).toBe(true)
|
||||
expect(isWindowlessLaunch({ ORCA_E2E_HEADLESS: '1', ORCA_E2E_HEADFUL: '1' })).toBe(false)
|
||||
expect(isWindowlessLaunch({ ORCA_BACKGROUND_LAUNCH: '1' })).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe('showWindowWithoutStealingFocus', () => {
|
||||
it('keeps a headless window off screen', () => {
|
||||
const window = makeWindow()
|
||||
showWindowWithoutStealingFocus(window, { ORCA_E2E_HEADLESS: '1' })
|
||||
expect(window.show).not.toHaveBeenCalled()
|
||||
expect(window.showInactive).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('shows a background window without activating it', () => {
|
||||
const window = makeWindow()
|
||||
showWindowWithoutStealingFocus(window, { ORCA_BACKGROUND_LAUNCH: '1' })
|
||||
expect(window.showInactive).toHaveBeenCalledOnce()
|
||||
expect(window.show).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('shows normally for a real user launch', () => {
|
||||
const window = makeWindow()
|
||||
showWindowWithoutStealingFocus(window, {})
|
||||
expect(window.show).toHaveBeenCalledOnce()
|
||||
expect(window.showInactive).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('ignores a destroyed window', () => {
|
||||
const window = makeWindow(true)
|
||||
showWindowWithoutStealingFocus(window, {})
|
||||
expect(window.show).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
|
||||
describe('applyBackgroundActivationPolicy', () => {
|
||||
function makeApp() {
|
||||
return {
|
||||
dock: { hide: vi.fn(() => {}) },
|
||||
setActivationPolicy: vi.fn((_policy: 'accessory' | 'prohibited' | 'regular') => {})
|
||||
}
|
||||
}
|
||||
|
||||
it('drops the macOS Dock tile and menu bar for headless runs', () => {
|
||||
const app = makeApp()
|
||||
expect(
|
||||
applyBackgroundActivationPolicy({
|
||||
app,
|
||||
env: { ORCA_E2E_HEADLESS: '1' },
|
||||
platform: 'darwin'
|
||||
})
|
||||
).toBe(true)
|
||||
expect(app.dock.hide).toHaveBeenCalledOnce()
|
||||
expect(app.setActivationPolicy).toHaveBeenCalledWith('accessory')
|
||||
})
|
||||
|
||||
it('leaves a headful or user launch with its normal Dock presence', () => {
|
||||
const headful = makeApp()
|
||||
applyBackgroundActivationPolicy({
|
||||
app: headful,
|
||||
env: { ORCA_E2E_HEADLESS: '1', ORCA_E2E_HEADFUL: '1' },
|
||||
platform: 'darwin'
|
||||
})
|
||||
expect(headful.setActivationPolicy).not.toHaveBeenCalled()
|
||||
|
||||
const user = makeApp()
|
||||
applyBackgroundActivationPolicy({ app: user, env: {}, platform: 'darwin' })
|
||||
expect(user.setActivationPolicy).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('is a no-op off macOS', () => {
|
||||
const app = makeApp()
|
||||
expect(
|
||||
applyBackgroundActivationPolicy({
|
||||
app,
|
||||
env: { ORCA_E2E_HEADLESS: '1' },
|
||||
platform: 'win32'
|
||||
})
|
||||
).toBe(false)
|
||||
expect(app.dock.hide).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,83 @@
|
||||
import { app as electronApp, type BrowserWindow } from 'electron'
|
||||
|
||||
/**
|
||||
* Foreground policy for automated launches (E2E, benchmarks, agent-driven dev
|
||||
* validation). These runs may use the machine, but must never take the OS
|
||||
* foreground away from whatever the developer is doing.
|
||||
*
|
||||
* ORCA_BACKGROUND_LAUNCH=1 opts a normal launch in; ORCA_E2E_FOREGROUND=1 opts
|
||||
* back out for the few specs whose subject *is* native focus (IME, key events).
|
||||
*/
|
||||
|
||||
type ActivationPolicyApp = {
|
||||
dock?: { hide: () => void }
|
||||
setActivationPolicy: (policy: 'accessory' | 'prohibited' | 'regular') => void
|
||||
}
|
||||
|
||||
/** Reads ORCA_BACKGROUND_LAUNCH, ORCA_E2E_FOREGROUND, ORCA_E2E_HEADLESS, ORCA_E2E_HEADFUL. */
|
||||
type PolicyEnv = Readonly<Record<string, string | undefined>>
|
||||
|
||||
/** True when this process must not steal focus, raise windows, or activate the app. */
|
||||
export function isBackgroundLaunch(env: PolicyEnv = process.env): boolean {
|
||||
if (env.ORCA_E2E_FOREGROUND === '1') {
|
||||
return false
|
||||
}
|
||||
return (
|
||||
env.ORCA_BACKGROUND_LAUNCH === '1' ||
|
||||
env.ORCA_E2E_HEADLESS === '1' ||
|
||||
env.ORCA_E2E_HEADFUL === '1'
|
||||
)
|
||||
}
|
||||
|
||||
/** True when no window should reach the screen at all (headless E2E; Playwright drives via CDP). */
|
||||
export function isWindowlessLaunch(env: PolicyEnv = process.env): boolean {
|
||||
return isBackgroundLaunch(env) && env.ORCA_E2E_HEADLESS === '1' && env.ORCA_E2E_HEADFUL !== '1'
|
||||
}
|
||||
|
||||
/**
|
||||
* macOS: a windowless run still claims a Dock tile and the menu bar as it starts
|
||||
* and exits. `accessory` drops both while leaving programmatic activation intact.
|
||||
*/
|
||||
export function applyBackgroundActivationPolicy(
|
||||
options: {
|
||||
app?: ActivationPolicyApp
|
||||
env?: PolicyEnv
|
||||
platform?: NodeJS.Platform
|
||||
warn?: (message: string, error: unknown) => void
|
||||
} = {}
|
||||
): boolean {
|
||||
const platform = options.platform ?? process.platform
|
||||
if (platform !== 'darwin' || !isWindowlessLaunch(options.env ?? process.env)) {
|
||||
return false
|
||||
}
|
||||
try {
|
||||
const app = options.app ?? electronApp
|
||||
app.dock?.hide()
|
||||
app.setActivationPolicy('accessory')
|
||||
return true
|
||||
} catch (error) {
|
||||
options.warn?.('[window] Failed to apply background activation policy', error)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reveal a window without taking the foreground: hidden entirely when windowless,
|
||||
* `showInactive()` (visible, not raised over the active app) in background launches.
|
||||
*/
|
||||
export function showWindowWithoutStealingFocus(
|
||||
window: BrowserWindow,
|
||||
env: PolicyEnv = process.env
|
||||
): void {
|
||||
if (window.isDestroyed()) {
|
||||
return
|
||||
}
|
||||
if (isWindowlessLaunch(env)) {
|
||||
return
|
||||
}
|
||||
if (isBackgroundLaunch(env)) {
|
||||
window.showInactive()
|
||||
return
|
||||
}
|
||||
window.show()
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import { app, type BrowserWindow } from 'electron'
|
||||
import type { Store } from '../persistence'
|
||||
import { getMainE2EConfig } from '../e2e-config'
|
||||
import { isWindowlessLaunch, showWindowWithoutStealingFocus } from './foreground-activation-policy'
|
||||
import { MIN_HEIGHT, MIN_WIDTH, syncTrafficLightPosition } from './main-window-visual-lifecycle'
|
||||
|
||||
export type MainWindowStateLifecycle = {
|
||||
@@ -57,15 +57,14 @@ export function installMainWindowStateLifecycle(args: {
|
||||
handledInitialReadyToShow = true
|
||||
clearInitialRevealFallbackTimer()
|
||||
|
||||
// Why: in E2E headless mode keep the window hidden (Playwright drives via CDP) so tests don't steal focus.
|
||||
const e2eConfig = getMainE2EConfig()
|
||||
if (e2eConfig.headless) {
|
||||
// Why: headless E2E keeps the window off screen entirely (Playwright drives via CDP).
|
||||
if (isWindowlessLaunch()) {
|
||||
return
|
||||
}
|
||||
if (savedMaximized) {
|
||||
mainWindow.maximize()
|
||||
}
|
||||
mainWindow.show()
|
||||
showWindowWithoutStealingFocus(mainWindow)
|
||||
}
|
||||
mainWindow.on('ready-to-show', revealInitialWindow)
|
||||
if (revealOnDidFinishLoad === true) {
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
# Keep Automated Runs Out of the Foreground
|
||||
|
||||
Tests and agent-driven app launches share the developer's machine. They may use it; they must never
|
||||
take the foreground — no window raised over the editor, no focus stolen, no Dock tile churn.
|
||||
|
||||
`src/main/window/foreground-activation-policy.ts` enforces this in the main process. It is on
|
||||
whenever `ORCA_E2E_HEADLESS=1`, `ORCA_E2E_HEADFUL=1`, or `ORCA_BACKGROUND_LAUNCH=1`:
|
||||
|
||||
- headless → the window never reaches the screen (Playwright drives it via CDP)
|
||||
- headful / background → `showInactive()`, no `app.focus({ steal: true })`, no
|
||||
`moveTop()`/always-on-top reinforcement
|
||||
- macOS headless → `accessory` activation policy, so no Dock tile and no menu-bar takeover
|
||||
|
||||
Rules when adding tests or scripts:
|
||||
|
||||
- Launch through `tests/e2e/helpers/orca-app.ts` (or `orca-restart.ts`) — they already set the env.
|
||||
- A raw `electron.launch()` outside those helpers must pass `ORCA_BACKGROUND_LAUNCH: '1'`.
|
||||
- Call `showInactive()`, never `show()`, when an `app.evaluate()` block reveals a window.
|
||||
- Tag a spec `@headful` only when it needs real pixels; it still runs in the background.
|
||||
- `ORCA_E2E_FOREGROUND=1` is the only opt-out, for runs whose subject _is_ native focus (IME and
|
||||
other OS-level key injection). Add a comment saying why.
|
||||
@@ -21,7 +21,7 @@ export function assertPairedClientWindowRevealed(report: PairedClientWindowRevea
|
||||
}
|
||||
if (!report.isVisible) {
|
||||
throw new Error(
|
||||
`Paired client window stayed hidden after show() (windows: ${report.windowCount})`
|
||||
`Paired client window stayed hidden after showInactive() (windows: ${report.windowCount})`
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -64,8 +64,10 @@ export async function revealPairedClientWindow(
|
||||
const windows = BrowserWindow.getAllWindows()
|
||||
const window = windows[0]
|
||||
const wasVisible = window?.isVisible() ?? false
|
||||
// Why showInactive: the renderer only needs `visibilityState === 'visible'`;
|
||||
// show() would also raise the window over whatever the developer is doing.
|
||||
if (window && !wasVisible) {
|
||||
window.show()
|
||||
window.showInactive()
|
||||
}
|
||||
return {
|
||||
isVisible: window?.isVisible() ?? false,
|
||||
|
||||
@@ -89,6 +89,8 @@ export async function launchInstalledApp({
|
||||
// (logs/daemon/terminal-history) under a controlled dir.
|
||||
...extraEnv,
|
||||
ORCA_E2E_USER_DATA_DIR: userDataDir,
|
||||
// Why: the driven app stays off the foreground so a local run doesn't steal focus.
|
||||
ORCA_BACKGROUND_LAUNCH: '1',
|
||||
HOME: isolatedHome,
|
||||
USERPROFILE: isolatedHome,
|
||||
ORCA_E2E_HOME_DIR: isolatedHome
|
||||
|
||||
Reference in New Issue
Block a user