mirror of
https://github.com/stablyai/orca.git
synced 2026-09-27 16:02:35 +00:00
* fix(browser): scope back/forward/reload/zoom/grab shortcuts to the originating split With two browser panes visible in a split, Back, Forward, Reload, Hard Reload, page zoom and Focus Address Bar fired in every visible pane. Main forwarded these guest chords without the page id, and each split's active pane subscribed. The renderer-side listeners for the same chords were also window-wide per pane, so a key pressed in the toolbar (or in a terminal in another split) reached every active browser pane. Guest-forwarded chords now carry the originating browserPageId; preload admits only well-formed payloads and each pane ignores ids that aren't its own. Toolbar-path listeners use the same focused-split scope Find already uses. The streamed remote pane's history chord moves onto that scoped hook. Cmd/Ctrl+C grab (STA-3319) gets the same scope and no longer arms while a text selection exists outside the browser pane, so copying from the native chat transcript works again. * refactor(browser): simplify split shortcut scoping per review Drop the preload payload admission (main and preload ship together), fold the three inline scope checks into browserChromeShortcutOwnsEvent, and replace the outside-overlay selection check with a plain live-selection rule so Cmd+C copies from surfaces that do not move split focus. * refactor(browser): share one zoom command type and tidy shortcut comments BrowserPageZoomEventDetail and BrowserPageZoomCommand were the same shape; keep one in shared/browser-page-zoom.ts and route guest and local zoom through a single handler. * refactor(browser): narrow the zoom event with instanceof instead of a cast * test(e2e): pin split-scoped browser shortcuts Two browser splits (and a terminal beside a browser) now prove that Back, Forward, Reload, Hard Reload, page zoom, Focus Address Bar, and the element grab chord act only on the split that sent them, from both the guest page and the browser toolbar. A native chat selection proves Cmd/Ctrl+C copies instead of arming grab. Split fixtures move to a shared helper so both specs reuse them.
173 lines
6.1 KiB
TypeScript
173 lines
6.1 KiB
TypeScript
import { expect, test } from './helpers/orca-app'
|
|
import type { Page } from '@stablyai/playwright-test'
|
|
import { focusActiveTerminalInput } from './helpers/terminal'
|
|
import { ensureTerminalVisible, waitForActiveWorktree, waitForSessionReady } from './helpers/store'
|
|
import {
|
|
browserAddressBar,
|
|
createBrowserSplit,
|
|
createTerminalBrowserSplit,
|
|
focusBrowserAddressBar,
|
|
focusBrowserGroup,
|
|
guestModifier,
|
|
pressKeyInBrowserGuest,
|
|
shortcutModifier as modifier,
|
|
waitForFocusedGroup
|
|
} from './helpers/browser-split-fixture'
|
|
|
|
function browserFindInput(page: Page) {
|
|
return page.getByPlaceholder('Find in page...')
|
|
}
|
|
|
|
function browserFindCloseButton(page: Page) {
|
|
return browserFindInput(page).locator('xpath=..').getByTitle('Close')
|
|
}
|
|
|
|
function browserSplitFindInput(page: Page, browserTabId: string) {
|
|
return page
|
|
.locator(`[data-browser-overlay-tab-id="${browserTabId}"]`)
|
|
.getByPlaceholder('Find in page...')
|
|
}
|
|
|
|
async function pressFindInBrowserGuest(
|
|
page: Page,
|
|
browserTabId: string,
|
|
browserPageId: string
|
|
): Promise<void> {
|
|
await pressKeyInBrowserGuest(page, browserTabId, browserPageId, 'F', [guestModifier])
|
|
}
|
|
|
|
function terminalFindInput(page: Page) {
|
|
return page.locator('[data-terminal-search-root] input:visible')
|
|
}
|
|
|
|
test.describe('browser split shortcuts', () => {
|
|
test.beforeEach(async ({ orcaPage }) => {
|
|
await waitForSessionReady(orcaPage)
|
|
await waitForActiveWorktree(orcaPage)
|
|
await ensureTerminalVisible(orcaPage)
|
|
})
|
|
|
|
test('routes repeated Find shortcuts to the focused terminal or browser split', async ({
|
|
orcaPage
|
|
}) => {
|
|
const fixture = await createTerminalBrowserSplit(orcaPage)
|
|
|
|
await orcaPage.evaluate(({ terminalGroupId }) => {
|
|
const state = window.__store?.getState()
|
|
const worktreeId = state?.activeWorktreeId
|
|
if (state && worktreeId) {
|
|
state.focusGroup(worktreeId, terminalGroupId)
|
|
}
|
|
}, fixture)
|
|
await focusActiveTerminalInput(orcaPage)
|
|
await waitForFocusedGroup(orcaPage, fixture.terminalGroupId)
|
|
await orcaPage.keyboard.press(`${modifier}+f`)
|
|
await expect(terminalFindInput(orcaPage)).toBeFocused()
|
|
await expect(browserFindInput(orcaPage)).toBeHidden()
|
|
await orcaPage.keyboard.press('Escape')
|
|
|
|
await focusBrowserGroup(orcaPage, fixture.browserGroupId)
|
|
await focusBrowserAddressBar(orcaPage, fixture.browserTabId)
|
|
await orcaPage.keyboard.press(`${modifier}+f`)
|
|
await expect(browserFindInput(orcaPage)).toBeFocused()
|
|
await expect(terminalFindInput(orcaPage)).toBeHidden()
|
|
await browserFindCloseButton(orcaPage).click()
|
|
await expect(browserFindInput(orcaPage)).toBeHidden()
|
|
|
|
await orcaPage.keyboard.press(`${modifier}+f`)
|
|
await expect(browserFindInput(orcaPage)).toBeFocused()
|
|
await browserFindCloseButton(orcaPage).click()
|
|
|
|
await orcaPage.evaluate(({ browserTabId }) => {
|
|
window.__store?.getState().closeBrowserTab(browserTabId)
|
|
}, fixture)
|
|
await expect(
|
|
orcaPage.locator(`[data-browser-overlay-tab-id="${fixture.browserTabId}"]`)
|
|
).toHaveCount(0)
|
|
|
|
await focusActiveTerminalInput(orcaPage)
|
|
await orcaPage.keyboard.press(`${modifier}+f`)
|
|
await expect(terminalFindInput(orcaPage)).toBeFocused()
|
|
await expect(browserFindInput(orcaPage)).toBeHidden()
|
|
})
|
|
|
|
test('opens Find only in the browser split whose guest owns the shortcut', async ({
|
|
orcaPage
|
|
}) => {
|
|
const fixture = await createBrowserSplit(orcaPage)
|
|
|
|
await pressFindInBrowserGuest(orcaPage, fixture.firstBrowserTabId, fixture.firstBrowserPageId)
|
|
|
|
await expect(browserSplitFindInput(orcaPage, fixture.firstBrowserTabId)).toBeVisible()
|
|
await expect(browserSplitFindInput(orcaPage, fixture.secondBrowserTabId)).toBeHidden()
|
|
await expect
|
|
.poll(() =>
|
|
orcaPage.evaluate(
|
|
({ browserPageId, browserTabId }) =>
|
|
window.__store
|
|
?.getState()
|
|
.browserPagesByWorkspace[browserTabId]?.find((page) => page.id === browserPageId)
|
|
?.loadError?.code ?? null,
|
|
{
|
|
browserPageId: fixture.firstBrowserPageId,
|
|
browserTabId: fixture.firstBrowserTabId
|
|
}
|
|
)
|
|
)
|
|
.toBeNull()
|
|
})
|
|
|
|
test('keeps browser Find available when split focus state is temporarily missing', async ({
|
|
orcaPage
|
|
}) => {
|
|
const fixture = await createTerminalBrowserSplit(orcaPage)
|
|
await focusBrowserGroup(orcaPage, fixture.browserGroupId)
|
|
const addressBar = browserAddressBar(orcaPage, fixture.browserTabId)
|
|
await focusBrowserAddressBar(orcaPage, fixture.browserTabId)
|
|
|
|
await orcaPage.evaluate(() => {
|
|
const store = window.__store
|
|
const worktreeId = store?.getState().activeWorktreeId
|
|
if (!store || !worktreeId) {
|
|
throw new Error('Active worktree unavailable')
|
|
}
|
|
store.setState((state) => {
|
|
const activeGroupIdByWorktree = { ...state.activeGroupIdByWorktree }
|
|
delete activeGroupIdByWorktree[worktreeId]
|
|
return { activeGroupIdByWorktree }
|
|
})
|
|
})
|
|
await expect(addressBar).toBeFocused()
|
|
|
|
await orcaPage.keyboard.press(`${modifier}+f`)
|
|
await expect(browserFindInput(orcaPage)).toBeFocused()
|
|
await expect(terminalFindInput(orcaPage)).toBeHidden()
|
|
})
|
|
|
|
test('keeps browser Find available when the focused split ID is stale', async ({ orcaPage }) => {
|
|
const fixture = await createTerminalBrowserSplit(orcaPage)
|
|
await focusBrowserGroup(orcaPage, fixture.browserGroupId)
|
|
const addressBar = browserAddressBar(orcaPage, fixture.browserTabId)
|
|
await focusBrowserAddressBar(orcaPage, fixture.browserTabId)
|
|
|
|
await orcaPage.evaluate(() => {
|
|
const store = window.__store
|
|
const worktreeId = store?.getState().activeWorktreeId
|
|
if (!store || !worktreeId) {
|
|
throw new Error('Active worktree unavailable')
|
|
}
|
|
store.setState((state) => ({
|
|
activeGroupIdByWorktree: {
|
|
...state.activeGroupIdByWorktree,
|
|
[worktreeId]: 'removed-group'
|
|
}
|
|
}))
|
|
})
|
|
await expect(addressBar).toBeFocused()
|
|
|
|
await orcaPage.keyboard.press(`${modifier}+f`)
|
|
await expect(browserFindInput(orcaPage)).toBeFocused()
|
|
await expect(terminalFindInput(orcaPage)).toBeHidden()
|
|
})
|
|
})
|