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 { struct ContentView_macOS: View {
@Environment(SessionStore.self) private var session @Environment(SessionStore.self) private var session
@Environment(AppNavigation.self) private var navigation
/// The landing state no collection selected yet is what Home actually /// The landing state no collection selected yet is what Home actually
/// means, so this starts `true` rather than auto-selecting the first /// means, so this starts `true` rather than auto-selecting the first
/// collection the way this used to work. /// 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 // `CollectionOverviewView`, so on Home it was a dead end: a
// user could click it, type, and nothing would happen. The // user could click it, type, and nothing would happen. The
// sidebar's global search already covers "search everything." // sidebar's global search already covers "search everything."
if !(isShowingHome && documentPath.isEmpty) { if !navigation.isShowingSettings && !(isShowingHome && documentPath.isEmpty) {
ToolbarItem(placement: .primaryAction) { ToolbarItem(placement: .primaryAction) {
contextualSearchField contextualSearchField
} }
@@ -96,6 +97,7 @@ struct ContentView_macOS: View {
isContextualSearchExpanded = false isContextualSearchExpanded = false
selectedCollection = nil selectedCollection = nil
isShowingHome = true isShowingHome = true
navigation.isShowingSettings = false
replaceDocumentPath(with: []) replaceDocumentPath(with: [])
} }
@@ -144,7 +146,12 @@ struct ContentView_macOS: View {
@ViewBuilder @ViewBuilder
private var leadingToolbarContent: some View { private var leadingToolbarContent: some View {
Group { Group {
if !trimmedGlobalQuery.isEmpty { if navigation.isShowingSettings {
HStack(spacing: 6) {
Image(systemName: "gearshape.fill")
Text("Settings")
}
} else if !trimmedGlobalQuery.isEmpty {
HStack(spacing: 6) { HStack(spacing: 6) {
Image(systemName: "magnifyingglass") Image(systemName: "magnifyingglass")
Text("Search") Text("Search")
@@ -223,6 +230,7 @@ struct ContentView_macOS: View {
globalSearchQuery = "" globalSearchQuery = ""
selectedCollection = collection selectedCollection = collection
isContextualSearchExpanded = true isContextualSearchExpanded = true
navigation.isShowingSettings = false
Task { @MainActor in Task { @MainActor in
isContextualSearchFocused = true isContextualSearchFocused = true
} }
@@ -233,11 +241,13 @@ struct ContentView_macOS: View {
/// hierarchy immediately rather than just the leaf. /// hierarchy immediately rather than just the leaf.
private func selectDocumentChain(_ collection: OutlineCollection, _ chain: [OutlineDocument]) { private func selectDocumentChain(_ collection: OutlineCollection, _ chain: [OutlineDocument]) {
selectedCollection = collection selectedCollection = collection
navigation.isShowingSettings = false
replaceDocumentPath(with: chain) replaceDocumentPath(with: chain)
} }
/// Flat-list / search-result clicks: no known ancestors, single-level push. /// Flat-list / search-result clicks: no known ancestors, single-level push.
private func openDocument(_ document: OutlineDocument) { private func openDocument(_ document: OutlineDocument) {
navigation.isShowingSettings = false
replaceDocumentPath(with: [document]) replaceDocumentPath(with: [document])
} }
@@ -256,7 +266,13 @@ struct ContentView_macOS: View {
@ViewBuilder @ViewBuilder
private var detail: some View { 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 // A fresh NavigationStack per collection (or when entering/leaving
// search), so switching either also clears any pushed document. // search), so switching either also clears any pushed document.
NavigationStack(path: $documentPath) { NavigationStack(path: $documentPath) {
-10
View File
@@ -3,7 +3,6 @@ import OutlineKit
struct RootView: View { struct RootView: View {
@Environment(SessionStore.self) private var session @Environment(SessionStore.self) private var session
@Environment(AppNavigation.self) private var navigation
@State private var welcomeName: String? @State private var welcomeName: String?
@State private var starStore = StarStore() @State private var starStore = StarStore()
@AppStorage("outpost.fullLocalSyncEnabled") private var isFullLocalSyncEnabled = false @AppStorage("outpost.fullLocalSyncEnabled") private var isFullLocalSyncEnabled = false
@@ -21,18 +20,9 @@ struct RootView: View {
.transition(.opacity) .transition(.opacity)
.zIndex(1) .zIndex(1)
} }
#if os(macOS)
if navigation.isShowingSettings {
SettingsView(onDone: { navigation.isShowingSettings = false })
.transition(.opacity)
.zIndex(2)
}
#endif
} }
.environment(starStore) .environment(starStore)
.animation(.easeInOut(duration: 0.45), value: welcomeName != nil) .animation(.easeInOut(duration: 0.45), value: welcomeName != nil)
.animation(.easeInOut(duration: 0.2), value: navigation.isShowingSettings)
.task { .task {
await session.refreshTeamInfoIfNeeded() await session.refreshTeamInfoIfNeeded()
} }