diff --git a/src/renderer/src/hooks/useIpcEvents.ts b/src/renderer/src/hooks/useIpcEvents.ts index 83590b654..60ea13278 100644 --- a/src/renderer/src/hooks/useIpcEvents.ts +++ b/src/renderer/src/hooks/useIpcEvents.ts @@ -236,7 +236,20 @@ export function useIpcEvents(): void { window.api.ui.replyTabCreate({ requestId: data.requestId, error: 'No active worktree' }) return } - const workspace = store.createBrowserTab(worktreeId, data.url, { title: data.url }) + // Why: CLI-created tabs should land in the same group as the active + // browser tab, not the terminal's group (which is typically the + // UI-active group when an agent is running commands). + const activeBrowserTabId = store.activeBrowserTabIdByWorktree[worktreeId] + const activeBrowserUnifiedTab = activeBrowserTabId + ? (store.unifiedTabsByWorktree[worktreeId] ?? []).find( + (t) => t.contentType === 'browser' && t.entityId === activeBrowserTabId + ) + : undefined + + const workspace = store.createBrowserTab(worktreeId, data.url, { + title: data.url, + targetGroupId: activeBrowserUnifiedTab?.groupId + }) // Why: registerGuest fires with the page ID (not workspace ID) as // browserPageId. Return the page ID so waitForTabRegistration can // correlate correctly. diff --git a/src/renderer/src/store/slices/browser.test.ts b/src/renderer/src/store/slices/browser.test.ts index 3d94a2577..ca90d0c30 100644 --- a/src/renderer/src/store/slices/browser.test.ts +++ b/src/renderer/src/store/slices/browser.test.ts @@ -2,6 +2,69 @@ import { describe, expect, it } from 'vitest' import { createTestStore, makeTabGroup, makeWorktree, seedStore } from './store-test-helpers' describe('browser slice', () => { + it('places a new tab in the target group when targetGroupId is provided', () => { + const store = createTestStore() + const worktreeId = 'repo1::/tmp/wt-1' + seedStore(store, { + activeRepoId: 'repo1', + activeWorktreeId: worktreeId, + activeTabType: 'terminal', + worktreesByRepo: { + repo1: [makeWorktree({ id: worktreeId, repoId: 'repo1', path: '/tmp/wt-1' })] + }, + groupsByWorktree: { + [worktreeId]: [ + makeTabGroup({ id: 'terminal-group', worktreeId, activeTabId: null, tabOrder: [] }), + makeTabGroup({ id: 'browser-group', worktreeId, activeTabId: null, tabOrder: [] }) + ] + }, + activeGroupIdByWorktree: { [worktreeId]: 'terminal-group' }, + browserTabsByWorktree: {}, + unifiedTabsByWorktree: {} + }) + + const created = store.getState().createBrowserTab(worktreeId, 'https://example.com', { + title: 'Example', + targetGroupId: 'browser-group' + }) + + const unifiedTab = (store.getState().unifiedTabsByWorktree[worktreeId] ?? []).find( + (t) => t.contentType === 'browser' && t.entityId === created.id + ) + expect(unifiedTab?.groupId).toBe('browser-group') + }) + + it('falls back to active group when targetGroupId is not provided', () => { + const store = createTestStore() + const worktreeId = 'repo1::/tmp/wt-1' + seedStore(store, { + activeRepoId: 'repo1', + activeWorktreeId: worktreeId, + activeTabType: 'terminal', + worktreesByRepo: { + repo1: [makeWorktree({ id: worktreeId, repoId: 'repo1', path: '/tmp/wt-1' })] + }, + groupsByWorktree: { + [worktreeId]: [ + makeTabGroup({ id: 'terminal-group', worktreeId, activeTabId: null, tabOrder: [] }), + makeTabGroup({ id: 'browser-group', worktreeId, activeTabId: null, tabOrder: [] }) + ] + }, + activeGroupIdByWorktree: { [worktreeId]: 'terminal-group' }, + browserTabsByWorktree: {}, + unifiedTabsByWorktree: {} + }) + + const created = store.getState().createBrowserTab(worktreeId, 'https://example.com', { + title: 'Example' + }) + + const unifiedTab = (store.getState().unifiedTabsByWorktree[worktreeId] ?? []).find( + (t) => t.contentType === 'browser' && t.entityId === created.id + ) + expect(unifiedTab?.groupId).toBe('terminal-group') + }) + it('reopens the most recently closed browser tab in the same worktree', () => { const store = createTestStore() const worktreeId = 'repo1::/tmp/wt-1' diff --git a/src/renderer/src/store/slices/browser.ts b/src/renderer/src/store/slices/browser.ts index 6cca2307c..6af9a72a0 100644 --- a/src/renderer/src/store/slices/browser.ts +++ b/src/renderer/src/store/slices/browser.ts @@ -18,6 +18,7 @@ type CreateBrowserTabOptions = { activate?: boolean title?: string sessionProfileId?: string | null + targetGroupId?: string } type CreateBrowserPageOptions = { @@ -395,7 +396,8 @@ export const createBrowserSlice: StateCreator = if (!alreadyHasUnifiedTab) { state.createUnifiedTab(worktreeId, 'browser', { entityId: workspaceId, - label: browserTab.title + label: browserTab.title, + targetGroupId: options?.targetGroupId }) } return browserTab