mirror of
https://github.com/stablyai/orca.git
synced 2026-11-04 11:07:42 +00:00
fix(wsl): a workspace switch wakes the explorer and git status only while recent (#35897)
The explorer's workspace-open read and the first git status took a pending user switch with no age limit, so a switch no consumer used (sidebar closed) could be redeemed long after the distro stopped. They now use the same 10 s window as pane restore and git history, through one shared takeRecentWorkspaceSwitchWake.
This commit is contained in:
@@ -5,7 +5,8 @@ import type { DirCache } from './file-explorer-types'
|
||||
import { useFileExplorerTreeLoadEffects } from './use-file-explorer-tree-load-effects'
|
||||
import {
|
||||
noteUserWorkspaceSwitch,
|
||||
resetWorkspaceSwitchUserActsForTests
|
||||
resetWorkspaceSwitchUserActsForTests,
|
||||
WORKSPACE_SWITCH_MOUNT_WINDOW_MS
|
||||
} from '@/lib/workspace-switch-user-act'
|
||||
|
||||
afterEach(() => {
|
||||
@@ -141,6 +142,19 @@ it("only the user's own switch to a workspace lets its open read start a stopped
|
||||
renderHook(useFileExplorerTreeLoadEffects, { initialProps: props })
|
||||
expect(props.resetAndLoad).toHaveBeenCalledExactlyOnceWith({ wakeWslDistro: true })
|
||||
})
|
||||
it('an old switch no consumer used is not redeemed by a later mount (E9)', () => {
|
||||
vi.useFakeTimers()
|
||||
try {
|
||||
const { props } = params()
|
||||
// The user switched here with the sidebar closed; the explorer mounts long after.
|
||||
noteUserWorkspaceSwitch(props.workspaceKey)
|
||||
vi.advanceTimersByTime(WORKSPACE_SWITCH_MOUNT_WINDOW_MS + 1)
|
||||
renderHook(useFileExplorerTreeLoadEffects, { initialProps: props })
|
||||
expect(props.resetAndLoad).toHaveBeenCalledExactlyOnceWith(undefined)
|
||||
} finally {
|
||||
vi.useRealTimers()
|
||||
}
|
||||
})
|
||||
it('a restore, remount or re-open with no new switch never wakes WSL', () => {
|
||||
const { props } = params()
|
||||
renderHook(useFileExplorerTreeLoadEffects, { initialProps: props })
|
||||
|
||||
@@ -4,15 +4,13 @@ import type { RuntimeClientTarget } from '../../../../../../shared/runtime-clien
|
||||
import { getConnectionId } from '@/lib/connection-context'
|
||||
import { getRuntimeGitHistory } from '@/runtime/runtime-git-status-client'
|
||||
import type { GitHistoryPanelState } from './git-history-panel'
|
||||
import { takeWorkspaceSwitchWake } from '@/lib/workspace-switch-user-act'
|
||||
import { takeRecentWorkspaceSwitchWake } from '@/lib/workspace-switch-user-act'
|
||||
|
||||
const EMPTY_GIT_HISTORY_STATE: GitHistoryPanelState = { status: 'idle' }
|
||||
|
||||
/** `wakeWslDistro`: the user opening Commits or its Refresh, a user act that may start a stopped WSL distro. */
|
||||
type HistoryRefresh = { wakeWslDistro?: boolean }
|
||||
const SHOWN_HISTORY_LOAD: HistoryRefresh = { wakeWslDistro: true }
|
||||
// The mounts and re-shows a user's own workspace switch causes land within this window.
|
||||
const SWITCH_MOUNT_WINDOW_MS = 10_000
|
||||
|
||||
/** Cache/prune history per worktree and reject superseded requests; refreshGitHistoryRef lets effects refresh without callback resubscription. */
|
||||
export function useSourceControlGitHistory({
|
||||
@@ -169,10 +167,7 @@ export function useSourceControlGitHistory({
|
||||
// workspace (once per switch). A remount, restore or Orca's own re-show stays background (E9).
|
||||
const userAct =
|
||||
userExpanded ||
|
||||
(activeWorktreeId !== null &&
|
||||
takeWorkspaceSwitchWake(activeWorktreeId, 'git-history', {
|
||||
withinMs: SWITCH_MOUNT_WINDOW_MS
|
||||
}))
|
||||
(activeWorktreeId !== null && takeRecentWorkspaceSwitchWake(activeWorktreeId, 'git-history'))
|
||||
void refreshGitHistoryRef.current(userAct ? SHOWN_HISTORY_LOAD : undefined)
|
||||
}, [
|
||||
// History uses compareBaseRef, which can move independently of effectiveBaseRef.
|
||||
|
||||
@@ -6,7 +6,7 @@ import { decideExpandedDirLoad } from './file-explorer-stale-dir-cache'
|
||||
import { clearFileExplorerUndoHistory } from './fileExplorerUndoRedo'
|
||||
import type { DirCache } from './file-explorer-types'
|
||||
import { splitPathSegments } from './path-tree'
|
||||
import { takeWorkspaceSwitchWake } from '@/lib/workspace-switch-user-act'
|
||||
import { takeRecentWorkspaceSwitchWake } from '@/lib/workspace-switch-user-act'
|
||||
|
||||
type UseFileExplorerTreeLoadEffectsParams = {
|
||||
/** The store worktree id, keying the user's own switches to this workspace. */
|
||||
@@ -91,7 +91,7 @@ export function useFileExplorerTreeLoadEffects({
|
||||
resetSelection()
|
||||
setNameFilterQuery('')
|
||||
// Only the user's own switch here may start its stopped WSL distro (E9), once per switch.
|
||||
const wake = workspaceKey !== null && takeWorkspaceSwitchWake(workspaceKey, 'explorer')
|
||||
const wake = workspaceKey !== null && takeRecentWorkspaceSwitchWake(workspaceKey, 'explorer')
|
||||
resetAndLoad(wake ? { wakeWslDistro: true } : undefined)
|
||||
clearFileExplorerUndoHistory()
|
||||
}, [visibleFilesWorktreePath, resetSelection]) // eslint-disable-line react-hooks/exhaustive-deps
|
||||
|
||||
@@ -5,7 +5,7 @@ import { isGitRepoKind } from '../../../../shared/repo-kind'
|
||||
import { getExecutionHostIdForWorktree } from '@/lib/worktree-runtime-owner'
|
||||
import { refreshGitStatusForWorktree } from './git-status-refresh'
|
||||
import { isWindowVisible } from '@/lib/window-visibility-interval'
|
||||
import { takeWorkspaceSwitchWake } from '@/lib/workspace-switch-user-act'
|
||||
import { takeRecentWorkspaceSwitchWake } from '@/lib/workspace-switch-user-act'
|
||||
import {
|
||||
hasInteractiveActiveGitStatusConsumer,
|
||||
shouldPollActiveGitStatus
|
||||
@@ -134,7 +134,7 @@ export function useGitStatusPolling(options: { enabled?: boolean } = {}): void {
|
||||
}
|
||||
// Only the first status after the user's own switch here may start WSL (E9); polls, remounts
|
||||
// and restores stay gated.
|
||||
const wakeWslDistro = takeWorkspaceSwitchWake(activeWorktreeId, 'git-status')
|
||||
const wakeWslDistro = takeRecentWorkspaceSwitchWake(activeWorktreeId, 'git-status')
|
||||
await refreshGitStatusForWorktree({
|
||||
target: copy.transport,
|
||||
ownerHostId: copy.hostId,
|
||||
|
||||
+2
-7
@@ -1,4 +1,4 @@
|
||||
import { takeWorkspaceSwitchWake } from '@/lib/workspace-switch-user-act'
|
||||
import { takeRecentWorkspaceSwitchWake } from '@/lib/workspace-switch-user-act'
|
||||
import { holdPaneForStoppedWslDistro } from '../wsl-distro-stopped-hold-state'
|
||||
import { recordPtyConnectDiagnostic } from './pty-connect-limits'
|
||||
import type { ConnectPanePtySession } from './connect-pane-pty-session'
|
||||
@@ -7,9 +7,6 @@ import type { WslDistroHoldState } from '../../../../../shared/wsl-distro-stoppe
|
||||
// Clears the saved screen the hold painted so the restore below paints it once.
|
||||
const CLEAR_HELD_SCREEN = '\x1b[0m\x1b[2J\x1b[3J\x1b[H'
|
||||
|
||||
// The mounts a user's switch itself causes land well inside this; a later remount does not.
|
||||
const SWITCH_MOUNT_WINDOW_MS = 10_000
|
||||
|
||||
/**
|
||||
* Whether a local restore may start a stopped WSL distro: only Start, or this pane's first mount
|
||||
* after the user's own switch to its workspace (once per switch, while recent). Anything else is
|
||||
@@ -22,9 +19,7 @@ export function wslRestoreStartArgs(
|
||||
if (
|
||||
session.runtimeEnvironmentId ||
|
||||
userAct ||
|
||||
takeWorkspaceSwitchWake(session.deps.worktreeId, `pane-restore:${session.cacheKey}`, {
|
||||
withinMs: SWITCH_MOUNT_WINDOW_MS
|
||||
})
|
||||
takeRecentWorkspaceSwitchWake(session.deps.worktreeId, `pane-restore:${session.cacheKey}`)
|
||||
) {
|
||||
return {}
|
||||
}
|
||||
|
||||
@@ -30,6 +30,9 @@ export function lastUserWorkspaceSwitchAt(workspaceKey: string): number | undefi
|
||||
return lastSwitchAt.get(workspaceKey)
|
||||
}
|
||||
|
||||
/** How recent a switch must be for the mounts it causes to count as that switch (`withinMs`). */
|
||||
export const WORKSPACE_SWITCH_MOUNT_WINDOW_MS = 10_000
|
||||
|
||||
/**
|
||||
* True once per noted switch for each consumer; a remount or re-render with no new switch is false.
|
||||
* `withinMs` also requires the switch to be that recent (the mounts a switch itself causes).
|
||||
@@ -52,6 +55,13 @@ export function takeWorkspaceSwitchWake(
|
||||
return true
|
||||
}
|
||||
|
||||
/** `takeWorkspaceSwitchWake` for a consumer the switch itself mounts or runs. */
|
||||
export function takeRecentWorkspaceSwitchWake(workspaceKey: string, consumer: string): boolean {
|
||||
return takeWorkspaceSwitchWake(workspaceKey, consumer, {
|
||||
withinMs: WORKSPACE_SWITCH_MOUNT_WINDOW_MS
|
||||
})
|
||||
}
|
||||
|
||||
export function resetWorkspaceSwitchUserActsForTests(): void {
|
||||
lastSwitchAt.clear()
|
||||
switchCounts.clear()
|
||||
|
||||
Reference in New Issue
Block a user