diff --git a/apps/desktop/src/main/browser-agent/session.ts b/apps/desktop/src/main/browser-agent/session.ts index 6f494b979f8..abbefea8537 100644 --- a/apps/desktop/src/main/browser-agent/session.ts +++ b/apps/desktop/src/main/browser-agent/session.ts @@ -2012,6 +2012,22 @@ export function reloadPage(contents: WebContents): void { contents.reload() } +/** + * Reloads past the HTTP cache, the browser's Shift-reload. A failed or hung + * page takes the plain reload's recovery instead: a load error retries the + * URL that failed, which need not be the committed page, and a hung renderer + * must be restarted, since reloading it in place waits on the hung page. + */ +function hardReloadPage(contents: WebContents): void { + const issue = tabForContents(contents)?.pageIssue + if (issue?.kind === 'load-error' || issue?.kind === 'unresponsive') { + reloadPage(contents) + return + } + prepareExplicitNavigation(contents) + contents.reloadIgnoringCache() +} + /** Hands one page selection to the exact app window and chat hosting its tab. */ function addPageSelectionToChat(contents: WebContents, text: string): void { if (!text.trim() || getBrowserScopeId() !== getActiveBrowserScopeId()) return @@ -2494,8 +2510,7 @@ function initializeTabView( return } if (shortcut === 'hard-reload') { - prepareExplicitNavigation(contents) - contents.reloadIgnoringCache() + hardReloadPage(contents) return } @@ -3512,8 +3527,7 @@ export function handleFocusedShortcut( reloadPage(shortcutTab.view.webContents) return true case 'hard-reload': - prepareExplicitNavigation(shortcutTab.view.webContents) - shortcutTab.view.webContents.reloadIgnoringCache() + hardReloadPage(shortcutTab.view.webContents) return true case 'back': goBack(shortcutTab.view.webContents) diff --git a/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/resource-content/components/browser-session/browser-find-bar.tsx b/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/resource-content/components/browser-session/browser-find-bar.tsx index 5827b5d2013..8a1f2bc6539 100644 --- a/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/resource-content/components/browser-session/browser-find-bar.tsx +++ b/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/resource-content/components/browser-session/browser-find-bar.tsx @@ -142,7 +142,7 @@ export function BrowserFindBar({ inputRef, onClose, scopeId }: BrowserFindBarPro // The panel and the global command layer both listen for these. event.stopPropagation() // Keys during an IME composition edit the composed text. - if (event.nativeEvent.isComposing) return + if (event.nativeEvent.isComposing || event.nativeEvent.keyCode === 229) return if (event.key === 'Escape') { event.preventDefault() dismiss() diff --git a/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/resource-content/components/browser-session/browser-page-issue.tsx b/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/resource-content/components/browser-session/browser-page-issue.tsx index e1a5bc9d603..d59a858afd0 100644 --- a/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/resource-content/components/browser-session/browser-page-issue.tsx +++ b/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/resource-content/components/browser-session/browser-page-issue.tsx @@ -125,17 +125,15 @@ export function browserPageIssueCopy(issue: BrowserPageIssue): BrowserPageIssueC } } -function isEditingOutside(heading: HTMLElement | null): boolean { +/** + * Whether nothing in Sim holds focus, as when it was in the native page that + * just failed. Any focused element, from the chat composer to the omnibox, + * keeps it. `document.activeElement` survives a window blur, so a caret left + * in chat still counts. + */ +function isFocusFree(): boolean { const active = document.activeElement - // activeElement survives a window blur, so a caret left in chat still counts. - if (!(active instanceof HTMLElement)) return false - const section = heading?.closest('section') - if (section?.contains(active)) return false - return ( - active.isContentEditable || - active instanceof HTMLInputElement || - active instanceof HTMLTextAreaElement - ) + return !active || active === document.body } /** Replaces a hidden native page and optionally claims renderer focus for keyboard recovery. */ @@ -144,9 +142,8 @@ export function BrowserPageIssueView({ issue, onReload, focusRecovery }: Browser const copy = browserPageIssueCopy(issue) useEffect(() => { - // Keyboard recovery for someone who was in the page; a caret in chat or - // any other Sim field stays where it is. - if (focusRecovery && !isEditingOutside(headingRef.current)) headingRef.current?.focus() + // Keyboard recovery for someone who was in the browser. + if (focusRecovery && isFocusFree()) headingRef.current?.focus() }, [focusRecovery, issue]) const Icon = issue.kind === 'load-error' ? Globe : CircleAlert diff --git a/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/resource-content/components/browser-session/browser-session.tsx b/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/resource-content/components/browser-session/browser-session.tsx index 94ce6a6d3a3..fa9fc4b5bd2 100644 --- a/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/resource-content/components/browser-session/browser-session.tsx +++ b/apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/resource-content/components/browser-session/browser-session.tsx @@ -1157,7 +1157,7 @@ export function BrowserSession({ onKeyDown={(event) => { event.stopPropagation() // Keys during an IME composition edit the composed text, not the URL. - if (event.nativeEvent.isComposing) return + if (event.nativeEvent.isComposing || event.nativeEvent.keyCode === 229) return if (event.key === 'ArrowDown' || event.key === 'ArrowUp') { // Never move a highlight through a list that is not on screen. if (!suggestionsOpen) return