diff --git a/Termind/App/RootView.swift b/Termind/App/RootView.swift index bfa4a7e3..b0abab1a 100644 --- a/Termind/App/RootView.swift +++ b/Termind/App/RootView.swift @@ -23,7 +23,7 @@ struct RootView: View { NavigationSplitView(columnVisibility: sidebarVisibility) { ZStack { - SidebarMaterialBackground() + SidebarGlassBackground() .ignoresSafeArea() WindowSidebarView() } diff --git a/Termind/Design/DesignTokens.swift b/Termind/Design/DesignTokens.swift index 81761cd6..0055d971 100644 --- a/Termind/Design/DesignTokens.swift +++ b/Termind/Design/DesignTokens.swift @@ -423,27 +423,37 @@ private struct TermindGlassBar: ViewModifier { } } -/// The stable source-list surface shared by every mode of the left column. +/// The Liquid Glass surface shared by every mode of the left column. /// /// `NavigationSplitView` supplies the column geometry, but the custom AppKit /// lists inside it do not inherit SwiftUI's source-list background. Hosting one -/// visual-effect view behind the complete column keeps list rows and empty -/// space on the same native material, including Reduce Transparency behavior. -/// Window-local blending prevents a full-screen Space's wallpaper tint from -/// overwhelming the interaction surfaces drawn above it. -struct SidebarMaterialBackground: NSViewRepresentable { +/// system glass surface behind the complete column keeps list rows and empty +/// space on one coherent material, including behind the window titlebar. +struct SidebarGlassBackground: View { + @ViewBuilder + var body: some View { + if #available(macOS 26.0, *) { + Color.clear + .glassEffect(.regular, in: .rect) + } else { + LegacySidebarMaterialBackground() + } + } +} + +/// Native material fallback for systems before Liquid Glass. +struct LegacySidebarMaterialBackground: NSViewRepresentable { func makeNSView(context: Context) -> NSVisualEffectView { let view = NSVisualEffectView() - // `.sidebar` is deliberately quite opaque and gray. With floating glass - // controls above it that becomes a muddy slab; window material is the - // quieter translucent canvas and lets those controls carry the hierarchy. - view.material = .windowBackground - view.blendingMode = .withinWindow - view.state = .followsWindowActiveState + Self.configure(view) return view } func updateNSView(_ view: NSVisualEffectView, context: Context) { + Self.configure(view) + } + + static func configure(_ view: NSVisualEffectView) { view.material = .windowBackground view.blendingMode = .withinWindow view.state = .followsWindowActiveState diff --git a/TermindTests/WindowSidebarTests.swift b/TermindTests/WindowSidebarTests.swift index b33250fd..a48b401a 100644 --- a/TermindTests/WindowSidebarTests.swift +++ b/TermindTests/WindowSidebarTests.swift @@ -1,3 +1,4 @@ +import AppKit import Testing @testable import Termind @@ -5,6 +6,17 @@ import Testing @Suite @MainActor struct WindowSidebarTests { + @Test + func legacySidebarMaterialStaysWithinTheWindow() { + let view = NSVisualEffectView() + + LegacySidebarMaterialBackground.configure(view) + + #expect(view.material == .windowBackground) + #expect(view.blendingMode == .withinWindow) + #expect(view.state == .followsWindowActiveState) + } + @Test func allDestinationsExposeNavigatorAndThreeToolSidebars() { let window = WindowModel()