diff --git a/src/renderer/src/components/sidebar/WorktreeCardIssueDetailSection.tsx b/src/renderer/src/components/sidebar/WorktreeCardIssueDetailSection.tsx
index 76fa707c30b..4cc4ab7837b 100644
--- a/src/renderer/src/components/sidebar/WorktreeCardIssueDetailSection.tsx
+++ b/src/renderer/src/components/sidebar/WorktreeCardIssueDetailSection.tsx
@@ -7,7 +7,7 @@ import {
DropdownMenuTrigger
} from '@/components/ui/dropdown-menu'
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip'
-import { CircleDot, Copy, Ellipsis, ExternalLink, MonitorUp, Pencil } from 'lucide-react'
+import { CircleDot, Copy, Ellipsis, ExternalLink, Globe, MonitorUp, Pencil } from 'lucide-react'
import { translate } from '@/i18n/i18n'
import {
WorktreeCardDetailSection,
@@ -22,6 +22,7 @@ type WorktreeCardIssueDetailSectionProps = {
issueMenuOpen: boolean
onIssueMenuOpenChange: (open: boolean) => void
onCopyIssueLink?: () => void
+ onOpenIssueInBrowser?: (url: string) => void
onEditIssue?: (event: React.MouseEvent) => void
onOpenGitHubIssueInOrca?: (event: React.MouseEvent) => void
}
@@ -31,6 +32,7 @@ export function WorktreeCardIssueDetailSection({
issueMenuOpen,
onIssueMenuOpenChange,
onCopyIssueLink,
+ onOpenIssueInBrowser,
onEditIssue,
onOpenGitHubIssueInOrca
}: WorktreeCardIssueDetailSectionProps): React.JSX.Element | null {
@@ -71,7 +73,7 @@ export function WorktreeCardIssueDetailSection({
)}
actions={
<>
- {issue.url && onCopyIssueLink && (
+ {issue.url && (onCopyIssueLink || onOpenIssueInBrowser) && (
{issueMenuOpen ? (
moreActionsTrigger
@@ -84,10 +86,25 @@ export function WorktreeCardIssueDetailSection({
)}
-
-
- {translate('auto.components.sidebar.WorktreeCardMeta.copyLink', 'Copy link')}
-
+ {onOpenIssueInBrowser && (
+ {
+ onOpenIssueInBrowser(issue.url!)
+ }}
+ >
+
+ {translate(
+ 'auto.components.sidebar.WorktreeCardMeta.openInOrcaBrowser',
+ 'Open in Orca browser'
+ )}
+
+ )}
+ {onCopyIssueLink && (
+
+
+ {translate('auto.components.sidebar.WorktreeCardMeta.copyLink', 'Copy link')}
+
+ )}
)}
diff --git a/src/renderer/src/components/sidebar/WorktreeCardMeta.interaction.test.tsx b/src/renderer/src/components/sidebar/WorktreeCardMeta.interaction.test.tsx
index 2b2cabb5947..d172df1bac3 100644
--- a/src/renderer/src/components/sidebar/WorktreeCardMeta.interaction.test.tsx
+++ b/src/renderer/src/components/sidebar/WorktreeCardMeta.interaction.test.tsx
@@ -116,7 +116,10 @@ describe('WorktreeCardDetailsHover interactions', () => {
writeClipboardText.mockResolvedValue(undefined)
})
- function renderHover(onUnlinkReview = vi.fn()): ReturnType {
+ function renderHover(
+ onUnlinkReview = vi.fn(),
+ onOpenReviewInBrowser?: () => void
+ ): ReturnType {
container = document.createElement('div')
root = createRoot(container)
act(() => {
@@ -130,6 +133,7 @@ describe('WorktreeCardDetailsHover interactions', () => {
onEditComment={vi.fn()}
onOpenReviewInOrca={vi.fn()}
onUnlinkReview={onUnlinkReview}
+ onOpenReviewInBrowser={onOpenReviewInBrowser}
>
Linked PR
@@ -288,6 +292,49 @@ describe('WorktreeCardDetailsHover interactions', () => {
).toBe('false')
})
+ it('opens the review URL in Orca browser and leaves existing actions independent', () => {
+ const onOpenReviewInBrowser = vi.fn()
+ const onUnlinkReview = renderHover(vi.fn(), onOpenReviewInBrowser)
+
+ act(() => {
+ interactionMocks.onHoverOpenChange?.(true)
+ interactionMocks.onReviewMenuOpenChange?.(true)
+ })
+
+ const browserButton = Array.from(container.querySelectorAll('button')).find((button) =>
+ button.textContent?.includes('Open in Orca browser')
+ )
+
+ act(() => {
+ browserButton?.dispatchEvent(new MouseEvent('click', { bubbles: true }))
+ })
+
+ expect(onOpenReviewInBrowser).toHaveBeenCalledWith('https://github.com/acme/orca/pull/456')
+ expect(onUnlinkReview).not.toHaveBeenCalled()
+ expect(container.querySelector('[data-hover-open]')?.getAttribute('data-hover-open')).toBe(
+ 'false'
+ )
+ })
+
+ it('preserves repeated-click behavior by forwarding each browser action', () => {
+ const onOpenReviewInBrowser = vi.fn()
+ renderHover(vi.fn(), onOpenReviewInBrowser)
+
+ act(() => {
+ interactionMocks.onReviewMenuOpenChange?.(true)
+ })
+ const browserButton = Array.from(container.querySelectorAll('button')).find((button) =>
+ button.textContent?.includes('Open in Orca browser')
+ )
+
+ act(() => {
+ browserButton?.dispatchEvent(new MouseEvent('click', { bubbles: true }))
+ browserButton?.dispatchEvent(new MouseEvent('click', { bubbles: true }))
+ })
+
+ expect(onOpenReviewInBrowser).toHaveBeenCalledTimes(2)
+ })
+
it('reports clipboard failures without unlinking the review', async () => {
writeClipboardText.mockRejectedValueOnce(new Error('clipboard unavailable'))
const onUnlinkReview = renderHover()
@@ -310,4 +357,43 @@ describe('WorktreeCardDetailsHover interactions', () => {
expect(onUnlinkReview).not.toHaveBeenCalled()
expect(toastMocks.error).toHaveBeenCalledWith('Failed to copy link')
})
+
+ it('passes a linked issue URL to the embedded-browser action', () => {
+ const onOpenIssueInBrowser = vi.fn()
+ container = document.createElement('div')
+ root = createRoot(container)
+ act(() => {
+ root.render(
+
+ Linked issue
+
+ )
+ })
+
+ act(() => {
+ interactionMocks.onHoverOpenChange?.(true)
+ interactionMocks.onReviewMenuOpenChange?.(true)
+ })
+ const browserButton = Array.from(container.querySelectorAll('button')).find((button) =>
+ button.textContent?.includes('Open in Orca browser')
+ )
+
+ act(() => {
+ browserButton?.dispatchEvent(new MouseEvent('click', { bubbles: true }))
+ })
+
+ expect(onOpenIssueInBrowser).toHaveBeenCalledWith('https://github.com/acme/orca/issues/5518')
+ })
})
diff --git a/src/renderer/src/components/sidebar/WorktreeCardMeta.test.tsx b/src/renderer/src/components/sidebar/WorktreeCardMeta.test.tsx
index 1e207973911..aa816fdd05d 100644
--- a/src/renderer/src/components/sidebar/WorktreeCardMeta.test.tsx
+++ b/src/renderer/src/components/sidebar/WorktreeCardMeta.test.tsx
@@ -130,6 +130,7 @@ describe('WorktreeCardDetailsHover', () => {
onEditIssue={vi.fn()}
onEditComment={vi.fn()}
onOpenReviewInOrca={vi.fn()}
+ onOpenReviewInBrowser={vi.fn()}
onUnlinkReview={vi.fn()}
>
Linked PR
@@ -143,10 +144,12 @@ describe('WorktreeCardDetailsHover', () => {
expect(moreActionsIndex).toBeGreaterThan(-1)
expect(markup).toContain('More PR actions')
expect(markup).toContain('Copy link')
+ expect(markup).toContain('Open in Orca browser')
expect(markup).toContain('Unlink PR')
expect(moreActionsIndex).toBeLessThan(openInOrcaIndex)
expect(openInOrcaIndex).toBeLessThan(viewOnGitHubIndex)
expect(markup).not.toContain('aria-label="Unlink PR"')
+ expect(markup.indexOf('Open in Orca browser')).toBeLessThan(markup.indexOf('Copy link'))
expect(markup.indexOf('Copy link')).toBeLessThan(markup.indexOf('Unlink PR'))
})
@@ -166,6 +169,7 @@ describe('WorktreeCardDetailsHover', () => {
onEditIssue={vi.fn()}
onEditComment={vi.fn()}
onOpenGitHubIssueInOrca={vi.fn()}
+ onOpenIssueInBrowser={vi.fn()}
>
Linked issue
@@ -179,6 +183,8 @@ describe('WorktreeCardDetailsHover', () => {
expect(moreActionsIndex).toBeGreaterThan(-1)
expect(copyLinkIndex).toBeGreaterThan(-1)
+ expect(markup).toContain('Open in Orca browser')
+ expect(markup.indexOf('Open in Orca browser')).toBeLessThan(copyLinkIndex)
expect(editIssueIndex).toBeGreaterThan(-1)
expect(moreActionsIndex).toBeLessThan(editIssueIndex)
expect(copyLinkIndex).toBeLessThan(editIssueIndex)
@@ -203,6 +209,7 @@ describe('WorktreeCardDetailsHover', () => {
onEditIssue={vi.fn()}
onEditComment={vi.fn()}
onUnlinkReview={vi.fn()}
+ onOpenReviewInBrowser={vi.fn()}
>
Linked MR
@@ -211,6 +218,53 @@ describe('WorktreeCardDetailsHover', () => {
expect(markup).toContain('aria-label="More MR actions"')
expect(markup).toContain('Unlink MR')
expect(markup).toContain('View on GitLab')
+ expect(markup).toContain('Open in Orca browser')
+ })
+
+ it('hides the embedded-browser action when a linked review has no URL', () => {
+ const markup = renderToStaticMarkup(
+
+ Linked PR
+
+ )
+
+ expect(markup).not.toContain('Open in Orca browser')
+ })
+
+ it('keeps the embedded-browser action provider-neutral for unsupported review URLs', () => {
+ const markup = renderToStaticMarkup(
+
+ Linked review
+
+ )
+
+ expect(markup).toContain('Open in Orca browser')
})
it('displays Linear issue details with link', () => {
diff --git a/src/renderer/src/components/sidebar/WorktreeCardMeta.tsx b/src/renderer/src/components/sidebar/WorktreeCardMeta.tsx
index 071d857aed2..3c288f6eb94 100644
--- a/src/renderer/src/components/sidebar/WorktreeCardMeta.tsx
+++ b/src/renderer/src/components/sidebar/WorktreeCardMeta.tsx
@@ -70,8 +70,10 @@ export function WorktreeCardDetailsHover({
onEditIssue,
onEditComment,
onOpenGitHubIssueInOrca,
+ onOpenIssueInBrowser,
onOpenLinearIssueInOrca,
onOpenReviewInOrca,
+ onOpenReviewInBrowser,
onUnlinkReview,
onOpenAutomation,
onOpenAutomationRun,
@@ -212,6 +214,14 @@ export function WorktreeCardDetailsHover({
onOpenGitHubIssueInOrca={
onOpenGitHubIssueInOrca ? dismissAndRun(onOpenGitHubIssueInOrca) : undefined
}
+ onOpenIssueInBrowser={
+ onOpenIssueInBrowser && issue?.url
+ ? (url: string) => {
+ closeHover()
+ onOpenIssueInBrowser(url)
+ }
+ : undefined
+ }
/>
{linearIssue && (
@@ -305,6 +315,9 @@ export function WorktreeCardDetailsHover({
reviewMenuOpen={reviewMenuOpen}
onReviewMenuOpenChange={handleReviewMenuOpenChange}
onOpenReviewInOrca={onOpenReviewInOrca}
+ onOpenReviewInBrowser={
+ onOpenReviewInBrowser && review?.url ? onOpenReviewInBrowser : undefined
+ }
onCopyReviewLink={review?.url ? handleCopyReviewLink : undefined}
onUnlinkReview={onUnlinkReview}
closeHover={closeHover}
diff --git a/src/renderer/src/components/sidebar/WorktreeCardReviewDetailSection.tsx b/src/renderer/src/components/sidebar/WorktreeCardReviewDetailSection.tsx
index 257ccd70c70..59edfde10bd 100644
--- a/src/renderer/src/components/sidebar/WorktreeCardReviewDetailSection.tsx
+++ b/src/renderer/src/components/sidebar/WorktreeCardReviewDetailSection.tsx
@@ -7,7 +7,7 @@ import {
DropdownMenuTrigger
} from '@/components/ui/dropdown-menu'
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip'
-import { Copy, Ellipsis, ExternalLink, MonitorUp, Unlink } from 'lucide-react'
+import { Copy, Ellipsis, ExternalLink, Globe, MonitorUp, Unlink } from 'lucide-react'
import { translate } from '@/i18n/i18n'
import {
WorktreeCardDetailSection,
@@ -24,6 +24,7 @@ type WorktreeCardReviewDetailSectionProps = {
onReviewMenuOpenChange: (open: boolean) => void
onOpenReviewInOrca?: (event: React.MouseEvent) => void
onCopyReviewLink?: () => void
+ onOpenReviewInBrowser?: (url: string) => void
onUnlinkReview?: () => void
closeHover: () => void
}
@@ -34,6 +35,7 @@ export function WorktreeCardReviewDetailSection({
onReviewMenuOpenChange,
onOpenReviewInOrca,
onCopyReviewLink,
+ onOpenReviewInBrowser,
onUnlinkReview,
closeHover
}: WorktreeCardReviewDetailSectionProps): React.JSX.Element | null {
@@ -78,7 +80,7 @@ export function WorktreeCardReviewDetailSection({
)}
actions={
<>
- {(onCopyReviewLink || onUnlinkReview) && (
+ {(onCopyReviewLink || onOpenReviewInBrowser || onUnlinkReview) && (
)}
+ {onOpenReviewInBrowser && (
+ {
+ closeHover()
+ onOpenReviewInBrowser(review.url!)
+ }}
+ >
+
+ {translate(
+ 'auto.components.sidebar.WorktreeCardMeta.openInOrcaBrowser',
+ 'Open in Orca browser'
+ )}
+
+ )}
{onCopyReviewLink && (
{
diff --git a/src/renderer/src/components/sidebar/use-worktree-card-secondary-details.ts b/src/renderer/src/components/sidebar/use-worktree-card-secondary-details.ts
index e5ffe899c8d..111f1fa95f6 100644
--- a/src/renderer/src/components/sidebar/use-worktree-card-secondary-details.ts
+++ b/src/renderer/src/components/sidebar/use-worktree-card-secondary-details.ts
@@ -1,6 +1,9 @@
import React, { useCallback } from 'react'
+import { toast } from 'sonner'
import type { GitHubWorkItem } from '../../../../shared/github/work-item-types'
+import { translate } from '@/i18n/i18n'
+import { openWorkspaceBrowserTab } from '@/lib/workspace-browser-tab-open'
import { hasWorktreeCardDetails } from './WorktreeCardMeta'
import { usePromptCacheCountdownStartedAt } from './CacheTimer'
import { useWorktreeAgentRows } from './useWorktreeAgentRows'
@@ -141,6 +144,34 @@ export function useWorktreeCardSecondaryDetails({
},
[hoverReview, openTaskPage, repo]
)
+ const openLinkedUrlInBrowser = useCallback(
+ (url: string): void => {
+ void openWorkspaceBrowserTab({
+ workspaceId: worktree.id,
+ url,
+ intent: { kind: 'url' }
+ }).catch((error: unknown) => {
+ toast.error(
+ error instanceof Error
+ ? error.message
+ : translate('auto.lib.workspace.browser.tab.open.urlFailed', 'Unable to open URL.')
+ )
+ })
+ },
+ [worktree.id]
+ )
+ const handleOpenIssueInBrowser = useCallback(
+ (url: string): void => {
+ openLinkedUrlInBrowser(url)
+ },
+ [openLinkedUrlInBrowser]
+ )
+ const handleOpenReviewInBrowser = useCallback(
+ (url: string): void => {
+ openLinkedUrlInBrowser(url)
+ },
+ [openLinkedUrlInBrowser]
+ )
const hoverReviewProvider = hoverReview?.provider
const hasExplicitLinkedReview =
(hoverReviewProvider === 'github' && worktree.linkedPR !== null) ||
@@ -214,7 +245,9 @@ export function useWorktreeCardSecondaryDetails({
showInlineAgentList,
compactInlineAgentRows,
handleOpenGitHubIssueInOrca,
+ handleOpenIssueInBrowser,
handleOpenReviewInOrca,
+ handleOpenReviewInBrowser,
hasExplicitLinkedReview,
handleUnlinkReview,
handleOpenLinearIssueInOrca,
diff --git a/src/renderer/src/components/sidebar/worktree-card-meta-types.ts b/src/renderer/src/components/sidebar/worktree-card-meta-types.ts
index 54637f7a50a..c35ad3c9fa5 100644
--- a/src/renderer/src/components/sidebar/worktree-card-meta-types.ts
+++ b/src/renderer/src/components/sidebar/worktree-card-meta-types.ts
@@ -57,8 +57,10 @@ export type WorktreeCardDetailsHoverProps = WorktreeCardMetaBadgesProps & {
onEditIssue?: (event: React.MouseEvent) => void
onEditComment?: (event: React.MouseEvent) => void
onOpenGitHubIssueInOrca?: (event: React.MouseEvent) => void
+ onOpenIssueInBrowser?: (url: string) => void
onOpenLinearIssueInOrca?: (event: React.MouseEvent) => void
onOpenReviewInOrca?: (event: React.MouseEvent) => void
+ onOpenReviewInBrowser?: (url: string) => void
onUnlinkReview?: () => void
onOpenAutomation?: (event: React.MouseEvent) => void
onOpenAutomationRun?: (event: React.MouseEvent) => void
diff --git a/src/renderer/src/components/sidebar/worktree-card-parent-content.tsx b/src/renderer/src/components/sidebar/worktree-card-parent-content.tsx
index 4cf122b0519..147e6615a9d 100644
--- a/src/renderer/src/components/sidebar/worktree-card-parent-content.tsx
+++ b/src/renderer/src/components/sidebar/worktree-card-parent-content.tsx
@@ -44,9 +44,11 @@ export function WorktreeCardParentContent({
handleEditIssue,
handleEditComment,
handleOpenGitHubIssueInOrca,
+ handleOpenIssueInBrowser,
linearIssue,
handleOpenLinearIssueInOrca,
handleOpenReviewInOrca,
+ handleOpenReviewInBrowser,
handleOpenAutomation,
handleOpenAutomationRun,
hasExplicitLinkedReview,
@@ -98,10 +100,14 @@ export function WorktreeCardParentContent({
? handleOpenGitHubIssueInOrca
: undefined
}
+ onOpenIssueInBrowser={
+ hoverIssue && 'url' in hoverIssue && hoverIssue.url ? handleOpenIssueInBrowser : undefined
+ }
onOpenLinearIssueInOrca={linearIssue?.url ? handleOpenLinearIssueInOrca : undefined}
onOpenReviewInOrca={
hoverReview?.url && hoverReview.provider === 'github' ? handleOpenReviewInOrca : undefined
}
+ onOpenReviewInBrowser={hoverReview?.url ? handleOpenReviewInBrowser : undefined}
onOpenAutomation={affiliateListMode ? undefined : handleOpenAutomation}
onOpenAutomationRun={affiliateListMode ? undefined : handleOpenAutomationRun}
onUnlinkReview={
diff --git a/src/renderer/src/components/sidebar/worktree-card-presentation.tsx b/src/renderer/src/components/sidebar/worktree-card-presentation.tsx
index de5c72f43b2..a5b708a9046 100644
--- a/src/renderer/src/components/sidebar/worktree-card-presentation.tsx
+++ b/src/renderer/src/components/sidebar/worktree-card-presentation.tsx
@@ -57,8 +57,10 @@ export function buildWorktreeCardPresentation(card: WorktreeCardController) {
handleEditIssue,
handleEditComment,
handleOpenGitHubIssueInOrca,
+ handleOpenIssueInBrowser,
handleOpenLinearIssueInOrca,
handleOpenReviewInOrca,
+ handleOpenReviewInBrowser,
handleOpenAutomation,
handleOpenAutomationRun,
hasExplicitLinkedReview,
@@ -167,12 +169,18 @@ export function buildWorktreeCardPresentation(card: WorktreeCardController) {
? handleOpenGitHubIssueInOrca
: undefined
}
+ onOpenIssueInBrowser={
+ metaIssue && 'url' in metaIssue && metaIssue.url
+ ? handleOpenIssueInBrowser
+ : undefined
+ }
onOpenLinearIssueInOrca={linearIssue?.url ? handleOpenLinearIssueInOrca : undefined}
onOpenReviewInOrca={
metaReview?.url && metaReview.provider === 'github'
? handleOpenReviewInOrca
: undefined
}
+ onOpenReviewInBrowser={metaReview?.url ? handleOpenReviewInBrowser : undefined}
onOpenAutomation={affiliateListMode ? undefined : handleOpenAutomation}
onOpenAutomationRun={affiliateListMode ? undefined : handleOpenAutomationRun}
// Why: compact mode hides the metadata badge row, so title hover carries the explicit-link affordance.
@@ -231,10 +239,14 @@ export function buildWorktreeCardPresentation(card: WorktreeCardController) {
onOpenGitHubIssueInOrca={
metaIssue && 'url' in metaIssue && metaIssue.url ? handleOpenGitHubIssueInOrca : undefined
}
+ onOpenIssueInBrowser={
+ metaIssue && 'url' in metaIssue && metaIssue.url ? handleOpenIssueInBrowser : undefined
+ }
onOpenLinearIssueInOrca={linearIssue?.url ? handleOpenLinearIssueInOrca : undefined}
onOpenReviewInOrca={
metaReview?.url && metaReview.provider === 'github' ? handleOpenReviewInOrca : undefined
}
+ onOpenReviewInBrowser={metaReview?.url ? handleOpenReviewInBrowser : undefined}
onOpenAutomation={affiliateListMode ? undefined : handleOpenAutomation}
onOpenAutomationRun={affiliateListMode ? undefined : handleOpenAutomationRun}
// Why: branch lookup can surface a review without persisted metadata; only unlink when explicitly linked.
diff --git a/src/renderer/src/i18n/locales/en.json b/src/renderer/src/i18n/locales/en.json
index 31b048c7d1c..042adb66254 100644
--- a/src/renderer/src/i18n/locales/en.json
+++ b/src/renderer/src/i18n/locales/en.json
@@ -5415,6 +5415,7 @@
"eace1d2cf6": "neutral",
"dbe2d18972": "More {{value0}} actions",
"ae76907ca6": "Unlink {{value0}}",
+ "openInOrcaBrowser": "Open in Orca browser",
"ad25c3ff05": "View on {{value0}}",
"2c67730e07": "Open in Orca",
"e42941631a": "View on Linear",
diff --git a/src/renderer/src/lib/workspace-browser-tab-open.test.ts b/src/renderer/src/lib/workspace-browser-tab-open.test.ts
index 307156f40f0..59ce6bac8d8 100644
--- a/src/renderer/src/lib/workspace-browser-tab-open.test.ts
+++ b/src/renderer/src/lib/workspace-browser-tab-open.test.ts
@@ -104,6 +104,31 @@ describe('openWorkspaceBrowserTab', () => {
expect(mocks.createRemote).not.toHaveBeenCalled()
})
+ it('creates a client tab without activating it when requested', async () => {
+ const createBrowserTab = vi.fn()
+ const sshHost = toSshExecutionHostId('ssh-target')
+ mocks.state = {
+ ...ownerState(sshHost),
+ createBrowserTab,
+ defaultBrowserSessionProfileId: 'focused-profile',
+ defaultBrowserSessionProfileIdByHostId: { [sshHost]: 'ssh-profile' }
+ }
+
+ await openWorkspaceBrowserTab({
+ workspaceId: WORKSPACE_ID,
+ url: 'https://github.com/acme/orca/pull/456',
+ intent: { kind: 'url' },
+ focusOnCreate: false,
+ selectWorktree: false
+ })
+
+ expect(createBrowserTab).toHaveBeenCalledWith(
+ WORKSPACE_ID,
+ 'https://github.com/acme/orca/pull/456',
+ expect.objectContaining({ activate: false })
+ )
+ })
+
it('fails closed when the asserted SSH browser route is opted out or belongs to another host', async () => {
const sshHost = toSshExecutionHostId('ssh-target')
mocks.state = {
@@ -164,6 +189,32 @@ describe('openWorkspaceBrowserTab', () => {
expect(createBrowserTab).not.toHaveBeenCalled()
})
+ it('stages a runtime tab without selecting the worktree or focusing the browser', async () => {
+ mocks.state = {
+ ...ownerState(toRuntimeExecutionHostId('hub-a')),
+ ...browserCapableRuntime('hub-a'),
+ createBrowserTab: vi.fn(),
+ defaultBrowserSessionProfileId: 'client-profile',
+ defaultBrowserSessionProfileIdByHostId: {}
+ }
+
+ await openWorkspaceBrowserTab({
+ workspaceId: WORKSPACE_ID,
+ url: 'https://gitlab.com/acme/orca/-/merge_requests/77',
+ intent: { kind: 'url' },
+ focusOnCreate: false,
+ selectWorktree: false
+ })
+
+ expect(mocks.createRemote).toHaveBeenCalledWith(
+ expect.objectContaining({
+ focusOnCreate: false,
+ selectWorktree: false,
+ url: 'https://gitlab.com/acme/orca/-/merge_requests/77'
+ })
+ )
+ })
+
it('waits for host registration before reconciling an asserted runtime link', async () => {
const createBrowserTab = vi.fn()
mocks.state = {
diff --git a/src/renderer/src/lib/workspace-browser-tab-open.ts b/src/renderer/src/lib/workspace-browser-tab-open.ts
index 9fed023ac4d..d159d47aab3 100644
--- a/src/renderer/src/lib/workspace-browser-tab-open.ts
+++ b/src/renderer/src/lib/workspace-browser-tab-open.ts
@@ -25,6 +25,10 @@ export type OpenWorkspaceBrowserTabRequest = {
targetGroupId?: string
url: string
intent: WorkspaceBrowserTabIntent
+ /** Keep the caller's current terminal/task surface selected while creating the tab. */
+ focusOnCreate?: boolean
+ /** Keep the caller's current workspace selected while creating the tab. */
+ selectWorktree?: boolean
expectedRuntimeEnvironmentId?: string
expectedSshConnectionId?: string
}
@@ -180,7 +184,7 @@ function createClientBrowserTab(
): void {
try {
state.createBrowserTab(request.workspaceId, request.url, {
- activate: true,
+ activate: request.focusOnCreate !== false,
browserRuntimeEnvironmentId: null,
focusAddressBar: false,
sessionProfileId:
@@ -284,7 +288,8 @@ export async function openWorkspaceBrowserTab(
...(expectedEnvironmentId !== null ? { waitForRegistration: true } : {}),
// Why: the tab is opened from this workspace's tab bar, so surface that
// workspace — otherwise a background worktree looks like nothing happened.
- selectWorktree: true,
+ ...(request.focusOnCreate !== undefined ? { focusOnCreate: request.focusOnCreate } : {}),
+ selectWorktree: request.selectWorktree !== false,
stagedTitle: presentation.title,
stagedFocusAddressBar: false,
failureLogMode: 'operation-only'