Files
orca/src/preload/api/shell-bridge.ts
T
OrcaWinandm4air 1a6d570412 fix(shell): OS open actions follow the file's owner, not the focused server (#27165)
* fix(shell): OS open actions follow the file's owner, not the focused server

Every shell open request (openPath, openInFileManager, openInExternalEditor,
openFilePath, openFileUri) now carries the path's owning host. Main allows
only a local owner, plus the SSH editor launch when the owner is that same
SSH target, and no longer consults the focused server. The renderer reveal
and Open-in checks drop their focused-server check to match.

* test(e2e): name the owner on the local editor control open

* fix(explorer): Open in follows the catalog owner for folder and host-only SSH workspaces

The explorer toolbar and the source-control Open in menu took their SSH
connection from the repo row, which a folder workspace or a host-only SSH
repo does not have, so the route read as local. Both now derive the route
from the catalog-confirmed owner.

* fix(sidebar): Open in keeps the SSH target of folder and host-only SSH rows

The row resolver now returns the SSH connection it resolved alongside the
runtime owner, so a folder workspace or host-only SSH repo with no repo
connection no longer reads as local.

---------

Co-authored-by: m4air <m4air@m4airs-MacBook-Air.local>
2026-10-09 23:39:05 -07:00

54 lines
2.1 KiB
TypeScript

import { ipcRenderer } from 'electron'
import type {
ShellOpenExternalEditorRequest,
ShellOpenExternalEditorResult,
ShellOpenLocalPathResult
} from '../../shared/shell-open-types'
import type { ExecutionHostId } from '../../shared/execution-host'
import type { PreloadApi } from '../api-types'
export const shellApi = {
openPath: (path: string, ownerHostId: ExecutionHostId): Promise<void> =>
ipcRenderer.invoke('shell:openPath', path, ownerHostId),
openInFileManager: (
path: string,
ownerHostId: ExecutionHostId
): Promise<ShellOpenLocalPathResult> =>
ipcRenderer.invoke('shell:openInFileManager', path, ownerHostId),
openInExternalEditor: (
request: ShellOpenExternalEditorRequest
): Promise<ShellOpenExternalEditorResult> =>
ipcRenderer.invoke('shell:openInExternalEditor', request),
openUrl: (url: string): Promise<void> => ipcRenderer.invoke('shell:openUrl', url),
openFilePath: (path: string, ownerHostId: ExecutionHostId): Promise<boolean> =>
ipcRenderer.invoke('shell:openFilePath', path, ownerHostId),
openFileUri: (uri: string, ownerHostId: ExecutionHostId): Promise<void> =>
ipcRenderer.invoke('shell:openFileUri', uri, ownerHostId),
pathsExist: (paths: string[]): Promise<boolean[]> =>
ipcRenderer.invoke('shell:pathsExist', paths),
pathExists: (path: string): Promise<boolean> => ipcRenderer.invoke('shell:pathExists', path),
pickAttachment: (): Promise<string | null> => ipcRenderer.invoke('shell:pickAttachment'),
pickAttachments: (): Promise<string[]> => ipcRenderer.invoke('shell:pickAttachments'),
pickImage: (): Promise<string | null> => ipcRenderer.invoke('shell:pickImage'),
pickRepoIconImage: (): Promise<{ dataUrl: string; fileName: string } | null> =>
ipcRenderer.invoke('shell:pickRepoIconImage'),
pickAudio: (): Promise<string | null> => ipcRenderer.invoke('shell:pickAudio'),
pickDirectory: (args: { defaultPath?: string }): Promise<string | null> =>
ipcRenderer.invoke('shell:pickDirectory', args),
copyFile: (args: { srcPath: string; destPath: string }): Promise<void> =>
ipcRenderer.invoke('shell:copyFile', args)
} satisfies PreloadApi['shell']