Files
orca/src/preload/api/worktrees-bridge.ts
T
Neil 6eebdbbaf8 fix(wsl): keep a stopped distro's worktrees and re-list once it runs (#35733)
* fix(wsl): keep a stopped distro's worktrees and re-list once it runs

After a relaunch with a WSL distro stopped, the E9 gate refused the
worktree scan and the sidebar showed "Worktree scan failed" with no rows
until the user pressed Retry. The listing now answers with the stored
rows (non-authoritative, so nothing is pruned), marks the distro stopped
or unverifiable in a new optional field, and re-lists once a wake or a
settled running read shows the distro running. The re-scan is held once
per repo and dropped when the repo is removed. The sidebar shows a
neutral state instead of a failure.

* fix(wsl): make the stopped-distro Retry a user act that may start the distro

The indicator's click now runs the scan inside runAsWslUserAct for the
repo's distro (new worktrees:scanStartingWslDistro IPC), then re-lists,
so clicking it can start the distro instead of re-running the gated
background scan that never could.
2026-10-29 06:54:51 -07:00

121 lines
4.9 KiB
TypeScript

import { ipcRenderer } from 'electron'
import type {
WorktreeBaseStatusEvent,
WorktreeRemoteBranchConflictEvent
} from '../../shared/worktree/base-ref-drift-types'
import type { CreateWorktreeProgressEvent } from '../../shared/worktree/create-types'
import type {
GitStatusMetadataChangedEvent,
WorktreeHeadIdentity
} from '../../shared/worktree/types'
import type { PreloadApi } from '../api-types'
export const worktreesApi = {
listRetiredNames: (args) => ipcRenderer.invoke('worktrees:listRetiredNames', args),
listDetected: (args) => ipcRenderer.invoke('worktrees:listDetected', args),
listKnownForExecutionHost: (args) =>
ipcRenderer.invoke('worktrees:listKnownForExecutionHost', args),
forgetRemovedForExecutionHost: (args) =>
ipcRenderer.invoke('worktrees:forgetRemovedForExecutionHost', args),
cancelListDetected: (args) => ipcRenderer.invoke('worktrees:cancelListDetected', args),
scanStartingWslDistro: (args) => ipcRenderer.invoke('worktrees:scanStartingWslDistro', args),
create: (args) => ipcRenderer.invoke('worktrees:create', args),
adoptProvisionedRoot: (args) => ipcRenderer.invoke('worktrees:adoptProvisionedRoot', args),
onCreateProgress: (callback: (data: CreateWorktreeProgressEvent) => void): (() => void) => {
const listener = (_event: Electron.IpcRendererEvent, data: CreateWorktreeProgressEvent) =>
callback(data)
ipcRenderer.on('createWorktree:progress', listener)
return () => ipcRenderer.removeListener('createWorktree:progress', listener)
},
prefetchCreateBase: (args) => ipcRenderer.invoke('worktrees:prefetchCreateBase', args),
resolvePrBase: (args) => ipcRenderer.invoke('worktrees:resolvePrBase', args),
resolveMrBase: (args) => ipcRenderer.invoke('worktrees:resolveMrBase', args),
previewNestedRemoval: (args) => ipcRenderer.invoke('worktrees:previewNestedRemoval', args),
sleep: (request) => ipcRenderer.invoke('worktrees:sleep', request),
sleepReopened: (worktreeId) => ipcRenderer.invoke('worktrees:sleepReopened', worktreeId),
resumeSleptPane: (pane) => ipcRenderer.invoke('worktrees:resumeSleptPane', pane),
retireStrandedSleepRecord: (args) =>
ipcRenderer.invoke('worktrees:retireStrandedSleepRecord', args),
remove: (args) => ipcRenderer.invoke('worktrees:remove', args),
forgetLocal: (args) => ipcRenderer.invoke('worktrees:forgetLocal', args),
forceDeletePreservedBranch: (args) =>
ipcRenderer.invoke('worktrees:forceDeletePreservedBranch', args),
updateMeta: (args) => ipcRenderer.invoke('worktrees:updateMeta', args),
listLineage: () => ipcRenderer.invoke('worktrees:listLineage'),
updateLineage: (args) => ipcRenderer.invoke('worktrees:updateLineage', args),
persistSortOrder: (args) => ipcRenderer.invoke('worktrees:persistSortOrder', args),
getBranchRenameFailureOutput: (args) =>
ipcRenderer.invoke('worktrees:getBranchRenameFailureOutput', args),
onChanged: (
callback: (data: {
repoId: string
renamed?: { oldWorktreeId: string; newWorktreeId: string }
}) => void
): (() => void) => {
const listener = (
_event: Electron.IpcRendererEvent,
data: { repoId: string; renamed?: { oldWorktreeId: string; newWorktreeId: string } }
) => callback(data)
ipcRenderer.on('worktrees:changed', listener)
return () => ipcRenderer.removeListener('worktrees:changed', listener)
},
onGitStatusMetadataChanged: (
callback: (data: GitStatusMetadataChangedEvent) => void
): (() => void) => {
const listener = (_event: Electron.IpcRendererEvent, data: GitStatusMetadataChangedEvent) =>
callback(data)
ipcRenderer.on('worktrees:gitStatusMetadataChanged', listener)
return () => ipcRenderer.removeListener('worktrees:gitStatusMetadataChanged', listener)
},
onHeadIdentitiesChanged: (
callback: (data: { repoId: string; identities: WorktreeHeadIdentity[] }) => void
): (() => void) => {
const listener = (
_event: Electron.IpcRendererEvent,
data: { repoId: string; identities: WorktreeHeadIdentity[] }
) => callback(data)
ipcRenderer.on('worktrees:headIdentitiesChanged', listener)
return () => ipcRenderer.removeListener('worktrees:headIdentitiesChanged', listener)
},
onBaseStatus: (callback: (data: WorktreeBaseStatusEvent) => void): (() => void) => {
const listener = (_event: Electron.IpcRendererEvent, data: WorktreeBaseStatusEvent) =>
callback(data)
ipcRenderer.on('worktree:baseStatus', listener)
return () => ipcRenderer.removeListener('worktree:baseStatus', listener)
},
onRemoteBranchConflict: (
callback: (data: WorktreeRemoteBranchConflictEvent) => void
): (() => void) => {
const listener = (_event: Electron.IpcRendererEvent, data: WorktreeRemoteBranchConflictEvent) =>
callback(data)
ipcRenderer.on('worktree:remoteBranchConflict', listener)
return () => ipcRenderer.removeListener('worktree:remoteBranchConflict', listener)
}
} satisfies PreloadApi['worktrees']