mirror of
https://github.com/stablyai/orca.git
synced 2026-09-29 00:02:56 +00:00
Consolidate tab bar host platform capability
This commit is contained in:
@@ -216,41 +216,23 @@ function TabBarInner({
|
||||
),
|
||||
[agentCmdOverrides, defaultAgent, detectedIds]
|
||||
)
|
||||
const [runtimeHostPlatform, setRuntimeHostPlatform] = useState<NodeJS.Platform | null>(null)
|
||||
useEffect(() => {
|
||||
if (
|
||||
!(globalThis as { __ORCA_WEB_CLIENT__?: boolean }).__ORCA_WEB_CLIENT__ ||
|
||||
!activeRuntimeEnvironmentId
|
||||
) {
|
||||
setRuntimeHostPlatform(null)
|
||||
return
|
||||
}
|
||||
let cancelled = false
|
||||
void window.api.runtime
|
||||
.getStatus()
|
||||
.then((status) => {
|
||||
if (!cancelled) {
|
||||
setRuntimeHostPlatform(status.hostPlatform ?? null)
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
if (!cancelled) {
|
||||
setRuntimeHostPlatform(null)
|
||||
}
|
||||
})
|
||||
return () => {
|
||||
cancelled = true
|
||||
}
|
||||
}, [activeRuntimeEnvironmentId])
|
||||
const isWebClient = (globalThis as { __ORCA_WEB_CLIENT__?: boolean }).__ORCA_WEB_CLIENT__ === true
|
||||
const windowsTerminalCapabilityOwnerKey = getWindowsTerminalCapabilityOwnerKey(
|
||||
activeRuntimeEnvironmentId
|
||||
)
|
||||
const shouldProbeWindowsShellCapabilities =
|
||||
(isWindows || (isWebClient && activeRuntimeEnvironmentId !== null)) &&
|
||||
!worktreeHasRemoteConnection
|
||||
const windowsTerminalCapabilities = useWindowsTerminalCapabilities(
|
||||
shouldProbeWindowsShellCapabilities,
|
||||
false,
|
||||
windowsTerminalCapabilityOwnerKey
|
||||
)
|
||||
// Why: SSH-backed PTYs ignore local Windows shell overrides; showing these
|
||||
// entries there promises PowerShell/CMD/Git Bash but opens the remote shell.
|
||||
const shouldShowWindowsShellMenu =
|
||||
(isWindows || runtimeHostPlatform === 'win32') && !worktreeHasRemoteConnection
|
||||
const windowsTerminalCapabilities = useWindowsTerminalCapabilities(
|
||||
shouldShowWindowsShellMenu,
|
||||
false,
|
||||
getWindowsTerminalCapabilityOwnerKey(activeRuntimeEnvironmentId)
|
||||
)
|
||||
(isWindows || windowsTerminalCapabilities.hostPlatform === 'win32') &&
|
||||
!worktreeHasRemoteConnection
|
||||
const resolvedGroupId = groupId ?? worktreeId
|
||||
|
||||
const statusByRelativePath = useMemo(() => buildStatusMap(gitStatusEntries), [gitStatusEntries])
|
||||
|
||||
@@ -16,7 +16,6 @@ const appStoreSnapshot: {
|
||||
repos: [],
|
||||
worktreesByRepo: {}
|
||||
}
|
||||
let runtimeHostPlatformState: NodeJS.Platform | null | undefined
|
||||
|
||||
const useAppStoreMock = vi.fn(
|
||||
(
|
||||
@@ -58,9 +57,6 @@ vi.mock('react', async () => {
|
||||
useRef: <T>(current: T) => ({ current }),
|
||||
useState: <T>(initial: T | (() => T)) => {
|
||||
const value = typeof initial === 'function' ? (initial as () => T)() : initial
|
||||
if (value === null && runtimeHostPlatformState !== undefined) {
|
||||
return [runtimeHostPlatformState as T, vi.fn()] as const
|
||||
}
|
||||
return [value, vi.fn()] as const
|
||||
}
|
||||
}
|
||||
@@ -251,7 +247,6 @@ describe('TabBar PowerShell launch wiring', () => {
|
||||
appStoreSnapshot.activeRuntimeEnvironmentId = null
|
||||
appStoreSnapshot.repos = []
|
||||
appStoreSnapshot.worktreesByRepo = {}
|
||||
runtimeHostPlatformState = undefined
|
||||
vi.stubGlobal('navigator', { userAgent: 'Windows' })
|
||||
})
|
||||
|
||||
@@ -267,7 +262,8 @@ describe('TabBar PowerShell launch wiring', () => {
|
||||
listDistros: vi.fn().mockResolvedValue([])
|
||||
},
|
||||
pwsh: { isAvailable: vi.fn().mockResolvedValue(true) },
|
||||
gitBash: { isAvailable: vi.fn().mockResolvedValue(false) }
|
||||
gitBash: { isAvailable: vi.fn().mockResolvedValue(false) },
|
||||
runtime: { getStatus: vi.fn().mockResolvedValue({ hostPlatform: 'win32' }) }
|
||||
}
|
||||
})
|
||||
const capabilities = await import('@/lib/windows-terminal-capabilities')
|
||||
@@ -318,7 +314,8 @@ describe('TabBar PowerShell launch wiring', () => {
|
||||
listDistros: vi.fn().mockResolvedValue(['Ubuntu'])
|
||||
},
|
||||
pwsh: { isAvailable: vi.fn().mockResolvedValue(false) },
|
||||
gitBash: { isAvailable: vi.fn().mockResolvedValue(false) }
|
||||
gitBash: { isAvailable: vi.fn().mockResolvedValue(false) },
|
||||
runtime: { getStatus: vi.fn().mockResolvedValue({ hostPlatform: 'win32' }) }
|
||||
}
|
||||
})
|
||||
const capabilities = await import('@/lib/windows-terminal-capabilities')
|
||||
@@ -364,11 +361,11 @@ describe('TabBar PowerShell launch wiring', () => {
|
||||
listDistros: vi.fn().mockResolvedValue(['Ubuntu'])
|
||||
},
|
||||
pwsh: { isAvailable: vi.fn().mockResolvedValue(false) },
|
||||
gitBash: { isAvailable: vi.fn().mockResolvedValue(false) }
|
||||
gitBash: { isAvailable: vi.fn().mockResolvedValue(false) },
|
||||
runtime: { getStatus: vi.fn().mockResolvedValue({ hostPlatform: 'win32' }) }
|
||||
}
|
||||
})
|
||||
appStoreSnapshot.activeRuntimeEnvironmentId = 'web-env-1'
|
||||
runtimeHostPlatformState = 'win32'
|
||||
const capabilities = await import('@/lib/windows-terminal-capabilities')
|
||||
await capabilities.loadWindowsTerminalCapabilities({
|
||||
force: true,
|
||||
@@ -419,7 +416,8 @@ describe('TabBar PowerShell launch wiring', () => {
|
||||
listDistros: vi.fn().mockResolvedValue([])
|
||||
},
|
||||
pwsh: { isAvailable: vi.fn().mockResolvedValue(false) },
|
||||
gitBash: { isAvailable: vi.fn().mockResolvedValue(true) }
|
||||
gitBash: { isAvailable: vi.fn().mockResolvedValue(true) },
|
||||
runtime: { getStatus: vi.fn().mockResolvedValue({ hostPlatform: 'win32' }) }
|
||||
}
|
||||
})
|
||||
const capabilities = await import('@/lib/windows-terminal-capabilities')
|
||||
@@ -474,7 +472,8 @@ describe('TabBar PowerShell launch wiring', () => {
|
||||
listDistros: vi.fn().mockResolvedValue(['Ubuntu'])
|
||||
},
|
||||
pwsh: { isAvailable: vi.fn().mockResolvedValue(true) },
|
||||
gitBash: { isAvailable: vi.fn().mockResolvedValue(true) }
|
||||
gitBash: { isAvailable: vi.fn().mockResolvedValue(true) },
|
||||
runtime: { getStatus: vi.fn().mockResolvedValue({ hostPlatform: 'win32' }) }
|
||||
}
|
||||
})
|
||||
const capabilities = await import('@/lib/windows-terminal-capabilities')
|
||||
|
||||
@@ -12,26 +12,32 @@ function stubTerminalCapabilityApi(args: {
|
||||
pwshAvailable: boolean
|
||||
wslDistros?: string[]
|
||||
gitBashAvailable?: boolean
|
||||
hostPlatform?: NodeJS.Platform | null
|
||||
}): {
|
||||
wslIsAvailable: ReturnType<typeof vi.fn>
|
||||
wslListDistros: ReturnType<typeof vi.fn>
|
||||
pwshIsAvailable: ReturnType<typeof vi.fn>
|
||||
isGitBashAvailable: ReturnType<typeof vi.fn>
|
||||
runtimeGetStatus: ReturnType<typeof vi.fn>
|
||||
} {
|
||||
const wslIsAvailable = vi.fn().mockResolvedValue(args.wslAvailable)
|
||||
const wslListDistros = vi.fn().mockResolvedValue(args.wslDistros ?? [])
|
||||
const pwshIsAvailable = vi.fn().mockResolvedValue(args.pwshAvailable)
|
||||
const isGitBashAvailable = vi.fn().mockResolvedValue(args.gitBashAvailable ?? false)
|
||||
const runtimeGetStatus = vi
|
||||
.fn()
|
||||
.mockResolvedValue({ hostPlatform: 'hostPlatform' in args ? args.hostPlatform : 'win32' })
|
||||
|
||||
vi.stubGlobal('window', {
|
||||
api: {
|
||||
wsl: { isAvailable: wslIsAvailable, listDistros: wslListDistros },
|
||||
pwsh: { isAvailable: pwshIsAvailable },
|
||||
gitBash: { isAvailable: isGitBashAvailable }
|
||||
gitBash: { isAvailable: isGitBashAvailable },
|
||||
runtime: { getStatus: runtimeGetStatus }
|
||||
}
|
||||
})
|
||||
|
||||
return { wslIsAvailable, wslListDistros, pwshIsAvailable, isGitBashAvailable }
|
||||
return { wslIsAvailable, wslListDistros, pwshIsAvailable, isGitBashAvailable, runtimeGetStatus }
|
||||
}
|
||||
|
||||
describe('windows terminal capabilities', () => {
|
||||
@@ -41,19 +47,25 @@ describe('windows terminal capabilities', () => {
|
||||
})
|
||||
|
||||
it('shares WSL, PowerShell, and Git Bash availability between terminal UI consumers', async () => {
|
||||
const { wslIsAvailable, wslListDistros, pwshIsAvailable, isGitBashAvailable } =
|
||||
stubTerminalCapabilityApi({
|
||||
wslAvailable: true,
|
||||
pwshAvailable: true,
|
||||
wslDistros: ['Ubuntu'],
|
||||
gitBashAvailable: true
|
||||
})
|
||||
const {
|
||||
wslIsAvailable,
|
||||
wslListDistros,
|
||||
pwshIsAvailable,
|
||||
isGitBashAvailable,
|
||||
runtimeGetStatus
|
||||
} = stubTerminalCapabilityApi({
|
||||
wslAvailable: true,
|
||||
pwshAvailable: true,
|
||||
wslDistros: ['Ubuntu'],
|
||||
gitBashAvailable: true
|
||||
})
|
||||
|
||||
expect(getCachedWindowsTerminalCapabilities()).toEqual({
|
||||
wslAvailable: false,
|
||||
wslDistros: [],
|
||||
pwshAvailable: false,
|
||||
gitBashAvailable: false,
|
||||
hostPlatform: null,
|
||||
isLoading: false
|
||||
})
|
||||
|
||||
@@ -62,6 +74,7 @@ describe('windows terminal capabilities', () => {
|
||||
wslDistros: ['Ubuntu'],
|
||||
pwshAvailable: true,
|
||||
gitBashAvailable: true,
|
||||
hostPlatform: 'win32',
|
||||
isLoading: false
|
||||
}
|
||||
await expect(loadWindowsTerminalCapabilities()).resolves.toEqual(expected)
|
||||
@@ -72,6 +85,7 @@ describe('windows terminal capabilities', () => {
|
||||
expect(wslListDistros).toHaveBeenCalledTimes(1)
|
||||
expect(pwshIsAvailable).toHaveBeenCalledTimes(1)
|
||||
expect(isGitBashAvailable).toHaveBeenCalledTimes(1)
|
||||
expect(runtimeGetStatus).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
it('keeps WSL available when the PowerShell version probe fails', async () => {
|
||||
@@ -81,7 +95,8 @@ describe('windows terminal capabilities', () => {
|
||||
api: {
|
||||
wsl: { isAvailable: wslIsAvailable, listDistros: vi.fn().mockResolvedValue([]) },
|
||||
pwsh: { isAvailable: pwshIsAvailable },
|
||||
gitBash: { isAvailable: vi.fn().mockResolvedValue(false) }
|
||||
gitBash: { isAvailable: vi.fn().mockResolvedValue(false) },
|
||||
runtime: { getStatus: vi.fn().mockResolvedValue({ hostPlatform: 'win32' }) }
|
||||
}
|
||||
})
|
||||
|
||||
@@ -90,6 +105,7 @@ describe('windows terminal capabilities', () => {
|
||||
wslDistros: [],
|
||||
pwshAvailable: false,
|
||||
gitBashAvailable: false,
|
||||
hostPlatform: 'win32',
|
||||
isLoading: false
|
||||
})
|
||||
})
|
||||
@@ -101,7 +117,8 @@ describe('windows terminal capabilities', () => {
|
||||
api: {
|
||||
wsl: { isAvailable: wslIsAvailable, listDistros: vi.fn().mockResolvedValue([]) },
|
||||
pwsh: { isAvailable: pwshIsAvailable },
|
||||
gitBash: { isAvailable: vi.fn().mockResolvedValue(false) }
|
||||
gitBash: { isAvailable: vi.fn().mockResolvedValue(false) },
|
||||
runtime: { getStatus: vi.fn().mockResolvedValue({ hostPlatform: 'win32' }) }
|
||||
}
|
||||
})
|
||||
|
||||
@@ -125,7 +142,8 @@ describe('windows terminal capabilities', () => {
|
||||
api: {
|
||||
wsl: { isAvailable: wslIsAvailable, listDistros: vi.fn().mockResolvedValue([]) },
|
||||
pwsh: { isAvailable: pwshIsAvailable },
|
||||
gitBash: { isAvailable: vi.fn().mockResolvedValue(false) }
|
||||
gitBash: { isAvailable: vi.fn().mockResolvedValue(false) },
|
||||
runtime: { getStatus: vi.fn().mockResolvedValue({ hostPlatform: 'win32' }) }
|
||||
}
|
||||
})
|
||||
|
||||
@@ -144,6 +162,10 @@ describe('windows terminal capabilities', () => {
|
||||
|
||||
it('does not reuse capability cache between runtime owners', async () => {
|
||||
const isGitBashAvailable = vi.fn().mockResolvedValueOnce(true).mockResolvedValueOnce(false)
|
||||
const runtimeGetStatus = vi
|
||||
.fn()
|
||||
.mockResolvedValueOnce({ hostPlatform: 'win32' })
|
||||
.mockResolvedValueOnce({ hostPlatform: 'linux' })
|
||||
vi.stubGlobal('window', {
|
||||
api: {
|
||||
wsl: {
|
||||
@@ -151,24 +173,28 @@ describe('windows terminal capabilities', () => {
|
||||
listDistros: vi.fn().mockResolvedValue([])
|
||||
},
|
||||
pwsh: { isAvailable: vi.fn().mockResolvedValue(false) },
|
||||
gitBash: { isAvailable: isGitBashAvailable }
|
||||
gitBash: { isAvailable: isGitBashAvailable },
|
||||
runtime: { getStatus: runtimeGetStatus }
|
||||
}
|
||||
})
|
||||
|
||||
await expect(
|
||||
loadWindowsTerminalCapabilities({ ownerKey: 'runtime:host-a' })
|
||||
).resolves.toMatchObject({ gitBashAvailable: true })
|
||||
).resolves.toMatchObject({ gitBashAvailable: true, hostPlatform: 'win32' })
|
||||
await expect(
|
||||
loadWindowsTerminalCapabilities({ ownerKey: 'runtime:host-b' })
|
||||
).resolves.toMatchObject({ gitBashAvailable: false })
|
||||
).resolves.toMatchObject({ gitBashAvailable: false, hostPlatform: 'linux' })
|
||||
|
||||
expect(getCachedWindowsTerminalCapabilities('runtime:host-a')).toMatchObject({
|
||||
gitBashAvailable: true
|
||||
gitBashAvailable: true,
|
||||
hostPlatform: 'win32'
|
||||
})
|
||||
expect(getCachedWindowsTerminalCapabilities('runtime:host-b')).toMatchObject({
|
||||
gitBashAvailable: false
|
||||
gitBashAvailable: false,
|
||||
hostPlatform: 'linux'
|
||||
})
|
||||
expect(isGitBashAvailable).toHaveBeenCalledTimes(2)
|
||||
expect(runtimeGetStatus).toHaveBeenCalledTimes(2)
|
||||
})
|
||||
|
||||
it('does not select the previous owner capabilities while a new owner loads', async () => {
|
||||
@@ -180,7 +206,8 @@ describe('windows terminal capabilities', () => {
|
||||
listDistros: vi.fn().mockResolvedValue([])
|
||||
},
|
||||
pwsh: { isAvailable: vi.fn().mockResolvedValue(false) },
|
||||
gitBash: { isAvailable: isGitBashAvailable }
|
||||
gitBash: { isAvailable: isGitBashAvailable },
|
||||
runtime: { getStatus: vi.fn().mockResolvedValue({ hostPlatform: 'win32' }) }
|
||||
}
|
||||
})
|
||||
|
||||
@@ -197,6 +224,7 @@ describe('windows terminal capabilities', () => {
|
||||
wslDistros: [],
|
||||
pwshAvailable: false,
|
||||
gitBashAvailable: false,
|
||||
hostPlatform: null,
|
||||
isLoading: false
|
||||
})
|
||||
})
|
||||
@@ -209,7 +237,8 @@ describe('windows terminal capabilities', () => {
|
||||
api: {
|
||||
wsl: { isAvailable: wslIsAvailable, listDistros: vi.fn().mockResolvedValue([]) },
|
||||
pwsh: { isAvailable: pwshIsAvailable },
|
||||
gitBash: { isAvailable: isGitBashAvailable }
|
||||
gitBash: { isAvailable: isGitBashAvailable },
|
||||
runtime: { getStatus: vi.fn().mockResolvedValue({ hostPlatform: 'win32' }) }
|
||||
}
|
||||
})
|
||||
|
||||
@@ -218,6 +247,7 @@ describe('windows terminal capabilities', () => {
|
||||
wslDistros: [],
|
||||
pwshAvailable: false,
|
||||
gitBashAvailable: false,
|
||||
hostPlatform: 'win32',
|
||||
isLoading: false
|
||||
})
|
||||
})
|
||||
|
||||
@@ -5,6 +5,7 @@ export type WindowsTerminalCapabilities = {
|
||||
wslDistros: string[]
|
||||
pwshAvailable: boolean
|
||||
gitBashAvailable: boolean
|
||||
hostPlatform: NodeJS.Platform | null
|
||||
isLoading: boolean
|
||||
}
|
||||
|
||||
@@ -13,6 +14,7 @@ const UNAVAILABLE_CAPABILITIES: WindowsTerminalCapabilities = {
|
||||
wslDistros: [],
|
||||
pwshAvailable: false,
|
||||
gitBashAvailable: false,
|
||||
hostPlatform: null,
|
||||
isLoading: false
|
||||
}
|
||||
|
||||
@@ -81,22 +83,27 @@ export function loadWindowsTerminalCapabilities(
|
||||
return pendingCapabilities
|
||||
}
|
||||
|
||||
// Why: Settings and the tab bar need one shared answer. Separate probes can
|
||||
// leave Settings rendering without WSL while the "+" menu already shows it.
|
||||
// Why: Settings, status bar, and paired web tab bars need one shared answer.
|
||||
// Separate probes can leave one surface showing stale Windows shell choices.
|
||||
const requestId = ++nextCapabilityRequestId
|
||||
latestCapabilityRequestIdByOwnerKey.set(ownerKey, requestId)
|
||||
const nextPendingCapabilities = Promise.all([
|
||||
window.api.wsl.isAvailable().catch(() => false),
|
||||
window.api.wsl.listDistros().catch(() => []),
|
||||
window.api.pwsh.isAvailable().catch(() => false),
|
||||
window.api.gitBash.isAvailable().catch(() => false)
|
||||
window.api.gitBash.isAvailable().catch(() => false),
|
||||
window.api.runtime
|
||||
.getStatus()
|
||||
.then((status) => status.hostPlatform ?? null)
|
||||
.catch(() => null)
|
||||
])
|
||||
.then(([wslAvailable, wslDistros, pwshAvailable, gitBashAvailable]) => {
|
||||
.then(([wslAvailable, wslDistros, pwshAvailable, gitBashAvailable, hostPlatform]) => {
|
||||
const capabilities = {
|
||||
wslAvailable,
|
||||
wslDistros,
|
||||
pwshAvailable,
|
||||
gitBashAvailable,
|
||||
hostPlatform,
|
||||
isLoading: false
|
||||
}
|
||||
if (requestId === latestCapabilityRequestIdByOwnerKey.get(ownerKey)) {
|
||||
|
||||
Reference in New Issue
Block a user