This commit is contained in:
austinpower1258 2026-03-11 18:32:23 -07:00
parent 6849b83f8d
commit 5233874425
4 changed files with 233 additions and 10 deletions

View file

@ -2710,6 +2710,111 @@ final class BrowserPanel: Panel, ObservableObject {
}
}
extension BrowserPanel {
private var needsWorkspaceContextReset: Bool {
shouldRenderWebView ||
currentURL != nil ||
!pageTitle.isEmpty ||
faviconPNGData != nil ||
searchState != nil ||
nativeCanGoBack ||
nativeCanGoForward ||
restoredHistoryCurrentURL != nil ||
!restoredBackHistoryStack.isEmpty ||
!restoredForwardHistoryStack.isEmpty ||
estimatedProgress > 0 ||
isLoading ||
isDownloading ||
activeDownloadCount != 0 ||
preferredDeveloperToolsVisible ||
webView.superview != nil
}
func resetForWorkspaceContextChange(reason: String) {
guard needsWorkspaceContextReset else {
#if DEBUG
dlog(
"browser.contextReset.skip panel=\(id.uuidString.prefix(5)) " +
"reason=\(reason) render=\(shouldRenderWebView ? 1 : 0)"
)
#endif
return
}
#if DEBUG
dlog(
"browser.contextReset.begin panel=\(id.uuidString.prefix(5)) " +
"reason=\(reason) render=\(shouldRenderWebView ? 1 : 0) " +
"url=\(preferredURLStringForOmnibar() ?? "nil")"
)
#endif
_ = hideDeveloperTools()
cancelDeveloperToolsRestoreRetry()
preferredDeveloperToolsVisible = false
preferredDeveloperToolsPresentation = .unknown
forceDeveloperToolsRefreshOnNextAttach = false
developerToolsDetachedOpenGraceDeadline = nil
developerToolsRestoreRetryAttempt = 0
preferredAttachedDeveloperToolsWidth = nil
preferredAttachedDeveloperToolsWidthFraction = nil
loadingEndWorkItem?.cancel()
loadingEndWorkItem = nil
loadingGeneration &+= 1
activeDownloadCount = 0
isDownloading = false
isLoading = false
estimatedProgress = 0
nativeCanGoBack = false
nativeCanGoForward = false
navigationDelegate?.lastAttemptedURL = nil
abandonRestoredSessionHistoryIfNeeded()
pendingAddressBarFocusRequestId = nil
preferredFocusIntent = .addressBar
suppressOmnibarAutofocusUntil = nil
suppressWebViewFocusUntil = nil
endSuppressWebViewFocusForAddressBar()
invalidateAddressBarPageFocusRestoreAttempts()
invalidateSearchFocusRequests(reason: "contextReset")
searchState = nil
pageTitle = ""
currentURL = nil
faviconPNGData = nil
lastFaviconURLString = nil
activePortalHostLease = nil
pendingDistinctPortalHostReplacementPaneId = nil
lockedPortalHost = nil
let oldWebView = webView
webViewObservers.removeAll()
webViewCancellables.removeAll()
BrowserWindowPortalRegistry.detach(webView: oldWebView)
oldWebView.stopLoading()
oldWebView.navigationDelegate = nil
oldWebView.uiDelegate = nil
if let oldCmuxWebView = oldWebView as? CmuxWebView {
oldCmuxWebView.onContextMenuDownloadStateChanged = nil
}
let replacement = Self.makeWebView()
webView = replacement
webViewInstanceID = UUID()
shouldRenderWebView = false
bindWebView(replacement)
refreshNavigationAvailability()
#if DEBUG
dlog(
"browser.contextReset.end panel=\(id.uuidString.prefix(5)) " +
"reason=\(reason) instance=\(webViewInstanceID.uuidString.prefix(6))"
)
#endif
}
}
func resolveBrowserNavigableURL(_ input: String) -> URL? {
let trimmed = input.trimmingCharacters(in: .whitespacesAndNewlines)
guard !trimmed.isEmpty else { return nil }

View file

@ -13813,16 +13813,7 @@ class TerminalController {
result = "ERROR: Tab not found"
return
}
tab.statusEntries.removeAll()
tab.logEntries.removeAll()
tab.progress = nil
tab.gitBranch = nil
tab.panelGitBranches.removeAll()
tab.pullRequest = nil
tab.panelPullRequests.removeAll()
tab.surfaceListeningPorts.removeAll()
tab.listeningPorts.removeAll()
tab.metadataBlocks.removeAll()
tab.resetSidebarContext(reason: "reset_sidebar")
}
return result
}

View file

@ -1686,6 +1686,63 @@ final class Workspace: Identifiable, ObservableObject {
}
}
func resetSidebarContext(reason: String = "unspecified") {
statusEntries.removeAll()
logEntries.removeAll()
progress = nil
gitBranch = nil
panelGitBranches.removeAll()
pullRequest = nil
panelPullRequests.removeAll()
surfaceListeningPorts.removeAll()
listeningPorts.removeAll()
metadataBlocks.removeAll()
resetBrowserPanelsForContextChange(reason: reason)
}
func resetBrowserPanelsForContextChange(reason: String) {
let browserPanels = panels.values.compactMap { $0 as? BrowserPanel }
guard !browserPanels.isEmpty else { return }
#if DEBUG
dlog(
"workspace.contextReset.browserPanels workspace=\(id.uuidString.prefix(5)) " +
"reason=\(reason) count=\(browserPanels.count)"
)
#endif
for browserPanel in browserPanels {
browserPanel.resetForWorkspaceContextChange(reason: reason)
guard let tabId = surfaceIdFromPanelId(browserPanel.id),
let existing = bonsplitController.tab(tabId) else {
continue
}
let nextTitle = browserPanel.displayTitle
if panelTitles[browserPanel.id] != nextTitle {
panelTitles[browserPanel.id] = nextTitle
}
let resolvedTitle = resolvedPanelTitle(panelId: browserPanel.id, fallback: nextTitle)
let titleUpdate: String? = existing.title == resolvedTitle ? nil : resolvedTitle
let faviconUpdate: Data?? = existing.iconImageData == nil ? nil : .some(nil)
let loadingUpdate: Bool? = existing.isLoading ? false : nil
guard titleUpdate != nil || faviconUpdate != nil || loadingUpdate != nil else {
continue
}
bonsplitController.updateTab(
tabId,
title: titleUpdate,
iconImageData: faviconUpdate,
hasCustomTitle: panelCustomTitles[browserPanel.id] != nil,
isLoading: loadingUpdate
)
}
}
@discardableResult
func updatePanelTitle(panelId: UUID, title: String) -> Bool {
let trimmed = title.trimmingCharacters(in: .whitespacesAndNewlines)

View file

@ -2430,6 +2430,76 @@ final class BrowserSessionHistoryRestoreTests: XCTestCase {
XCTAssertFalse(panel.shouldRenderWebView)
}
func testResetSidebarContextClearsBrowserPanelsIntoNewTabState() throws {
let workspace = Workspace()
let paneId = try XCTUnwrap(workspace.bonsplitController.allPaneIds.first)
let contextPanelId = try XCTUnwrap(workspace.focusedPanelId)
let browser = try XCTUnwrap(
workspace.newBrowserSurface(
inPane: paneId,
url: URL(string: "https://example.com"),
focus: false
)
)
browser.restoreSessionNavigationHistory(
backHistoryURLStrings: ["https://example.com/prev"],
forwardHistoryURLStrings: ["https://example.com/next"],
currentURLString: "https://example.com/current"
)
browser.startFind()
workspace.statusEntries["task"] = SidebarStatusEntry(key: "task", value: "Issue #1208")
workspace.metadataBlocks["notes"] = SidebarMetadataBlock(
key: "notes",
markdown: "test",
priority: 0,
timestamp: Date()
)
workspace.progress = SidebarProgressState(value: 0.5, label: "Loading")
workspace.updatePanelGitBranch(panelId: contextPanelId, branch: "issue-1208", isDirty: false)
workspace.updatePanelPullRequest(
panelId: contextPanelId,
number: 1208,
label: "PR",
url: try XCTUnwrap(URL(string: "https://example.com/pull/1208")),
status: .open
)
workspace.surfaceListeningPorts[contextPanelId] = [3000]
workspace.recomputeListeningPorts()
XCTAssertTrue(browser.shouldRenderWebView)
XCTAssertNotNil(browser.preferredURLStringForOmnibar())
XCTAssertTrue(browser.canGoBack)
XCTAssertTrue(browser.canGoForward)
XCTAssertNotNil(browser.searchState)
XCTAssertFalse(workspace.statusEntries.isEmpty)
XCTAssertFalse(workspace.metadataBlocks.isEmpty)
XCTAssertNotNil(workspace.progress)
XCTAssertNotNil(workspace.gitBranch)
XCTAssertNotNil(workspace.pullRequest)
XCTAssertEqual(workspace.listeningPorts, [3000])
workspace.resetSidebarContext(reason: "test")
XCTAssertTrue(workspace.statusEntries.isEmpty)
XCTAssertTrue(workspace.logEntries.isEmpty)
XCTAssertTrue(workspace.metadataBlocks.isEmpty)
XCTAssertNil(workspace.progress)
XCTAssertNil(workspace.gitBranch)
XCTAssertTrue(workspace.panelGitBranches.isEmpty)
XCTAssertNil(workspace.pullRequest)
XCTAssertTrue(workspace.panelPullRequests.isEmpty)
XCTAssertTrue(workspace.surfaceListeningPorts.isEmpty)
XCTAssertTrue(workspace.listeningPorts.isEmpty)
XCTAssertFalse(browser.shouldRenderWebView)
XCTAssertNil(browser.preferredURLStringForOmnibar())
XCTAssertFalse(browser.canGoBack)
XCTAssertFalse(browser.canGoForward)
XCTAssertNil(browser.searchState)
}
}
@MainActor