fix(settings): keep the sidebar visible instead of a full-window overlay

Settings now swaps into the same NavigationSplitView detail pane as
Home/collections (gated on AppNavigation.isShowingSettings) instead of
covering the whole root window — the sidebar, and the ability to just
click something else in it to leave Settings, stays available. Any
sidebar navigation (Home, a collection, a document) exits Settings.
This commit is contained in:
2026-08-15 00:17:51 +01:00
parent 4769645489
commit 4f423ef971
2 changed files with 19 additions and 13 deletions
@@ -4,6 +4,7 @@ import OutlineKit
struct ContentView_macOS: View {
@Environment(SessionStore.self) private var session
@Environment(AppNavigation.self) private var navigation
/// The landing state no collection selected yet is what Home actually
/// means, so this starts `true` rather than auto-selecting the first
/// collection the way this used to work.
@@ -75,7 +76,7 @@ struct ContentView_macOS: View {
// `CollectionOverviewView`, so on Home it was a dead end: a
// user could click it, type, and nothing would happen. The
// sidebar's global search already covers "search everything."
if !(isShowingHome && documentPath.isEmpty) {
if !navigation.isShowingSettings && !(isShowingHome && documentPath.isEmpty) {
ToolbarItem(placement: .primaryAction) {
contextualSearchField
}
@@ -96,6 +97,7 @@ struct ContentView_macOS: View {
isContextualSearchExpanded = false
selectedCollection = nil
isShowingHome = true
navigation.isShowingSettings = false
replaceDocumentPath(with: [])
}
@@ -144,7 +146,12 @@ struct ContentView_macOS: View {
@ViewBuilder
private var leadingToolbarContent: some View {
Group {
if !trimmedGlobalQuery.isEmpty {
if navigation.isShowingSettings {
HStack(spacing: 6) {
Image(systemName: "gearshape.fill")
Text("Settings")
}
} else if !trimmedGlobalQuery.isEmpty {
HStack(spacing: 6) {
Image(systemName: "magnifyingglass")
Text("Search")
@@ -223,6 +230,7 @@ struct ContentView_macOS: View {
globalSearchQuery = ""
selectedCollection = collection
isContextualSearchExpanded = true
navigation.isShowingSettings = false
Task { @MainActor in
isContextualSearchFocused = true
}
@@ -233,11 +241,13 @@ struct ContentView_macOS: View {
/// hierarchy immediately rather than just the leaf.
private func selectDocumentChain(_ collection: OutlineCollection, _ chain: [OutlineDocument]) {
selectedCollection = collection
navigation.isShowingSettings = false
replaceDocumentPath(with: chain)
}
/// Flat-list / search-result clicks: no known ancestors, single-level push.
private func openDocument(_ document: OutlineDocument) {
navigation.isShowingSettings = false
replaceDocumentPath(with: [document])
}
@@ -256,7 +266,13 @@ struct ContentView_macOS: View {
@ViewBuilder
private var detail: some View {
if let apiClient = session.apiClient {
if navigation.isShowingSettings {
// Deliberately not a `NavigationStack` destination or a separate
// window it's its own page swapped into the same detail pane
// Home/collections use, so the sidebar (and the ability to just
// click something else in it) stays available while it's open.
SettingsView(onDone: { navigation.isShowingSettings = false })
} else if let apiClient = session.apiClient {
// A fresh NavigationStack per collection (or when entering/leaving
// search), so switching either also clears any pushed document.
NavigationStack(path: $documentPath) {
-10
View File
@@ -3,7 +3,6 @@ import OutlineKit
struct RootView: View {
@Environment(SessionStore.self) private var session
@Environment(AppNavigation.self) private var navigation
@State private var welcomeName: String?
@State private var starStore = StarStore()
@AppStorage("outpost.fullLocalSyncEnabled") private var isFullLocalSyncEnabled = false
@@ -21,18 +20,9 @@ struct RootView: View {
.transition(.opacity)
.zIndex(1)
}
#if os(macOS)
if navigation.isShowingSettings {
SettingsView(onDone: { navigation.isShowingSettings = false })
.transition(.opacity)
.zIndex(2)
}
#endif
}
.environment(starStore)
.animation(.easeInOut(duration: 0.45), value: welcomeName != nil)
.animation(.easeInOut(duration: 0.2), value: navigation.isShowingSettings)
.task {
await session.refreshTeamInfoIfNeeded()
}