Files
orca/src/preload/api/ui-command-event-api.ts
T
Brennan Benson a4606ccae3 fix(cli): orca file open no longer moves your view unless you pass --focus (#24244)
* docs(cli): file open/diff/open-changed say they switch the user's view and are for user requests only

Refs #9944

* fix(cli): file open/diff/open-changed leave the user's view alone unless --focus

`orca file open`, `file diff` and `file open-changed` always switched the
desktop to the target worktree, selected the tab and revealed it in the
sidebar. An agent skill that opens its answer pulled the user out of whatever
they were typing in (#9944), and a phone opening a file moved the desktop too.

The commands now add the tab in its worktree without changing anything on
screen, including when that worktree is the one being viewed: the new tab is
added to the tab bar but the active tab, tab type and focus stay put. In a
worktree the user is not viewing, the tab becomes that worktree's selection so
it is in front when they go there. `--focus` keeps today's behavior.

files.open / files.openDiff take an optional `navigation` target (the existing
RUNTIME_NAVIGATION_TARGETS vocabulary); the CLI sends 'all' for --focus, like
`worktree create --activate`, and nothing otherwise. The renderer moves the
host view only when the target reaches the host; a missing field (phones,
older CLIs) leaves it still. Editor opens for a worktree other than the
on-screen one no longer write the global activeFileId/activeTabType.

Refs #9944

* test(cli): justify the window and runtime stubs in the file-open notification test

* fix(cli): keep phone file opens switching the desktop; the CLI asks for 'caller'

Phone opens send no `navigation` field, and the phone's diff-review "Open in
session" relies on the desktop selecting the diff it opened. A missing field
now keeps the original switch exactly; the CLI says what it wants instead:
'caller' (no host move) by default and 'all' for --focus. Older CLIs, which
send nothing, keep switching as they always have.

Refs #9944

* fix(cli): background file opens select the tab without counting as a visit

A CLI open into a worktree the user is not viewing selected the new tab with
the same activation a user click uses, which stamps lastFocusedAt and the
group's recency list. The worktree jump palette sorts recent tabs by that
time, so every agent `orca file open` into another worktree jumped to the top
of the user's recent tabs.

Editor opens now take a selection mode: 'focus' (default, unchanged),
'background' (select within its worktree without recording focus or recency)
and 'none' (add only). createUnifiedTab and activateTab gain recordFocus:false
for the background case.

Also: tests for reopening an already-open file or diff without --focus, a
comment that file opens move only the host window ('all' acts as 'host'),
root help lines back under 100 columns, and an accurate remote test title.

Refs #9944

* fix(tabs): a background-selected tab still joins its group's tab history

recordFocus:false skipped both the focus-time stamp and the group's
recentTabIds append while still making the tab the group's active tab. Ctrl+Tab
looks the active tab up in that history, so after a background CLI open it
did nothing (or went to the wrong tab) once the user switched to that
worktree, and hydrate kept the broken history across a restart.

Only the focus-time stamp is skipped now; the jump palette's recent rows sort
by that alone, so the palette fix stands.

Refs #9944

* fix(cli): file open/diff/open-changed --focus help says it brings the user to the file

The three commands borrowed the shared --focus line written for terminal
create ("Reveal the created terminal session in Orca"). They now use the
per-command flag help table; terminal create's line is unchanged.

Refs #9944
2026-09-30 20:46:47 -07:00

251 lines
11 KiB
TypeScript

import type { MarkdownDocument } from '../../shared/filesystem-entry-types'
import type { PersistedUIState } from '../../shared/persisted-ui-state-types'
import type { TuiAgent } from '../../shared/tui-agent'
import type { RuntimeNavigationTarget } from '../../shared/runtime-navigation'
import type { TerminalSurfaceCloseTarget } from '../../shared/terminal-surface-close-target'
import type {
WorktreeDefaultTabsLaunch,
WorktreeSetupLaunch,
WorktreeStartupLaunch
} from '../../shared/worktree/launch-types'
import type { FeatureInteractionId } from '../../shared/feature-interactions'
import type { KeybindingActionId } from '../../shared/keybindings'
import type { BrowserFindSource } from '../../shared/browser-find-source'
import type {
BrowserHistoryNavigateCommand,
BrowserPageCommandTarget
} from '../../shared/browser-page-command-target'
import type { BrowserPageZoomCommand } from '../../shared/browser-page-zoom'
import type {
AgentProviderSessionMetadata,
SleepingAgentLaunchConfig
} from '../../shared/agent-session-resume'
import type { TerminalPaneSplitSource } from '../../shared/feature-education-telemetry'
import type {
RuntimeMobileSessionTabMove,
RuntimeTerminalCreateRequestPayload,
RuntimeTerminalPresentation
} from '../../shared/runtime-types'
import type {
RuntimeMobileMarkdownRequest,
RuntimeMobileMarkdownResponse
} from '../../shared/mobile-markdown-document'
import type { TerminalTabCreateReply } from '../../shared/terminal-reveal-identity'
import type {
TerminalTabCloseRequest,
TerminalTabCloseResponse
} from '../../shared/terminal-tab-close'
import type {
SessionTabCloseRequest,
SessionTabCloseResponse
} from '../../shared/session-tab-close'
export type CloseActiveTabPayload = { sourceId: string }
export type UiCommandEventApi = {
get: () => Promise<PersistedUIState>
set: (args: Partial<PersistedUIState>) => Promise<void>
/** Like set, but REJECTS when the update did not reach the host (the web preload's set
* swallows transport failures for offline use). The diff writer needs the distinction:
* folding an unacked patch into its baseline would silently stop retrying it (STA-5781). */
setWithAck?: (args: Partial<PersistedUIState>) => Promise<void>
recordFeatureInteraction: (id: FeatureInteractionId) => Promise<PersistedUIState>
onStateChanged: (callback: (ui: PersistedUIState) => void) => () => void
onOpenSettings: (callback: () => void) => () => void
/** Consumes a one-shot tray/menu-bar "open settings" intent queued before mount. */
consumePendingOpenSettings: () => Promise<boolean>
onOpenSkillShare: (callback: (shareId: string) => void) => () => void
consumePendingSkillShare: () => Promise<string | null>
/** OS "Open With" markdown paths pushed while a renderer is already listening. */
onOpenMarkdownFiles: (callback: (documents: MarkdownDocument[]) => void) => () => void
/** Drains the "Open With" paths queued before this renderer's listener attached. */
consumePendingMarkdownFileOpens: () => Promise<MarkdownDocument[]>
onOpenSetupGuide: (callback: () => void) => () => void
onOpenFeatureTour: (callback: () => void) => () => void
onOpenCrashReport: (callback: () => void) => () => void
onToggleLeftSidebar: (callback: () => void) => () => void
onToggleRightSidebar: (callback: () => void) => () => void
onToggleWorktreePalette: (callback: () => void) => () => void
onToggleFloatingTerminal: (callback: () => void) => () => void
onTerminalShortcutCaptured: (
callback: (data: { actionId: KeybindingActionId }) => void
) => () => void
onOpenQuickOpen: (callback: () => void) => () => void
onToggleQuickCommandsMenu: (callback: () => void) => () => void
onOpenNewWorkspace: (callback: () => void) => () => void
onDeleteCurrentWorkspace: (callback: () => void) => () => void
onOpenWorkspaceBoard: (callback: () => void) => () => void
onOpenTasks: (callback: () => void) => () => void
onToggleAgentDashboard: (callback: () => void) => () => void
onJumpToWorktreeIndex: (callback: (index: number) => void) => () => void
onJumpToTabIndex: (callback: (index: number) => void) => () => void
onWorktreeHistoryNavigate: (callback: (direction: 'back' | 'forward') => void) => () => void
onNewBrowserTab: (callback: () => void) => () => void
onNewMarkdownTab: (callback: () => void) => () => void
onNewSimulatorTab: (callback: () => void) => () => void
onRequestTabCreate: (
callback: (data: {
requestId: string
url: string
worktreeId?: string
browserPageId?: string
sessionProfileId?: string | null
sessionPartition?: string
activate?: boolean
}) => void
) => () => void
replyTabCreate: (reply: { requestId: string; browserPageId?: string; error?: string }) => void
onRequestTabSetProfile: (
callback: (data: {
requestId: string
browserPageId: string
profileId: string
sessionPartition?: string
}) => void
) => () => void
replyTabSetProfile: (reply: { requestId: string; error?: string }) => void
onRequestTabClose: (
callback: (data: { requestId: string; tabId: string | null; worktreeId?: string }) => void
) => () => void
replyTabClose: (reply: {
requestId: string
error?: string
code?: 'browser_tab_not_found'
}) => void
onNewTerminalTab: (callback: () => void) => () => void
onFocusBrowserAddressBar: (callback: (target: BrowserPageCommandTarget) => void) => () => void
onFindInBrowserPage: (source: BrowserFindSource, callback: () => void) => () => void
onReloadBrowserPage: (callback: (target: BrowserPageCommandTarget) => void) => () => void
onBrowserHistoryNavigate: (
callback: (command: BrowserHistoryNavigateCommand) => void
) => () => void
onZoomBrowserPage: (callback: (command: BrowserPageZoomCommand) => void) => () => void
onScrollBrowserPage?: (
callback: (event: { browserPageId: string; deltaX: number; deltaY: number }) => void
) => () => void
onHardReloadBrowserPage: (callback: (target: BrowserPageCommandTarget) => void) => () => void
onCloseActiveTab: (callback: (payload?: CloseActiveTabPayload) => void) => () => void
onCloseFloatingItem: (callback: (payload: { sourceId: string }) => void) => () => void
onSelectFloatingIndex: (callback: (payload: { index: number }) => void) => () => void
onSwitchTab: (callback: (direction: 1 | -1) => void) => () => void
onSwitchTabAcrossAllTypes: (callback: (direction: 1 | -1) => void) => () => void
onSwitchRecentTab: (callback: () => void) => () => void
onSwitchTerminalTab: (callback: (direction: 1 | -1) => void) => () => void
onCtrlTabKeyDown: (callback: (data: { shiftKey: boolean }) => void) => () => void
onCtrlTabKeyUp: (callback: () => void) => () => void
onToggleStatusBar: (callback: () => void) => () => void
onDictationKeyDown: (callback: () => void) => () => void
onExportPdfRequested: (callback: () => void) => () => void
onAppMenuPaste: (callback: () => void) => () => void
onAppMenuSelectionAction: (callback: (action: 'copy' | 'select-all') => void) => () => void
onEditableContextPaste: (callback: (data: { plainTextOnly: boolean }) => void) => () => void
onActivateWorktree: (
callback: (data: {
repoId: string
worktreeId: string
setup?: WorktreeSetupLaunch
startup?: WorktreeStartupLaunch
defaultTabs?: WorktreeDefaultTabsLaunch
}) => void
) => () => void
onCreateTerminal: (
callback: (data: {
requestId?: string
worktreeId: string
command?: string
cwd?: string
env?: Record<string, string>
launchConfig?: SleepingAgentLaunchConfig
resumeProviderSession?: AgentProviderSessionMetadata
launchToken?: string
launchAgent?: TuiAgent
viewMode?: 'terminal' | 'chat'
title?: string
ptyId?: string
activate?: boolean
focus?: boolean
presentation?: RuntimeTerminalPresentation
surfaceOwner?: false
tabId?: string
leafId?: string
splitFromLeafId?: string
splitDirection?: 'horizontal' | 'vertical'
splitTelemetrySource?: TerminalPaneSplitSource
}) => void
) => () => void
onRequestTerminalCreate: (
callback: (data: RuntimeTerminalCreateRequestPayload) => void
) => () => void
onRequestTerminalTabMount: (
callback: (data: { worktreeId: string; tabId?: string; ptyId?: string }) => void
) => () => void
replyTerminalCreate: (reply: TerminalTabCreateReply) => void
onSplitTerminal: (
callback: (data: {
tabId: string
paneRuntimeId: number
direction: 'horizontal' | 'vertical'
command?: string
worktreeId?: string
sourceLeafId?: string
telemetrySource?: TerminalPaneSplitSource
newLeafId?: string
}) => void
) => () => void
onRenameTerminal: (
callback: (data: { tabId: string; title: string | null }) => void
) => () => void
onFocusTerminal: (
callback: (data: {
tabId: string
worktreeId: string
leafId?: string | null
ackPaneKeyOnSuccess?: string
flashFocusedPane?: boolean
scrollToBottomIfOutputSinceLastView?: boolean
}) => void
) => () => void
onFocusEditorTab: (
callback: (data: {
tabId: string
worktreeId: string
/** The user clicked a notification, so revealing the tab is navigation and not a courtesy. */
userInitiated?: boolean
}) => void
) => () => void
onCloseSessionTab: (callback: (data: { tabId: string; worktreeId: string }) => void) => () => void
onSessionTabCloseRequest: (callback: (request: SessionTabCloseRequest) => void) => () => void
respondSessionTabClose: (response: SessionTabCloseResponse) => void
onMoveSessionTab: (
callback: (data: { worktreeId: string } & RuntimeMobileSessionTabMove) => void
) => () => void
onOpenFileFromMobile: (
callback: (data: {
worktreeId: string
filePath: string
relativePath: string
runtimeEnvironmentId?: string
navigation?: RuntimeNavigationTarget
}) => void
) => () => void
onOpenDiffFromMobile: (
callback: (data: {
worktreeId: string
filePath: string
relativePath: string
staged: boolean
runtimeEnvironmentId?: string
navigation?: RuntimeNavigationTarget
}) => void
) => () => void
onMobileMarkdownRequest: (callback: (request: RuntimeMobileMarkdownRequest) => void) => () => void
respondMobileMarkdownRequest: (response: RuntimeMobileMarkdownResponse) => void
onCloseTerminal: (callback: (target: TerminalSurfaceCloseTarget) => void) => () => void
onTerminalTabCloseRequest: (callback: (request: TerminalTabCloseRequest) => void) => () => void
respondTerminalTabClose: (response: TerminalTabCloseResponse) => void
onSleepWorktree: (callback: (data: { worktreeId: string }) => void) => () => void
onResumeSleepingAgents: (callback: (data: { worktreeId: string }) => void) => () => void
onTerminalZoom: (callback: (direction: 'in' | 'out' | 'reset') => void) => () => void
onSystemResumed: (callback: () => void) => () => void
}