From 39075537c79dd56f28148ca574df455360c1e007 Mon Sep 17 00:00:00 2001 From: Mohammad Al-Ahdal Date: Fri, 10 Oct 2025 00:51:22 -0600 Subject: [PATCH] added borderless setting --- .../Browser/Window/WindowView.swift | 12 +++--- Nook/Components/Settings/SettingsView.swift | 16 ++++++++ .../WebsiteView/EmptyWebsiteView.swift | 3 ++ Nook/Components/WebsiteView/WebsiteView.swift | 5 ++- .../SettingsManager/SettingsManager.swift | 39 ++++++++++++------- 5 files changed, 52 insertions(+), 23 deletions(-) diff --git a/Nook/Components/Browser/Window/WindowView.swift b/Nook/Components/Browser/Window/WindowView.swift index ee9ae944..ada3c657 100644 --- a/Nook/Components/Browser/Window/WindowView.swift +++ b/Nook/Components/Browser/Window/WindowView.swift @@ -30,7 +30,7 @@ struct WindowView: View { // Gradient background for the current space (bottom-most layer) SpaceGradientBackgroundView() .environmentObject(windowState) - + // Attach background context menu to the window background layer Color.white.opacity(isDark ? 0.3 : 0.4) .ignoresSafeArea(.all) @@ -49,10 +49,8 @@ struct WindowView: View { .environmentObject(browserManager) .environmentObject(windowState) .background(Color.clear) - mainLayout } - // TopBar Command Palette overlay TopBarCommandPalette() .environmentObject(browserManager) @@ -94,7 +92,7 @@ struct WindowView: View { } } - + // Toast overlays (matches WebsitePopup style/presentation) VStack { HStack { @@ -219,8 +217,8 @@ struct WindowView: View { sidebarColumn } } - .padding(.trailing, windowState.isFullScreen ? 0 : (windowState.isSidebarVisible && browserManager.settingsManager.sidebarPosition == .right ? 0 : aiVisible ? 0 : 8)) - .padding(.leading, windowState.isFullScreen ? 0 : (windowState.isSidebarVisible && browserManager.settingsManager.sidebarPosition == .left ? 0 : aiVisible ? 0 : 8)) + .padding(.trailing, (windowState.isFullScreen || browserManager.settingsManager.borderless) ? 0 : (windowState.isSidebarVisible && browserManager.settingsManager.sidebarPosition == .right ? 0 : aiVisible ? 0 : 8)) + .padding(.leading, (windowState.isFullScreen || browserManager.settingsManager.borderless) ? 0 : (windowState.isSidebarVisible && browserManager.settingsManager.sidebarPosition == .left ? 0 : aiVisible ? 0 : 8)) } private var sidebarColumn: some View { @@ -230,7 +228,7 @@ struct WindowView: View { if windowState.isSidebarVisible { // Position to span 14pts into sidebar and 2pts into web content (moved 6pts left) SidebarResizeView() - + .frame(maxHeight: .infinity) .environmentObject(browserManager) .environmentObject(windowState) diff --git a/Nook/Components/Settings/SettingsView.swift b/Nook/Components/Settings/SettingsView.swift index 78b3acf0..e0d59bd0 100644 --- a/Nook/Components/Settings/SettingsView.swift +++ b/Nook/Components/Settings/SettingsView.swift @@ -196,6 +196,22 @@ struct GeneralSettingsView: View { .foregroundStyle(.secondary) } } + + Divider().opacity(0.4) + + Toggle( + isOn: $browserManager.settingsManager + .borderless + ) { + VStack(alignment: .leading, spacing: 2) { + Text("Borderless") + Text( + "Remove the window border for a cleaner look" + ) + .font(.caption) + .foregroundStyle(.secondary) + } + } } SettingsSectionCard( diff --git a/Nook/Components/WebsiteView/EmptyWebsiteView.swift b/Nook/Components/WebsiteView/EmptyWebsiteView.swift index 695a717b..f461a123 100644 --- a/Nook/Components/WebsiteView/EmptyWebsiteView.swift +++ b/Nook/Components/WebsiteView/EmptyWebsiteView.swift @@ -21,6 +21,9 @@ struct EmptyWebsiteView: View { if windowState.isFullScreen { return 0 } + if browserManager.settingsManager.borderless { + return 0 + } if #available(macOS 26.0, *) { return 12 } else { diff --git a/Nook/Components/WebsiteView/WebsiteView.swift b/Nook/Components/WebsiteView/WebsiteView.swift index e8b8eaf7..c8e8abce 100644 --- a/Nook/Components/WebsiteView/WebsiteView.swift +++ b/Nook/Components/WebsiteView/WebsiteView.swift @@ -13,7 +13,7 @@ import AppKit struct LinkStatusBar: View { let hoveredLink: String? let isCommandPressed: Bool - + var body: some View { if let link = hoveredLink, !link.isEmpty { Text(isCommandPressed ? "Open \(link) in a new tab and focus it" : link) @@ -73,6 +73,9 @@ struct WebsiteView: View { if windowState.isFullScreen { return 0 } + if browserManager.settingsManager.borderless { + return 0 + } if #available(macOS 26.0, *) { return 12 } else { diff --git a/Nook/Managers/SettingsManager/SettingsManager.swift b/Nook/Managers/SettingsManager/SettingsManager.swift index 13b31889..fe38bb29 100644 --- a/Nook/Managers/SettingsManager/SettingsManager.swift +++ b/Nook/Managers/SettingsManager/SettingsManager.swift @@ -34,6 +34,7 @@ class SettingsManager { private let webSearchEngineKey = "settings.webSearchEngine" private let webSearchMaxResultsKey = "settings.webSearchMaxResults" private let webSearchContextSizeKey = "settings.webSearchContextSize" + private let borderlessKey = "settings.borderless" var currentSettingsTab: SettingsTabs = .general // Stored properties @@ -62,7 +63,7 @@ class SettingsManager { userDefaults.set(searchEngine.rawValue, forKey: searchEngineKey) } } - + var tabUnloadTimeout: TimeInterval { didSet { userDefaults.set(tabUnloadTimeout, forKey: tabUnloadTimeoutKey) @@ -77,19 +78,19 @@ class SettingsManager { NotificationCenter.default.post(name: .blockCrossSiteTrackingChanged, object: nil, userInfo: ["enabled": blockCrossSiteTracking]) } } - + var askBeforeQuit: Bool { didSet { userDefaults.set(askBeforeQuit, forKey: askBeforeQuitKey) } } - + var sidebarPosition: SidebarPosition { didSet { userDefaults.set(sidebarPosition.rawValue, forKey: sidebarPositionKey) } } - + var topBarAddressView: Bool { didSet { userDefaults.set(topBarAddressView, forKey: topBarAddressViewKey) @@ -126,6 +127,12 @@ class SettingsManager { } } + var borderless: Bool { + didSet { + userDefaults.set(borderless, forKey: borderlessKey) + } + } + var aiProvider: AIProvider { didSet { userDefaults.set(aiProvider.rawValue, forKey: aiProviderKey) @@ -205,7 +212,8 @@ class SettingsManager { webSearchEnabledKey: false, webSearchEngineKey: "auto", webSearchMaxResultsKey: 5, - webSearchContextSizeKey: "medium" + webSearchContextSizeKey: "medium", + borderlessKey: false ]) // Initialize properties from UserDefaults @@ -221,7 +229,7 @@ class SettingsManager { // Fallback to google if the stored value is somehow invalid self.searchEngine = .google } - + // Initialize tab unload timeout self.tabUnloadTimeout = userDefaults.double(forKey: tabUnloadTimeoutKey) self.blockCrossSiteTracking = userDefaults.bool(forKey: blockXSTKey) @@ -242,6 +250,7 @@ class SettingsManager { self.webSearchEngine = userDefaults.string(forKey: webSearchEngineKey) ?? "auto" self.webSearchMaxResults = userDefaults.integer(forKey: webSearchMaxResultsKey) self.webSearchContextSize = userDefaults.string(forKey: webSearchContextSizeKey) ?? "medium" + self.borderless = userDefaults.bool(forKey: borderlessKey) } } @@ -251,9 +260,9 @@ public enum AIProvider: String, CaseIterable, Identifiable { case gemini = "gemini" case openRouter = "openrouter" case ollama = "ollama" - + public var id: String { rawValue } - + var displayName: String { switch self { case .gemini: return "Google Gemini" @@ -261,7 +270,7 @@ public enum AIProvider: String, CaseIterable, Identifiable { case .ollama: return "Ollama (Local)" } } - + var isRecommended: Bool { return false } @@ -272,23 +281,23 @@ public enum AIProvider: String, CaseIterable, Identifiable { public enum GeminiModel: String, CaseIterable, Identifiable { case flash = "gemini-flash-latest" case pro = "gemini-2.5-pro" - + public var id: String { rawValue } - + var displayName: String { switch self { case .flash: return "Gemini Flash" case .pro: return "Gemini 2.5 Pro" } } - + var description: String { switch self { case .flash: return "Fast responses, great for quick questions" case .pro: return "Most capable model, best for complex analysis" } } - + var icon: String { switch self { case .flash: return "bolt.fill" @@ -311,9 +320,9 @@ public enum OpenRouterModel: String, CaseIterable, Identifiable { case gpt5mini = "openai/gpt-5-mini" case gpt5 = "openai/gpt-5" - + public var id: String { rawValue } - + var displayName: String { switch self { case .deepseekChatV31: return "DeepSeek Chat V3.1 (Free)"