fix(ios): preserve sidebar navigation after leaving terminal search

This commit is contained in:
Kaede Akino committed 2026-10-06 23:29:01 +08:00
1 parent 2502b76cd6
commit 6a84ab04b4
3 files changed
+45 -6

No files matched your search

@@ -2,11 +2,12 @@ import SwiftUI
struct MobileToolbarLayout: Equatable {
var isTopBar = false
var isSidebar = false
var verticalEdge: HorizontalEdge?
var isVertical: Bool { verticalEdge != nil }
var usesIconSwitcher: Bool { isTopBar || isVertical }
var showsWorkspaceTabs: Bool { isTopBar || isVertical }
var showsWorkspaceTabs: Bool { isTopBar || isSidebar || isVertical }
}
extension EnvironmentValues {
@@ -19,11 +20,12 @@ private struct MobileToolbarEnvironment: ViewModifier {
func body(content: Content) -> some View {
if #available(iOS 27.1, *) {
content.modifier(MobileVerticalToolbarEnvironment(isTopBar: placement == .topBar))
content.modifier(
MobileVerticalToolbarEnvironment(isTopBar: placement == .topBar, isSidebar: placement == .sidebar))
} else {
content.environment(
\.mobileToolbarLayout,
MobileToolbarLayout(isTopBar: placement == .topBar))
MobileToolbarLayout(isTopBar: placement == .topBar, isSidebar: placement == .sidebar))
}
}
}
@@ -31,12 +33,13 @@ private struct MobileToolbarEnvironment: ViewModifier {
@available(iOS 27.1, *)
private struct MobileVerticalToolbarEnvironment: ViewModifier {
let isTopBar: Bool
let isSidebar: Bool
@Environment(\.toolbarVerticalEdge) private var edge
func body(content: Content) -> some View {
content.environment(
\.mobileToolbarLayout,
MobileToolbarLayout(isTopBar: isTopBar, verticalEdge: edge))
MobileToolbarLayout(isTopBar: isTopBar, isSidebar: isSidebar, verticalEdge: edge))
}
}
@@ -145,7 +145,8 @@ struct MobileWorkspaceView: View {
}
.navigationTitle(workspace.descriptor.title)
.navigationBarTitleDisplayMode(.inline)
.toolbar(toolbarLayout.showsWorkspaceTabs ? .visible : .hidden, for: .tabBar)
// Retained, inactive workspaces must not hide another destination's navigation when folding.
.toolbar(workspaceIsVisible && !toolbarLayout.showsWorkspaceTabs ? .hidden : .visible, for: .tabBar)
.toolbar(removing: toolbarLayout.usesIconSwitcher ? .title : nil)
.toolbar {
if toolbarLayout.usesIconSwitcher {
+36 -1
View File
@@ -9,6 +9,40 @@ import UIKit
@Suite(.serialized, .requiresKeyWindow)
@MainActor
struct MobileTerminalSearchIntegrationTests {
@Test func expandingVaultAfterWorkspaceSearchKeepsSidebarAvailable() async throws {
let fixture = try SearchFixture(kind: .ssh, fullRoot: true)
defer { fixture.close() }
let scene = try #require(fixture.scene)
fixture.resize(width: 430, height: 900, sizeClass: .compact)
try await fixture.settle()
let tabs = try #require(fixture.tabController)
let search = try #require(fixture.searchController)
search.isActive = true
try await fixture.settle()
scene.selectedDestination = .vault
try await fixture.settle()
fixture.resize(width: 1024, height: 900, sizeClass: .regular)
if #available(iOS 27.0, *) { tabs.sidebar.preferredPlacement = .sidebar }
tabs.sidebar.isHidden = false
try await fixture.settle()
if #available(iOS 27.0, *) { #expect(tabs.sidebar.isAvailable) }
#expect(!search.isActive)
#expect(!tabs.sidebar.isHidden)
#expect(!tabs.isTabBarHidden)
#expect(scene.selectedDestination == .vault)
// The retained workspace must also keep navigation available in sidebar mode.
scene.selectedDestination = .workspaces
try await fixture.settle()
#expect(!tabs.sidebar.isHidden)
#expect(!tabs.isTabBarHidden)
scene.selectedDestination = .vault
try await fixture.settle()
#expect(!tabs.sidebar.isHidden)
#expect(!tabs.isTabBarHidden)
}
@Test(arguments: [ConnectionHistoryEntry.Kind.ssh, .sftp])
func leavingWorkspaceSearchRestoresVaultNavigationAcrossResizing(kind: ConnectionHistoryEntry.Kind) async throws {
let fixture = try SearchFixture(kind: kind, fullRoot: true)
@@ -278,8 +312,9 @@ private final class SearchFixture {
func settle() async throws { try await Task.sleep(for: .milliseconds(700)) }
func resize(width: CGFloat, height: CGFloat) {
func resize(width: CGFloat, height: CGFloat, sizeClass: UIUserInterfaceSizeClass? = nil) {
let frame = CGRect(x: 0, y: 0, width: width, height: height)
if let sizeClass { host.traitOverrides.horizontalSizeClass = sizeClass }
window.frame = frame
host.view.frame = frame
host.view.setNeedsLayout()