nook-browser/Nook/main 812k tokens More Tools
```
├── .github/
   ├── workflows/
      ├── enforce-pr-base.yml (100 tokens)
      ├── macos-notarize.yml (1700 tokens)
├── .gitignore (500 tokens)
├── .gitmodules (100 tokens)
├── App/
   ├── AppDelegate.swift (3.1k tokens)
   ├── ContentView.swift (900 tokens)
   ├── NookApp.swift (2.3k tokens)
   ├── NookCommands.swift (3.1k tokens)
   ├── Window/
      ├── WindowView.swift (3.8k tokens)
├── CLAUDE.md (2.1k tokens)
├── CODE_OF_CONDUCT.md (400 tokens)
├── CONTRIBUTING.md (800 tokens)
├── CommandPalette/
   ├── CommandPalette Accessories/
      ├── CommandPaletteSuggestionView.swift (900 tokens)
      ├── GenericSuggestionItem.swift (200 tokens)
      ├── HistorySuggestionItem.swift (800 tokens)
      ├── TabSuggestionItem.swift (500 tokens)
   ├── CommandPalette.swift (300 tokens)
   ├── CommandPaletteView.swift (5.6k tokens)
├── LICENSE (omitted)
├── Navigation/
   ├── Sidebar/
      ├── SidebarBottomBar.swift (500 tokens)
      ├── SidebarHeader.swift (600 tokens)
      ├── SpaceContextMenu.swift (800 tokens)
      ├── SpacesList/
         ├── SpacesList.swift (1300 tokens)
         ├── SpacesListItem.swift (1600 tokens)
      ├── SpacesSideBarView.swift (3.1k tokens)
├── Nook.xcodeproj/
   ├── project.pbxproj (5.9k tokens)
   ├── project.xcworkspace/
      ├── contents.xcworkspacedata
      ├── xcshareddata/
         ├── WorkspaceSettings.xcsettings
         ├── swiftpm/
            ├── Package.resolved (1600 tokens)
   ├── xcshareddata/
      ├── xcschemes/
         ├── Nook.xcscheme (700 tokens)
├── Nook/
   ├── Assets.xcassets/
      ├── AccentColor.colorset/
         ├── Contents.json
      ├── Browser Logos/
         ├── Contents.json
         ├── arc-logo.imageset/
            ├── Contents.json (100 tokens)
            ├── Frame-2.png
         ├── chrome-logo.imageset/
            ├── Contents.json (100 tokens)
            ├── Frame-1.png
         ├── dia-logo.imageset/
            ├── Contents.json (100 tokens)
            ├── Frame 255.png
         ├── firefox-logo.imageset/
            ├── Contents.json (100 tokens)
            ├── Frame.png
         ├── safari-logo.imageset/
            ├── Contents.json (100 tokens)
            ├── safari-logo.png
         ├── zen-logo.imageset/
            ├── Contents.json (100 tokens)
            ├── zen-logo.png
      ├── Contents.json
      ├── adblocker-off.imageset/
         ├── Contents.json (100 tokens)
         ├── adblocker-off.png
         ├── adblocker-on.png
      ├── adblocker-on.imageset/
         ├── Contents.json (100 tokens)
         ├── adblocker-off.png
         ├── adblocker-on.png
      ├── ai-chat-off.imageset/
         ├── Contents.json (100 tokens)
         ├── ai-chat-off.png
      ├── ai-chat-on.imageset/
         ├── Contents.json (100 tokens)
         ├── ai-chat-on.png
      ├── github.fill.symbolset/
         ├── Contents.json
         ├── github.fill.svg (9.8k tokens)
      ├── noise_texture.imageset/
         ├── Contents.json (100 tokens)
         ├── noise_texture.png
         ├── noise_texture@2x.png
         ├── noise_texture@3x.png
      ├── nook-logo-1024.imageset/
         ├── Contents.json (100 tokens)
         ├── nook-logo-1024.png
      ├── opencollective-fill.symbolset/
         ├── Contents.json
         ├── opencollective.symbols.svg (900 tokens)
      ├── plainBackgroundColor.colorset/
         ├── Contents.json (100 tokens)
      ├── sidebar.imageset/
         ├── Contents.json (100 tokens)
         ├── sidebar.png
      ├── top-of-window.imageset/
         ├── Contents.json (100 tokens)
         ├── topofwindow.png
      ├── tulips.imageset/
         ├── Contents.json (100 tokens)
         ├── michael-loftus-aK4Slh-4uhU-unsplash.jpg
      ├── url-in-sidebar.imageset/
         ├── Contents.json (100 tokens)
         ├── url-in-sidebar.png
      ├── url-top-of-website.imageset/
         ├── Contents.json (100 tokens)
         ├── url-top-of-website.png
      ├── windowBackgroundColor.colorset/
         ├── Contents.json (100 tokens)
   ├── Components/
      ├── Boosts - deprecated/
         ├── BoostColorCanvas.swift (1300 tokens)
         ├── ColorWheelPicker.swift (1400 tokens)
      ├── Boosts/
         ├── BoostCodeButton.swift (300 tokens)
         ├── BoostColorPicker.swift (1700 tokens)
         ├── BoostFontOptions.swift (1100 tokens)
         ├── BoostFonts.swift (1100 tokens)
         ├── BoostHeader.swift (700 tokens)
         ├── BoostOptions.swift (1600 tokens)
         ├── BoostUI.swift (800 tokens)
         ├── BoostZapButton.swift (300 tokens)
         ├── CodeEditor/
            ├── BoostCodeView.swift (1300 tokens)
            ├── CodeEditor.swift (400 tokens)
            ├── CodeEditorFooter.swift (300 tokens)
            ├── CodeEditorHeader.swift (600 tokens)
      ├── Browser/
         ├── Window/
            ├── SpaceGradientBackgroundView.swift (200 tokens)
            ├── SplitCardView.swift (300 tokens)
            ├── SplitDropCaptureView.swift (1300 tokens)
            ├── TabCompositorView.swift (2.3k tokens)
      ├── ColorPicker/
         ├── AngleDial.swift (400 tokens)
         ├── ColorPickerView.swift (800 tokens)
         ├── ColorSwatchRowView.swift (1000 tokens)
         ├── GradientCanvasEditor.swift (5.7k tokens)
         ├── GradientEditorView.swift (700 tokens)
         ├── GradientNodePicker.swift (1200 tokens)
         ├── GradientPreview.swift (300 tokens)
         ├── GrainDial.swift (400 tokens)
         ├── GrainSlider.swift (600 tokens)
         ├── TransparencySlider.swift (800 tokens)
      ├── Dialog/
         ├── DialogView.swift (300 tokens)
      ├── DragDrop/
         ├── NookDragItem.swift (400 tokens)
         ├── NookDragPreviewWindow.swift (1900 tokens)
         ├── NookDragSessionManager.swift (2.7k tokens)
         ├── NookDragSourceView.swift (900 tokens)
         ├── NookDropZoneHostView.swift (900 tokens)
      ├── EmojiPicker/
         ├── EmojiPicker.swift (3k tokens)
      ├── Extensions/
         ├── ExtensionActionView.swift (1100 tokens)
         ├── ExtensionLibraryButton.swift (700 tokens)
         ├── ExtensionLibraryMoreMenu.swift (2.2k tokens)
         ├── ExtensionLibraryPanel.swift (1500 tokens)
         ├── ExtensionLibraryView.swift (3.7k tokens)
         ├── ExtensionPermissionView.swift (700 tokens)
         ├── PersistentPopover.swift (600 tokens)
         ├── PopupConsoleWindow.swift (1000 tokens)
      ├── FindBar/
         ├── FindBarView.swift (1500 tokens)
      ├── MiniWindow/
         ├── MiniWindowButtonStyle.swift (900 tokens)
         ├── MiniWindowToolbar.swift (2.4k tokens)
         ├── MiniWindowWebView.swift (4.2k tokens)
      ├── Navigation/
         ├── HoldGestureButton.swift (400 tokens)
         ├── NavigationHistoryContextMenu.swift (1200 tokens)
         ├── NavigationHistoryMenu.swift (1300 tokens)
         ├── NavigationHistoryOverlay.swift (500 tokens)
      ├── Peek/
         ├── PeekOverlayView.swift (2.6k tokens)
      ├── PulseTextField/
         ├── PulseTextField.swift (600 tokens)
      ├── Settings/
         ├── CacheDetailsView.swift (1800 tokens)
         ├── CacheManagementView.swift (2.9k tokens)
         ├── CookieDetailsView.swift (1400 tokens)
         ├── CookieManagementView.swift (2.6k tokens)
         ├── PrivacySettingsView.swift (2.4k tokens)
         ├── ProfilePickerView.swift (900 tokens)
         ├── ProfileRowView.swift (800 tokens)
         ├── SettingsTabBar.swift (100 tokens)
         ├── SettingsUtils.swift (500 tokens)
         ├── SettingsView.swift (8.5k tokens)
         ├── SettingsWindow.swift (700 tokens)
         ├── ShortcutRecorderView.swift (1200 tokens)
         ├── Tabs/
            ├── AI.swift (7.1k tokens)
            ├── AdBlocker.swift (1000 tokens)
            ├── AirTrafficControlSettingsView.swift (1800 tokens)
            ├── Appearance.swift (400 tokens)
            ├── General.swift (1400 tokens)
            ├── MemberCard.swift (500 tokens)
            ├── SponsorBlock.swift (600 tokens)
      ├── Sidebar/
         ├── AIChat/
            ├── AISidebarResizeView.swift (800 tokens)
            ├── SidebarAIChat.swift (6.2k tokens)
         ├── CopyURLToast/
            ├── CopyURLToast.swift (300 tokens)
         ├── FallbackDropBelowEssentialsModifier.swift (300 tokens)
         ├── MediaControls/
            ├── MediaControlsView.swift (2.4k tokens)
         ├── Menu/
            ├── DownloadIndicator.swift (400 tokens)
            ├── SidebarMenu.swift (700 tokens)
            ├── SidebarMenuDownloadsHover.swift (1800 tokens)
            ├── SidebarMenuDownloadsTab.swift (1700 tokens)
            ├── SidebarMenuHistoryTab.swift (5.2k tokens)
            ├── SidebarMenuTab.swift (300 tokens)
         ├── NavButtonsView.swift (2.1k tokens)
         ├── PinnedButtons/
            ├── EssentialTabsScrollView.swift (600 tokens)
            ├── EssentialsGridLayout.swift (100 tokens)
            ├── PinnedGrid.swift (3.2k tokens)
            ├── PinnedTabView.swift (1000 tokens)
            ├── PinnedUtils.swift (300 tokens)
         ├── SidebarResizeView.swift (900 tokens)
         ├── SpaceSection/
            ├── SpaceProfileBadge.swift (500 tokens)
            ├── SpaceProfileDropdown.swift (400 tokens)
            ├── SpaceSeparator.swift (700 tokens)
            ├── SpaceTab.swift (2.6k tokens)
            ├── SpaceTitle.swift (2.2k tokens)
            ├── SpaceView.swift (6.3k tokens)
            ├── SplitTabRow.swift (1100 tokens)
            ├── TabFolderView.swift (2.9k tokens)
         ├── TabClosureToast/
            ├── TabClosureToast.swift (300 tokens)
         ├── TopBar/
            ├── TopBarView.swift (4.1k tokens)
         ├── URLBarFramePreferenceKey.swift
         ├── URLBarView.swift (1800 tokens)
         ├── UpdateNotification/
            ├── SidebarUpdateNotification.swift (1000 tokens)
            ├── SidebarUpdateNotificationPreview.swift (600 tokens)
      ├── Toast/
         ├── ShortcutConflictToast.swift (600 tokens)
         ├── ToastView.swift (700 tokens)
      ├── WebsitePopup/
         ├── OAuthAssistBanner.swift (400 tokens)
      ├── WebsiteView/
         ├── EmptyWebsiteView.swift (300 tokens)
         ├── PageLoadingProgressBar.swift (800 tokens)
         ├── WebView.swift (2k tokens)
         ├── WebsiteLoadingIndicator.swift (300 tokens)
         ├── WebsiteView.swift (8.5k tokens)
      ├── Window/
         ├── DoubleClickView.swift (300 tokens)
         ├── MacButtons.swift (1100 tokens)
         ├── ToolExecutionGlowView.swift (400 tokens)
      ├── ZoomControls/
         ├── ZoomPopupView.swift (900 tokens)
   ├── Extensions/
      ├── NSUserInterfaceItemIdentifier+WebKit.swift (100 tokens)
      ├── View+GlassEffect.swift (100 tokens)
   ├── Info.plist (400 tokens)
   ├── Managers/
      ├── AIManager/
         ├── AIConfigService.swift (3k tokens)
         ├── AIProvider.swift (800 tokens)
         ├── AIService.swift (3.1k tokens)
         ├── MCP/
            ├── MCPClient.swift (1900 tokens)
            ├── MCPManager.swift (800 tokens)
            ├── MCPTransport.swift (2.3k tokens)
         ├── Providers/
            ├── GeminiProvider.swift (1600 tokens)
            ├── OllamaProvider.swift (1000 tokens)
            ├── OpenAICompatibleProvider.swift (1300 tokens)
            ├── OpenRouterProvider.swift (1500 tokens)
         ├── Tools/
            ├── BrowserToolExecutor.swift (4.6k tokens)
            ├── BrowserTools.swift (1200 tokens)
      ├── AuthenticationManager/
         ├── AuthenticationManager.swift (2000 tokens)
         ├── BasicAuthCredentialStore.swift (800 tokens)
      ├── BoostsManager/
         ├── BoostsManager.swift (4.9k tokens)
         ├── BoostsWindowManager.swift (4.1k tokens)
      ├── BrowserManager/
         ├── BrowserManager.swift (23k tokens)
      ├── CacheManager/
         ├── CacheManager.swift (2.9k tokens)
      ├── ContentBlockerManager/
         ├── AdvancedBlockingEngine.swift (6.3k tokens)
         ├── ContentBlockerManager.swift (3.3k tokens)
         ├── ContentRuleListCompiler.swift (2.6k tokens)
         ├── FilterListManager.swift (3.8k tokens)
         ├── Resources/
            ├── facebook-sponsored-blocker.js (1400 tokens)
            ├── nook-filters-default.txt (1000 tokens)
            ├── scriptlets.corelibs.json (93.3k tokens)
            ├── youtube-ad-blocker.js (3.4k tokens)
            ├── youtube-sponsorblock.js (3.4k tokens)
      ├── CookieManager/
         ├── CookieManager.swift (2.1k tokens)
      ├── DialogManager/
         ├── DialogManager.swift (2.8k tokens)
         ├── Dialogs/
            ├── BasicAuthDialog.swift (700 tokens)
            ├── BoostsDialog.swift (200 tokens)
            ├── BrowserImportDialog.swift (300 tokens)
            ├── EditPinnedURLDialog.swift (700 tokens)
            ├── ProfileCreationDialog.swift (1100 tokens)
            ├── ProfileDeleteConfirmationDialog.swift (500 tokens)
            ├── ProfileRenameDialog.swift (700 tokens)
            ├── SettingsDialog.swift (600 tokens)
            ├── SpaceCreationDialog.swift (1300 tokens)
            ├── SpaceDeleteConfirmationDialog.swift (600 tokens)
            ├── SpaceEditDialog.swift (1500 tokens)
      ├── DownloadManager/
         ├── DownloadManager.swift (5.1k tokens)
      ├── DragManager/
         ├── DragLockManager.swift (300 tokens)
         ├── TabDragManager.swift (200 tokens)
      ├── ExtensionManager/
         ├── BitwardenBiometricHandler.swift (2000 tokens)
         ├── ExtensionBridge.swift (2.1k tokens)
         ├── ExtensionManager+Delegate.swift (9.8k tokens)
         ├── ExtensionManager+Diagnostics.swift (3.4k tokens)
         ├── ExtensionManager+ExternallyConnectable.swift (10.2k tokens)
         ├── ExtensionManager+Installation.swift (9k tokens)
         ├── ExtensionManager+TabNotifications.swift (1000 tokens)
         ├── ExtensionManager.swift (3.6k tokens)
         ├── InternalNativePortHandler.swift (200 tokens)
         ├── NativeMessagingHandler.swift (2.4k tokens)
         ├── PopupUIDelegate.swift (1000 tokens)
         ├── README-URLSchemeHandler.md
      ├── ExternalMiniWindowManager/
         ├── ExternalMiniWindowManager.swift (1600 tokens)
         ├── MiniBrowserWindowView.swift (600 tokens)
      ├── FindManager/
         ├── FindManager.swift (700 tokens)
      ├── GradientColorManager/
         ├── GradientColorManager.swift (500 tokens)
      ├── HistoryManager/
         ├── HistoryManager.swift (2.4k tokens)
      ├── HoverSidebarManager/
         ├── HoverSidebarManager.swift (1500 tokens)
      ├── ImportManager/
         ├── Arc.swift (2.8k tokens)
         ├── Dia.swift (500 tokens)
         ├── ImportManager.swift (400 tokens)
         ├── Safari.swift (1700 tokens)
      ├── KeyboardShortcutManager/
         ├── KeyboardShortcutManager.swift (4.3k tokens)
         ├── WebsiteShortcutDetector.swift (2.3k tokens)
      ├── MediaControlsManager/
         ├── MediaControlsManager.swift (2.3k tokens)
      ├── PeekManager/
         ├── PeekManager.swift (1000 tokens)
         ├── PeekSession.swift (300 tokens)
         ├── PeekWebView.swift (1700 tokens)
      ├── PiPManager.swift (1100 tokens)
      ├── PrivacyManager/
         ├── OAuthDetector.swift (1300 tokens)
         ├── TrackingProtectionManager.swift (2.3k tokens)
      ├── ProfileManager/
         ├── ProfileManager.swift (1100 tokens)
      ├── SearchManager/
         ├── SearchManager.swift (2.3k tokens)
         ├── Utils.swift (900 tokens)
      ├── SiteRoutingManager/
         ├── SiteRoutingManager.swift (700 tokens)
         ├── SiteRoutingRule.swift (200 tokens)
      ├── SplitViewManager/
         ├── SplitViewManager.swift (3.3k tokens)
      ├── SponsorBlockManager/
         ├── SponsorBlockManager.swift (1600 tokens)
         ├── SponsorBlockModels.swift (900 tokens)
      ├── TabManager/
         ├── TabManager.swift (23.1k tokens)
      ├── TabOrganizerManager/
         ├── LocalLLMEngine.swift (1800 tokens)
         ├── TabOrganizationApplier.swift (1800 tokens)
         ├── TabOrganizationPlan.swift (1800 tokens)
         ├── TabOrganizationPrompt.swift (1100 tokens)
         ├── TabOrganizerManager.swift (1300 tokens)
      ├── WebViewCoordinator/
         ├── WebViewCoordinator.swift (3.3k tokens)
      ├── WindowRegistry/
         ├── WindowRegistry.swift (500 tokens)
      ├── ZoomManager/
         ├── ZoomManager.swift (1200 tokens)
   ├── Models/
      ├── AI/
         ├── AIModels.swift (2.9k tokens)
         ├── MCPModels.swift (1000 tokens)
      ├── BrowserConfig/
         ├── BrowserConfig.swift (1400 tokens)
      ├── BrowserWindowState.swift (1100 tokens)
      ├── Cache/
         ├── CacheModels.swift (2.4k tokens)
      ├── Cookie/
         ├── CookieModels.swift (1300 tokens)
      ├── Extension/
         ├── ExtensionModels.swift (500 tokens)
      ├── History/
         ├── HistoryEntity.swift (200 tokens)
      ├── KeyboardShortcut/
         ├── KeyboardShortcut.swift (3.5k tokens)
         ├── WebsiteShortcutRegistry.swift (4.5k tokens)
      ├── Profile/
         ├── Profile.swift (1400 tokens)
         ├── ProfileEntity.swift (100 tokens)
      ├── Settings/
         ├── SiteSearch.swift (1000 tokens)
      ├── Space/
         ├── GradientNode.swift (200 tokens)
         ├── Space.swift (200 tokens)
         ├── SpaceGradient.swift (2k tokens)
         ├── SpaceModels.swift (200 tokens)
      ├── Tab/
         ├── Tab.swift (32.8k tokens)
         ├── TabFolder.swift (200 tokens)
         ├── TabsModel.swift (600 tokens)
   ├── Nook.entitlements (100 tokens)
   ├── Supporting Files/
      ├── Nook-Bridging-Header.h
   ├── ThirdParty/
      ├── BigUIPaging/
         ├── Examples/
            ├── CardDeckExample.swift (300 tokens)
            ├── CustomPageViewExample.swift (300 tokens)
            ├── ExampleGridToPageView.swift (1200 tokens)
            ├── PageIndicatorExample.swift (800 tokens)
            ├── PageViewBasicExample.swift (400 tokens)
            ├── PageViewForEachExample.swift (100 tokens)
            ├── PageViewNavigationStackExample.swift (900 tokens)
         ├── Implementations/
            ├── PageIndicator/
               ├── PageIndicator+Environment.swift (600 tokens)
               ├── PageIndicator+iOS.swift (800 tokens)
               ├── PageIndicator.swift (1100 tokens)
            ├── PageView/
               ├── Platform/
                  ├── PlatformPageView+iOS.swift (1900 tokens)
                  ├── PlatformPageView+macOS.swift (2.7k tokens)
                  ├── PlatformPageView.swift (100 tokens)
               ├── Styles/
                  ├── BookPageViewStyle.swift (300 tokens)
                  ├── BookStackPageViewStyle.swift (200 tokens)
                  ├── CardDeckPageViewStyle.swift (1400 tokens)
                  ├── HistoryStackPageViewStyle.swift (200 tokens)
                  ├── PlainPageViewStyle.swift (200 tokens)
                  ├── PlatformPageViewStyle.swift (100 tokens)
                  ├── ScrollPageViewStyle.swift (200 tokens)
               ├── Types/
                  ├── PageViewDirection.swift (100 tokens)
                  ├── PageViewNavigateAction.swift (200 tokens)
                  ├── PageViewStyle.swift (600 tokens)
                  ├── PageViewStyleConfiguration.swift (500 tokens)
               ├── View/
                  ├── PageView+Environment.swift (600 tokens)
                  ├── PageView+Preferences.swift (100 tokens)
                  ├── PageView.swift (2.3k tokens)
                  ├── PageViewEnvironmentModifier.swift (200 tokens)
                  ├── PageViewNavigationButton.swift (500 tokens)
            ├── Utils/
               ├── View+Inspect.swift (800 tokens)
               ├── View+Measure.swift (100 tokens)
         ├── README.md (2000 tokens)
      ├── HTSymbolHook/
         ├── HTSymbolHook.h (200 tokens)
         ├── HTSymbolHook.m (1200 tokens)
         ├── README.md (200 tokens)
         ├── mach_override/
            ├── .gitignore
            ├── README.markdown (800 tokens)
            ├── Rakefile (100 tokens)
            ├── libudis86/
               ├── decode.c (5.4k tokens)
               ├── decode.h (1100 tokens)
               ├── extern.h (700 tokens)
               ├── input.c (1100 tokens)
               ├── input.h (600 tokens)
               ├── itab.c (63.4k tokens)
               ├── itab.h (2.1k tokens)
               ├── syn-att.c (1200 tokens)
               ├── syn-intel.c (1300 tokens)
               ├── syn.c (1200 tokens)
               ├── syn.h (400 tokens)
               ├── types.h (1500 tokens)
               ├── udint.h (500 tokens)
               ├── udis86.c (1900 tokens)
            ├── mach_override.c (5k tokens)
            ├── mach_override.h (600 tokens)
            ├── udis86.h (300 tokens)
      ├── MuteableWKWebView/
         ├── Info.plist (200 tokens)
         ├── MethodSwizzler.h
         ├── MethodSwizzler.m (200 tokens)
         ├── MuteableWKWebView.h (100 tokens)
         ├── MuteableWKWebView.m (400 tokens)
         ├── MuteableWKWebViewPrivate.h (100 tokens)
         ├── README.md (200 tokens)
         ├── SearchSymbol.h (100 tokens)
         ├── SearchSymbol.m (300 tokens)
         ├── getPage.c (300 tokens)
         ├── getPage.h (100 tokens)
   ├── Utils/
      ├── AnyShape.swift (100 tokens)
      ├── BarycentricGradientView.swift (1400 tokens)
      ├── BlurEffectView.swift (100 tokens)
      ├── BlurModifier.swift (200 tokens)
      ├── Colors.swift (1200 tokens)
      ├── Debug/
         ├── NavigationRootCauseAnalysis.md (1900 tokens)
      ├── ExtensionUtils.swift (500 tokens)
      ├── ForceArrowCursorView.swift (400 tokens)
      ├── HoverTrackingView.swift (400 tokens)
      ├── PasteboardTypes.swift
      ├── Shaders/
         ├── BarycentricShaders.metal (800 tokens)
      ├── WebKit/
         ├── FocusableWKWebView.swift (3k tokens)
         ├── WebContextMenu.swift (2.4k tokens)
         ├── WebContextMenuBridge.swift (1100 tokens)
      ├── WebStoreDownloader.swift (1300 tokens)
      ├── WebStoreInjector.js (1300 tokens)
      ├── WebStoreScriptHandler.swift (700 tokens)
      ├── WebViewThemeColorExtension.swift (1500 tokens)
      ├── WindowUtils.swift (700 tokens)
      ├── darkreader.js (51.3k tokens)
   ├── logo-dev.icon/
      ├── Assets/
         ├── Grid.png
         ├── Light Mode.svg (1100 tokens)
      ├── icon.json (400 tokens)
   ├── logo.icon/
      ├── Assets/
         ├── Light Mode.svg (1100 tokens)
      ├── icon.json (300 tokens)
├── Onboarding/
   ├── Components/
      ├── OnboardingUtils.swift (200 tokens)
      ├── RoundedSpinner.swift (100 tokens)
      ├── StageFooter.swift (500 tokens)
      ├── StageIndicator.swift (100 tokens)
      ├── TransitionView.swift (600 tokens)
      ├── ViewTransition.metal (200 tokens)
   ├── OnboardingView.swift (900 tokens)
   ├── Stages/
      ├── AdBlockerStage.swift (300 tokens)
      ├── AiChatStage.swift (300 tokens)
      ├── BackgroundStage.swift (600 tokens)
      ├── FInalStage.swift (100 tokens)
      ├── HelloStage.swift (100 tokens)
      ├── ImportStage.swift (400 tokens)
      ├── SafariImportFlow.swift (2.9k tokens)
      ├── TabLayoutStage.swift (300 tokens)
      ├── URLBarStage.swift (300 tokens)
├── README.md (1400 tokens)
├── Settings/
   ├── NookSettingsService.swift (5.7k tokens)
├── UI/
   ├── Buttons/
      ├── NavButtons/
         ├── NavButton.swift (1400 tokens)
      ├── NookButton/
         ├── NookButtonStyle.swift (1400 tokens)
   ├── ConditionalModifiers.swift (800 tokens)
├── assets/
   ├── icon.png
├── telemetry-id
```


## /.github/workflows/enforce-pr-base.yml

```yml path="/.github/workflows/enforce-pr-base.yml" 
name: Enforce PR base branch
on:
  pull_request:
    types: [opened, edited, synchronize]

jobs:
  check-branch:
    runs-on: ubuntu-latest
    steps:
      - name: Fail if PR targets main
        if: github.event.pull_request.base.ref == 'main'
        run: |
          echo "PRs must target dev, not main."
          exit 1

```

## /.github/workflows/macos-notarize.yml

```yml path="/.github/workflows/macos-notarize.yml" 
name: macOS Build, Sign, Notarize & Release

on:
  release:
    types: [published]
  workflow_dispatch:

jobs:
  build-sign-notarize:
    runs-on: macos-26
    permissions:
      contents: write

    steps:
      - name: Checkout code
        uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Set version info
        run: |
          VERSION="${{ github.event.release.tag_name }}"
          SHORT_VERSION="${VERSION#v}"
          BUILD_NUMBER=$(grep 'CURRENT_PROJECT_VERSION' Nook.xcodeproj/project.pbxproj \
            | head -n1 | grep -oE '[0-9]+' | tail -n1)
          echo "VERSION=$VERSION" >> $GITHUB_ENV
          echo "SHORT_VERSION=$SHORT_VERSION" >> $GITHUB_ENV
          echo "BUILD_NUMBER=$BUILD_NUMBER" >> $GITHUB_ENV
          echo "Building version $SHORT_VERSION (build $BUILD_NUMBER)"

      - name: Import Developer ID certificate
        env:
          APPLE_CERTIFICATE_P12_BASE64: ${{ secrets.APPLE_CERTIFICATE_P12_BASE64 }}
          APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
        run: |
          printf "%s" "$APPLE_CERTIFICATE_P12_BASE64" | base64 --decode > signing_certificate.p12

          security create-keychain -p "" build.keychain
          security default-keychain -s build.keychain
          security unlock-keychain -p "" build.keychain

          security import signing_certificate.p12 \
            -k build.keychain \
            -P "$APPLE_CERTIFICATE_PASSWORD" \
            -T /usr/bin/codesign

          security list-keychains -d user -s build.keychain login.keychain
          security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "" build.keychain

          IDENTITY=$(security find-identity -v -p codesigning build.keychain \
            | grep "Developer ID Application" | head -n1 | awk '{print $2}')
          echo "SIGNING_IDENTITY=$IDENTITY" >> $GITHUB_ENV
          echo "Using signing identity: $IDENTITY"

      - name: Build app
        env:
          APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
        run: |
          set -e
          mkdir -p build
          ENTITLEMENTS="$(pwd)/Nook/Nook.entitlements"
          echo "Attempting universal build (arm64 + x86_64)..."
          if ! xcodebuild -scheme Nook -configuration Release \
            -arch arm64 -arch x86_64 \
            -derivedDataPath build \
            CODE_SIGN_IDENTITY="$SIGNING_IDENTITY" \
            CODE_SIGN_STYLE=Manual \
            DEVELOPMENT_TEAM="$APPLE_TEAM_ID" \
            CODE_SIGN_ENTITLEMENTS="$ENTITLEMENTS" \
            PROVISIONING_PROFILE_SPECIFIER=""; then
            echo "Universal build failed, retrying Apple Silicon only..."
            xcodebuild -scheme Nook -configuration Release \
              -arch arm64 \
              -derivedDataPath build \
              CODE_SIGN_IDENTITY="$SIGNING_IDENTITY" \
              CODE_SIGN_STYLE=Manual \
              DEVELOPMENT_TEAM="$APPLE_TEAM_ID" \
              CODE_SIGN_ENTITLEMENTS="$ENTITLEMENTS" \
              PROVISIONING_PROFILE_SPECIFIER=""
          fi
          cp -R "build/Build/Products/Release/Nook.app" ./Nook.app

      - name: Re-sign for notarization
        run: |
          # Notarization requires all binaries to be signed with our Developer ID,
          # a secure timestamp, and hardened runtime. Sparkle ships pre-signed with
          # its own cert, so we must re-sign the entire bundle from inside out.
          SPARKLE="Nook.app/Contents/Frameworks/Sparkle.framework/Versions/B"

          # Sign innermost binaries first
          codesign --force --sign "$SIGNING_IDENTITY" --timestamp --options runtime \
            "$SPARKLE/XPCServices/Downloader.xpc/Contents/MacOS/Downloader"
          codesign --force --sign "$SIGNING_IDENTITY" --timestamp --options runtime \
            "$SPARKLE/XPCServices/Installer.xpc/Contents/MacOS/Installer"
          codesign --force --sign "$SIGNING_IDENTITY" --timestamp --options runtime \
            "$SPARKLE/Updater.app/Contents/MacOS/Updater"
          codesign --force --sign "$SIGNING_IDENTITY" --timestamp --options runtime \
            "$SPARKLE/Autoupdate"

          # Sign bundles
          codesign --force --sign "$SIGNING_IDENTITY" --timestamp --options runtime \
            "$SPARKLE/XPCServices/Downloader.xpc"
          codesign --force --sign "$SIGNING_IDENTITY" --timestamp --options runtime \
            "$SPARKLE/XPCServices/Installer.xpc"
          codesign --force --sign "$SIGNING_IDENTITY" --timestamp --options runtime \
            "$SPARKLE/Updater.app"

          # Sign the Sparkle dylib itself before signing the framework bundle
          codesign --force --sign "$SIGNING_IDENTITY" --timestamp \
            "$SPARKLE/Sparkle"

          # Sign the framework version (Versions/B) — do NOT also sign the
          # top-level Sparkle.framework symlink; it resolves to the same Versions/B
          # directory and double-signing would invalidate the signature.
          codesign --force --sign "$SIGNING_IDENTITY" --timestamp --options runtime "$SPARKLE"

          # Re-sign the main app with entitlements and hardened runtime
          codesign --force --sign "$SIGNING_IDENTITY" --timestamp \
            --options runtime \
            --entitlements "$(pwd)/Nook/Nook.entitlements" \
            "Nook.app"

      - name: Verify signature before notarizing
        run: codesign --verify --deep --strict --verbose=2 "Nook.app"

      - name: Notarize app
        env:
          APPLE_ID: ${{ secrets.APPLE_ID }}
          APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
          APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }}
        run: |
          xcrun notarytool store-credentials "nook-notary" \
            --apple-id "$APPLE_ID" \
            --team-id "$APPLE_TEAM_ID" \
            --password "$APPLE_APP_SPECIFIC_PASSWORD"

          # ditto preserves macOS symlinks and extended attributes; zip -r does not.
          # Broken symlinks in the archive cause "invalid signature" errors because
          # the bundle seal references symlink targets, not file copies.
          ditto -c -k --keepParent "Nook.app" Nook.zip
          SUBMIT_OUTPUT=$(xcrun notarytool submit "Nook.zip" \
            --keychain-profile "nook-notary" \
            --wait 2>&1)
          echo "$SUBMIT_OUTPUT"
          SUBMISSION_ID=$(echo "$SUBMIT_OUTPUT" | grep "^  id:" | head -1 | awk '{print $2}')
          STATUS=$(echo "$SUBMIT_OUTPUT" | grep "^  status:" | awk '{print $2}')
          if [ "$STATUS" != "Accepted" ]; then
            echo "Notarization rejected. Fetching log..."
            xcrun notarytool log "$SUBMISSION_ID" --keychain-profile "nook-notary"
            exit 1
          fi

      - name: Staple notarization ticket
        run: xcrun stapler staple "Nook.app"

      - name: Verify signature
        run: |
          codesign --verify --deep --strict --verbose=2 "Nook.app"
          spctl --assess --type execute --verbose "Nook.app"

      - name: Create DMG
        run: |
          hdiutil create -volname "Nook $SHORT_VERSION" \
            -srcfolder "Nook.app" \
            -ov -format UDZO "Nook-${VERSION}.dmg"

      - name: Upload DMG to release
        uses: softprops/action-gh-release@v2
        with:
          files: Nook-*.dmg
          fail_on_unmatched_files: false
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

      - name: Update appcast on gh-pages
        run: |
          DMG_URL="https://github.com/${{ github.repository }}/releases/download/${VERSION}/Nook-${VERSION}.dmg"
          DATE=$(date -R)

          git fetch origin gh-pages
          git checkout gh-pages

          ENTRY=$(cat << XMLEOF
            <item>
              <title>Version ${SHORT_VERSION}</title>
              <sparkle:releaseNotesLink>https://github.com/${{ github.repository }}/releases/tag/${VERSION}</sparkle:releaseNotesLink>
              <pubDate>${DATE}</pubDate>
              <enclosure
                url="${DMG_URL}"
                sparkle:version="${BUILD_NUMBER}"
                sparkle:shortVersionString="${SHORT_VERSION}"
                type="application/octet-stream"/>
            </item>
          XMLEOF
          )

          python3 -c "import sys; content = open('appcast.xml').read(); entry = sys.argv[1]; content = content.replace('</channel>', entry + '\n</channel>', 1); open('appcast.xml', 'w').write(content)" "$ENTRY"

          git config user.name "github-actions[bot]"
          git config user.email "github-actions[bot]@users.noreply.github.com"
          git add appcast.xml
          git commit -m "Release ${VERSION}"
          git push origin gh-pages

```

## /.gitignore

```gitignore path="/.gitignore" 
# Xcode
#
# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
sessions-config.json
.claude
Nook/.claude
.crush
/sessions
AGENTS.md
.xcodebuildmcp/
package.json
package-lock.json
.opencode/

## User settings
xcuserdata/

# Xcode nonsense
 *.pbxproj
# macOS garbage
.DS_Store

## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9)
*.xcscmblueprint
*.xccheckout

## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4)
build/
DerivedData/
*.moved-aside
*.pbxuser
!default.pbxuser
*.mode1v3
!default.mode1v3
*.mode2v3
!default.mode2v3
*.perspectivev3
!default.perspectivev3

## Obj-C/Swift specific
*.hmap

## App packaging
*.ipa
*.dSYM.zip
*.dSYM

## Playgrounds
'timeline.xctimeline'
playground.xcworkspace
*.playground
UIPlayground/

# Swift Package Manager
#
# Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
# Packages/
# Package.pins
# Package.resolved
# *.xcodeproj
#
# Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata
# hence it is not needed unless you have added a package configuration file to your project
# .swiftpm

.build/

# CocoaPods
#
# We recommend against adding the Pods directory to your .gitignore. However
# you should judge for yourself, the pros and cons are mentioned at:
# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
#
Pods/
#
# Add this line if you want to avoid checking in source code from the Xcode workspace
# *.xcworkspace

# Carthage
#
# Add this line if you want to avoid checking in source code from Carthage dependencies.
# Carthage/Checkouts

Carthage/Build/

# Accio dependency management
Dependencies/
.accio/

# fastlane
#
# It is recommended to not store the screenshots in the git repo.
# Instead, use fastlane to re-generate the screenshots whenever they are needed.
# For more information about the recommended setup visit:
# https://docs.fastlane.tools/best-practices/source-control/#source-control

fastlane/report.xml
fastlane/Preview.html
fastlane/screenshots/**/*.png
fastlane/test_output

# Code Injection
# After new code Injection tools there's a generated folder /iOSInjectionProject
# https://github.com/johnno1962/injectionforxcode

iOSInjectionProject/
# IDE
.vscode/

# Xcode build junk
DerivedData/
*.xcresult
*.log
*.profraw
.superpowers/

```

## /.gitmodules

```gitmodules path="/.gitmodules" 
[submodule "Fuzi"]
	path = Fuzi
	url = https://github.com/cezheng/Fuzi.git
[submodule "Highlightr"]
	path = Highlightr
	url = https://github.com/raspu/Highlightr.git
[submodule "LRUCache"]
	path = LRUCache
	url = https://github.com/nicklockwood/LRUCache.git
[submodule "Motion"]
	path = Motion
	url = https://github.com/b3ll/Motion.git
[submodule "reeeed"]
	path = reeeed
	url = https://github.com/nate-parrott/reeeed.git
[submodule "swift-atomics"]
	path = swift-atomics
	url = https://github.com/apple/swift-atomics.git
[submodule "swift-numerics"]
	path = swift-numerics
	url = https://github.com/apple/swift-numerics.git

```

## /App/AppDelegate.swift

```swift path="/App/AppDelegate.swift" 
//
//  AppDelegate.swift
//  Nook
//
//  Application lifecycle delegate handling app termination, URL events, and Sparkle updates
//

import AppKit
import OSLog
import Sparkle

/// Handles application-level lifecycle events and coordinates app termination
///
/// Key responsibilities:
/// - **URL Handling**: Opens external URLs (e.g., from other apps, custom URL schemes)
/// - **Mouse Button Events**: Maps mouse buttons 2/3/4 to command palette, back, and forward
/// - **App Termination**: Coordinates graceful shutdown with data persistence
/// - **Sparkle Updates**: Integrates with Sparkle framework for auto-updates
///
/// The termination flow uses async persistence to avoid MainActor deadlocks:
/// 1. Returns `.terminateLater` immediately
/// 2. Persists tab snapshots atomically
/// 3. Saves SwiftData context
/// 4. Cleans up WKWebView processes
/// 5. Replies with terminate approval
class AppDelegate: NSObject, NSApplicationDelegate, SPUUpdaterDelegate {
    private static let log = Logger(
        subsystem: Bundle.main.bundleIdentifier ?? "Nook", category: "AppTermination")

    // TEMPORARY: Reference to BrowserManager for coordinating browser operations
    // TODO: Replace with direct access to independent managers (TabManager, etc.)
    weak var browserManager: BrowserManager?

    // Window registry for accessing active window state
    weak var windowRegistry: WindowRegistry?

    // MCP Manager reference for cleanup on termination
    var mcpManager: MCPManager?

    private let urlEventClass = AEEventClass(kInternetEventClass)
    private let urlEventID = AEEventID(kAEGetURL)
    private var mouseEventMonitor: Any?
    private var wakeObserver: Any?
    private let userDefaults = UserDefaults.standard
    private var pendingURLs: [URL] = []
    


    // MARK: - Sparkle Updates

    /// Sparkle updater controller for automatic app updates
    lazy var updaterController: SPUStandardUpdaterController = {
        return SPUStandardUpdaterController(
            startingUpdater: true, updaterDelegate: self, userDriverDelegate: nil)
    }()

    // MARK: - Application Lifecycle

    func applicationDidFinishLaunching(_ notification: Notification) {
        setupURLEventHandling()
        setupMouseButtonHandling()
        setupSleepWakeHandling()
        let didFinishOnboarding = userDefaults.bool(forKey: "settings.didFinishOnboarding")

        if let window = NSApplication.shared.windows.first {
            // Always hide titlebar text immediately to prevent flash during transitions
            window.titlebarAppearsTransparent = true
            window.titleVisibility = .hidden
            window.toolbar?.isVisible = false

            if !didFinishOnboarding {
                window.setContentSize(NSSize(width: 1200, height: 720))
                window.center()
                NSApp.activate(ignoringOtherApps: true)
                NSApp.hideOtherApplications(nil)
            }
        }
    }

    /// Observes system wake notifications and resets crash counters on all tabs.
    ///
    /// When the system wakes from sleep, launchservicesd and other XPC services need
    /// a few seconds to fully restart. During this window, new WebContent processes crash
    /// immediately with XPC_ERROR_CONNECTION_INVALID. We reset crash counters on wake so
    /// the exponential backoff in webViewWebContentProcessDidTerminate starts fresh and
    /// the delayed reload eventually succeeds once XPC services are stable.
    private func setupSleepWakeHandling() {
        wakeObserver = NSWorkspace.shared.notificationCenter.addObserver(
            forName: NSWorkspace.didWakeNotification,
            object: nil,
            queue: .main
        ) { [weak self] _ in
            self?.handleSystemWake()
        }
    }

    private func handleSystemWake() {
        AppDelegate.log.info("System woke from sleep — resetting web process crash counters")
        // Reset crash counters so tabs get fresh backoff windows after wake.
        // Tabs that were mid-crash-loop before sleep will retry with a clean slate.
        // Called on the main queue (per NSWorkspace notification delivery), so MainActor access is safe.
        MainActor.assumeIsolated {
            guard let manager = browserManager else { return }
            for tab in manager.tabManager.allTabs() {
                tab.webProcessCrashCount = 0
                tab.lastWebProcessCrashDate = .distantPast
            }
        }
    }

    /// Registers handler for external URL events (e.g., clicking links from other apps)
    private func setupURLEventHandling() {
        NSAppleEventManager.shared().setEventHandler(
            self,
            andSelector: #selector(handleGetURLEvent(_:withReplyEvent:)),
            forEventClass: urlEventClass,
            andEventID: urlEventID
        )
    }

    /// Sets up global mouse button event monitoring for extra physical mouse buttons
    ///
    /// Many mice have extra buttons beyond left/right click. This maps them to browser actions:
    /// - **Button 2** (middle click/scroll wheel button): Open command palette
    /// - **Button 3** (typically a side button labeled "Back"): Navigate back in history
    /// - **Button 4** (typically a side button labeled "Forward"): Navigate forward in history
    ///
    /// This is common in browsers - side buttons on gaming/office mice are often used for navigation.
    private func setupMouseButtonHandling() {
        mouseEventMonitor = NSEvent.addLocalMonitorForEvents(matching: .otherMouseDown) {
            [weak self] event in
            guard let self = self,
                  let manager = self.browserManager,
                  let registry = self.windowRegistry else { return event }

            // Mouse events are delivered on the main thread, so we can safely assume main actor isolation
            MainActor.assumeIsolated {
                switch event.buttonNumber {
                case 2:  // Middle mouse button
                    if let hoveredId = manager.hoveredPinnedTabId,
                       let tab = manager.tabManager.allTabs().first(where: { $0.id == hoveredId }),
                       tab.pinnedURL != nil {
                        tab.resetToPinnedURL()
                    } else {
                        registry.activeWindow?.commandPalette?.open()
                    }
                case 3:  // Back button
                    guard
                        let windowState = registry.activeWindow,
                        let currentTab = manager.currentTab(for: windowState),
                        let webView = manager.getWebView(for: currentTab.id, in: windowState.id)
                    else {
                        return
                    }
                    webView.goBack()
                case 4:  // Forward button
                    guard
                        let windowState = registry.activeWindow,
                        let currentTab = manager.currentTab(for: windowState),
                        let webView = manager.getWebView(for: currentTab.id, in: windowState.id)
                    else {
                        return
                    }
                    webView.goForward()
                default:
                    break
                }
            }
            return event
        }
    }

    /// Handles URLs opened from external sources (e.g., Finder, other apps)
    func application(_ application: NSApplication, open urls: [URL]) {
        urls.forEach { handleIncoming(url: $0) }
    }

    // MARK: - Application Termination

    /// Initiates async termination process to avoid MainActor deadlocks
    ///
    /// Returns `.terminateLater` immediately, then performs async cleanup:
    /// 1. Phase 1: Atomic snapshot persistence (non-blocking)
    /// 2. Phase 2: SwiftData context save
    /// 3. Phase 3: WKWebView process cleanup
    ///
    /// - Returns: Always returns `.terminateLater` to handle termination asynchronously
    func applicationShouldTerminate(_ sender: NSApplication) -> NSApplication.TerminateReply {
        // When "warn before quitting" is disabled, terminate(nil) is called directly
        // from the SwiftUI CommandGroup button action. Returning .terminateLater in that
        // context deadlocks because the async Task can't execute during the termination
        // run loop mode. Since the user opted out of the warning, just quit immediately.
        let askBeforeQuit = userDefaults.bool(forKey: "settings.askBeforeQuit")
        if !askBeforeQuit {
            return .terminateNow
        }

        handleTermination(sender: sender, shouldTerminate: true)
        return .terminateLater
    }

    /// Performs async termination tasks on MainActor
    ///
    /// This method executes the three-phase shutdown process:
    /// - **Phase 1**: Atomic tab snapshot persistence
    /// - **Phase 2**: SwiftData context save
    /// - **Phase 3**: WebView cleanup
    ///
    /// Timing is logged for each phase to monitor performance.
    private func handleTermination(sender: NSApplication, shouldTerminate: Bool) {
        AppDelegate.log.info(
            "applicationShouldTerminate: returning terminateLater and starting async persistence")

        Task { @MainActor in
            guard shouldTerminate else {
                sender.reply(toApplicationShouldTerminate: false)
                return
            }

            // Minimal fallback if BrowserManager is unavailable
            guard let manager = browserManager else {
                // Attempt a best-effort save via shared persistence container
                do {
                    let ctx = Persistence.shared.container.mainContext
                    try ctx.save()
                    AppDelegate.log.info("Fallback save without BrowserManager succeeded")
                } catch {
                    AppDelegate.log.error(
                        "Fallback save without BrowserManager failed: \(String(describing: error))"
                    )
                }
                sender.reply(toApplicationShouldTerminate: true)
                return
            }

            let overallStart = CFAbsoluteTimeGetCurrent()
            AppDelegate.log.info("Termination task started on MainActor")

            // Phase 1: Atomic snapshot persistence (non-throwing Bool)
            let persistStart = CFAbsoluteTimeGetCurrent()
            let atomic: Bool = await manager.tabManager.persistSnapshotAwaitingResult()
            let pdt = CFAbsoluteTimeGetCurrent() - persistStart
            AppDelegate.log.info(
                "Atomic persistence \(atomic ? "succeeded" : "did not run; fallback used") in \(String(format: "%.3f", pdt))s"
            )

            // Phase 2: Ensure SwiftData changes are committed
            let contextSaveStart = CFAbsoluteTimeGetCurrent()
            do {
                try manager.modelContext.save()
                let sdt = CFAbsoluteTimeGetCurrent() - contextSaveStart
                AppDelegate.log.info("Context save completed in \(String(format: "%.3f", sdt))s")
            } catch {
                let sdt = CFAbsoluteTimeGetCurrent() - contextSaveStart
                AppDelegate.log.error(
                    "Context save failed in \(String(format: "%.3f", sdt))s: \(String(describing: error))"
                )
            }

            // Phase 3: Graceful cleanup
            manager.cleanupAllTabs()
            AppDelegate.log.info("Cleanup completed; WKWebView processes terminated")

            let total = CFAbsoluteTimeGetCurrent() - overallStart
            AppDelegate.log.info(
                "Termination task finished in \(String(format: "%.3f", total))s; replying to terminate"
            )
            sender.reply(toApplicationShouldTerminate: true)
        }
    }

    func applicationWillTerminate(_ notification: Notification) {
        // Keep minimal to avoid MainActor deadlocks; main work happens in applicationShouldTerminate
        AppDelegate.log.info("applicationWillTerminate called")

        // Stop MCP child processes synchronously (blocking up to 5 seconds)
        mcpManager?.stopAllSync()
    }

    // MARK: - External URL Handling

    /// Handles URL events from AppleScript/AppleEvents
    @objc private func handleGetURLEvent(
        _ event: NSAppleEventDescriptor, withReplyEvent replyEvent: NSAppleEventDescriptor
    ) {
        guard let stringValue = event.paramDescriptor(forKeyword: keyDirectObject)?.stringValue,
            let url = URL(string: stringValue)
        else {
            return
        }

        // Security: Only allow http/https URLs from external automation
        guard let scheme = url.scheme?.lowercased(),
              scheme == "http" || scheme == "https" else {
            return
        }

        handleIncoming(url: url)
    }

    /// Routes incoming external URLs to the browser manager
    ///
    /// If the browser manager isn't ready yet (cold launch via URL click),
    /// queues the URL and drains it once `browserManager` is set.
    private func handleIncoming(url: URL) {
        guard let manager = browserManager else {
            AppDelegate.log.info("Queuing URL for deferred open: \(url.absoluteString, privacy: .public)")
            pendingURLs.append(url)
            return
        }
        Task { @MainActor in
            // Air Traffic Control — route to designated space if a rule matches
            if manager.siteRoutingManager.applyRoute(url: url, from: nil) {
                return
            }
            manager.presentExternalURL(url)
        }
    }

    /// Opens any URLs that arrived before browserManager was available
    func drainPendingURLs() {
        guard !pendingURLs.isEmpty else { return }
        let urls = pendingURLs
        pendingURLs.removeAll()
        urls.forEach { handleIncoming(url: $0) }
    }
}

// MARK: - Sparkle Delegate

extension AppDelegate {
    /// Called when Sparkle finds a valid update
    func updater(_ updater: SPUUpdater, didFindValidUpdate item: SUAppcastItem) {
        Task { @MainActor in
            browserManager?.handleUpdaterFoundValidUpdate(item)
        }
    }

    /// Called when Sparkle finishes downloading an update
    func updater(_ updater: SPUUpdater, didFinishDownloadingUpdate item: SUAppcastItem) {
        Task { @MainActor in
            browserManager?.handleUpdaterFinishedDownloading(item)
        }
    }

    /// Called when no update is found
    func updaterDidNotFindUpdate(_ updater: SPUUpdater) {
        Task { @MainActor in
            browserManager?.handleUpdaterDidNotFindUpdate()
        }
    }

    /// Called when user cancels the update download
    func userDidCancelDownload(_ updater: SPUUpdater) {
        Task { @MainActor in
            browserManager?.handleUpdaterAbortedUpdate()
        }
    }

    /// Called when update process encounters an error
    func updater(_ updater: SPUUpdater, didAbortWithError error: any Error) {
        Task { @MainActor in
            browserManager?.handleUpdaterAbortedUpdate()
        }
    }

    /// Called when update is ready to install on quit
    func updater(
        _ updater: SPUUpdater, willInstallUpdateOnQuit item: SUAppcastItem,
        immediateInstallationInvocation: @escaping () -> Void
    ) {
        Task { @MainActor in
            browserManager?.handleUpdaterWillInstallOnQuit(item)
        }
    }
}

```

## /App/ContentView.swift

```swift path="/App/ContentView.swift" 
//
//  ContentView.swift
//  Nook
//
//  Created by Maciek Bagiński on 28/07/2025.
//  Updated by Aether Aurelia on 15/11/2025.
//

import SwiftUI
import AppKit

struct ContentView: View {
    @EnvironmentObject var browserManager: BrowserManager
    @EnvironmentObject var tabManager: TabManager
    @Environment(WindowRegistry.self) private var windowRegistry
    @State private var defaultWindowState = BrowserWindowState()
    @State private var commandPalette = CommandPalette()
    
    private let providedWindowState: BrowserWindowState?
    
    init(windowState: BrowserWindowState? = nil) {
        self.providedWindowState = windowState
    }
    
    private var windowState: BrowserWindowState {
        providedWindowState ?? defaultWindowState
    }

    var body: some View {
        WindowView()
            .environment(windowState)
            .environment(commandPalette)
            .environmentObject(browserManager.gradientColorManager)
            .background(WindowFocusBridge(windowState: windowState, windowRegistry: windowRegistry))
            .frame(minWidth: 470, minHeight: 382)
            .onAppear {
                // Set TabManager reference for computed properties
                windowState.tabManager = tabManager
                // Set CommandPalette reference for global shortcuts
                windowState.commandPalette = commandPalette
                // Register this window state with the registry
                windowRegistry.register(windowState)
            }
            .onDisappear {
                // Unregister this window state when the window closes
                windowRegistry.unregister(windowState.id)
            }
    }
}

private struct WindowFocusBridge: NSViewRepresentable {
    let windowState: BrowserWindowState
    let windowRegistry: WindowRegistry

    func makeCoordinator() -> Coordinator {
        Coordinator(windowState: windowState, windowRegistry: windowRegistry)
    }
    
    func makeNSView(context: Context) -> NSView {
        let view = NSView()
        DispatchQueue.main.async {
            context.coordinator.attach(to: view.window)
        }
        return view
    }
    
    func updateNSView(_ nsView: NSView, context: Context) {
        // Window is available at update time — call synchronously.
        // The attach method guards against re-attachment to the same window.
        context.coordinator.attach(to: nsView.window)
    }
    
    static func dismantleNSView(_ nsView: NSView, coordinator: Coordinator) {
        coordinator.detach()
    }

    final class Coordinator: NSObject {
        let windowState: BrowserWindowState
        let windowRegistry: WindowRegistry
        private weak var window: NSWindow?
        private var keyObserver: Any?

        init(windowState: BrowserWindowState, windowRegistry: WindowRegistry) {
            self.windowState = windowState
            self.windowRegistry = windowRegistry
        }

        func attach(to window: NSWindow?) {
            guard self.window !== window else { return }
            detach()
            self.window = window
            guard let window else { return }

            // Store NSWindow reference on the window state so other systems
            // (e.g. KeyboardShortcutManager) can identify browser windows
            Task { @MainActor in
                windowState.window = window
            }

            keyObserver = NotificationCenter.default.addObserver(
                forName: NSWindow.didBecomeKeyNotification,
                object: window,
                queue: .main
            ) { [weak self] _ in
                guard let self else { return }
                Task { @MainActor in
                    self.windowRegistry.setActive(self.windowState)
                }
            }

            if window.isKeyWindow {
                Task { @MainActor in
                    windowRegistry.setActive(windowState)
                }
            }
        }

        func detach() {
            if let observer = keyObserver {
                NotificationCenter.default.removeObserver(observer)
                keyObserver = nil
            }
            window = nil
        }

        deinit {
            detach()
        }
    }
}

```

## /App/NookApp.swift

```swift path="/App/NookApp.swift" 
//
//  NookApp.swift
//  Nook
//
//  Created by Maciek Bagiński on 28/07/2025.
//  Updated by Aether Aurelia on 15/11/2025.
//

import AppKit
import Carbon
import OSLog
import Sparkle
import SwiftUI

@main
struct NookApp: App {
    @State private var windowRegistry = WindowRegistry()
    @State private var webViewCoordinator = WebViewCoordinator()
    @State private var settingsManager = NookSettingsService()
    @State private var keyboardShortcutManager = KeyboardShortcutManager()
    @State private var aiConfigService: AIConfigService
    @State private var mcpManager = MCPManager()
    @State private var aiService: AIService
    @State private var tabOrganizerManager = TabOrganizerManager()
    @NSApplicationDelegateAdaptor(AppDelegate.self) var appDelegate

    // TEMPORARY: BrowserManager will be phased out as a global singleton.
    // Eventually each manager (TabManager, etc.) will be independent and injected via environment.
    @StateObject private var browserManager = BrowserManager()

    init() {
        let config = AIConfigService()
        _aiConfigService = State(initialValue: config)
        _aiService = State(initialValue: AIService(configService: config))
    }

    var body: some Scene {
        WindowGroup {
            TransitionView(showB: $settingsManager.didFinishOnboarding) {
                OnboardingView()
                    .ignoresSafeArea(.all)
                    .background(BackgroundWindowModifier())
                    .environment(\.nookSettings, settingsManager)
                    .environmentObject(browserManager)
            } viewB: {
                ContentView()
                    .ignoresSafeArea(.all)
                    .background(BackgroundWindowModifier())
                    .environmentObject(browserManager)
                    .environmentObject(browserManager.tabManager)
                    .environment(windowRegistry)
                    .environment(webViewCoordinator)
                    .environment(\.nookSettings, settingsManager)
                    .environment(keyboardShortcutManager)
                    .environment(aiConfigService)
                    .environment(mcpManager)
                    .environment(aiService)
                    .environment(tabOrganizerManager)
                    .onAppear {
                        setupApplicationLifecycle()
                        setupAIServices()
                    }
                
                
            }

        }
        .windowStyle(.hiddenTitleBar)
        .commands {
            NookCommands(
                browserManager: browserManager,
                windowRegistry: windowRegistry,
                shortcutManager: keyboardShortcutManager,
                tabOrganizerManager: tabOrganizerManager
            )
        }


        // macOS 26 style sidebar settings window
        Window("Nook Settings", id: "nook-settings") {
            SettingsWindow()
                .environmentObject(browserManager)
                .environmentObject(browserManager.tabManager)
                .environmentObject(browserManager.gradientColorManager)
                .environment(\.nookSettings, settingsManager)
                .environment(keyboardShortcutManager)
                .environment(aiConfigService)
                .environment(mcpManager)
                .environment(tabOrganizerManager)
        }
        .windowResizability(.contentSize)
        .defaultPosition(.center)
    }

    // MARK: - Application Lifecycle Setup

    /// Wires AI services to runtime dependencies (BrowserManager, MCP, etc.)
    private func setupAIServices() {
        aiService.browserManager = browserManager
        aiService.mcpManager = mcpManager

        let toolExecutor = BrowserToolExecutor(browserManager: browserManager)
        aiService.browserToolExecutor = toolExecutor

        // Start enabled MCP servers
        mcpManager.startEnabledServers(configs: aiConfigService.mcpServers)
    }

    /// Configures application-level dependencies and callbacks when the first window appears.
    ///
    /// This function sets up the following connections:
    /// - AppDelegate ↔ BrowserManager: For app termination cleanup and Sparkle update integration
    /// - WindowRegistry callbacks: Register, close, and activate window state
    /// - Keyboard shortcut manager: Enable global keyboard shortcuts
    ///
    /// TEMPORARY CONNECTIONS (to be removed during refactoring):
    /// - BrowserManager ← WebViewCoordinator: Currently BrowserManager holds a reference to coordinate web views
    /// - BrowserManager ← WindowRegistry: Currently BrowserManager tracks active window state
    ///
    /// These temporary connections exist because BrowserManager is currently a god object.
    /// Future refactoring will eliminate these by:
    /// 1. Moving WebViewCoordinator ownership to per-window state
    /// 2. Moving window management out of BrowserManager entirely
    /// 3. Using pure environment-based dependency injection
    private func setupApplicationLifecycle() {
        // Connect AppDelegate for termination and updates
        appDelegate.browserManager = browserManager
        appDelegate.windowRegistry = windowRegistry
        appDelegate.mcpManager = mcpManager
        appDelegate.drainPendingURLs()
        browserManager.appDelegate = appDelegate

        // TEMPORARY: Wire coordinators to BrowserManager
        // TODO: Remove these connections - coordinators should be independent
        browserManager.webViewCoordinator = webViewCoordinator
        browserManager.windowRegistry = windowRegistry
        browserManager.nookSettings = settingsManager
        browserManager.tabManager.nookSettings = settingsManager
        browserManager.siteRoutingManager.settingsService = settingsManager
        browserManager.siteRoutingManager.browserManager = browserManager
        browserManager.aiService = aiService
        browserManager.aiConfigService = aiConfigService

        // Configure managers that depend on settings
        browserManager.compositorManager.setMode(
            settingsManager.tabManagementMode
        )
        browserManager.contentBlockerManager.setEnabled(
            settingsManager.blockCrossSiteTracking || settingsManager.adBlockerEnabled
        )

        // Apply appearance mode
        applyAppearanceMode(settingsManager.appearanceMode)
        NotificationCenter.default.addObserver(
            forName: .appearanceModeChanged,
            object: nil,
            queue: .main
        ) { [weak settingsManager] _ in
            guard let settings = settingsManager else { return }
            applyAppearanceMode(settings.appearanceMode)
        }

        // Initialize keyboard shortcut manager
        keyboardShortcutManager.setBrowserManager(browserManager)
        browserManager.keyboardShortcutManager = keyboardShortcutManager
        browserManager.mcpManager = mcpManager
        browserManager.tabOrganizerManager = tabOrganizerManager

        // Set up window lifecycle callbacks
        windowRegistry.onWindowRegister = { [weak browserManager] windowState in
            browserManager?.setupWindowState(windowState)
        }
        // Retroactively set up any windows that registered before this callback was set
        // (child .onAppear fires before parent .onAppear in SwiftUI)
        for (_, windowState) in windowRegistry.windows {
            browserManager.setupWindowState(windowState)
        }

        windowRegistry.onWindowClose = {
            [webViewCoordinator, weak browserManager] windowId in
            // Only cleanup if browserManager still exists (it's captured weakly)
            if let browserManager = browserManager {
                webViewCoordinator.cleanupWindow(
                    windowId,
                    tabManager: browserManager.tabManager
                )
                browserManager.splitManager.cleanupWindow(windowId)

                // Clean up incognito window if applicable
                if let windowState = browserManager.windowRegistry?.windows[windowId],
                   windowState.isIncognito {
                    Task {
                        await browserManager.closeIncognitoWindow(windowState)
                    }
                }
            } else {
                // BrowserManager was deallocated - perform minimal cleanup
                // Remove compositor container view to prevent leaks
                webViewCoordinator.removeCompositorContainerView(for: windowId)
            }
        }

        windowRegistry.onActiveWindowChange = {
            [weak browserManager] windowState in
            browserManager?.setActiveWindowState(windowState)
        }
    }
}

// MARK: - Appearance Mode

private func applyAppearanceMode(_ mode: AppearanceMode) {
    switch mode {
    case .system:
        NSApp.appearance = nil  // Follow system
    case .light:
        NSApp.appearance = NSAppearance(named: .aqua)
    case .dark:
        NSApp.appearance = NSAppearance(named: .darkAqua)
    }
}

// MARK: - Window Configuration

/// Configures the window appearance and behavior for Nook browser windows
///
/// This modifier:
/// - Hides the title bar text while keeping native traffic light buttons visible
/// - Sets transparent background for custom window styling
/// - Configures minimum window size
/// - Enables full-size content view for edge-to-edge content
struct BackgroundWindowModifier: NSViewRepresentable {
    func makeNSView(context: Context) -> NSView {
        let view = NSView()
        DispatchQueue.main.async {
            if let window = view.window {
                window.toolbar?.isVisible = false
                window.titlebarAppearsTransparent = true
                window.backgroundColor = .clear
                window.titleVisibility = .hidden
                window.isReleasedWhenClosed = false
                // window.isMovableByWindowBackground = true // Disabled - use SwiftUI-based window drag system instead
                window.isMovable = true
                var mask: NSWindow.StyleMask = [
                    .titled, .closable, .miniaturizable, .resizable,
                    .fullSizeContentView,
                ]
                // Preserve fullScreen flag — removing it outside a transition crashes on macOS 15.5+
                if window.styleMask.contains(.fullScreen) {
                    mask.insert(.fullScreen)
                }
                window.styleMask = mask

                window.minSize = NSSize(width: 470, height: 382)
                window.contentMinSize = NSSize(width: 470, height: 382)

                // Persist and restore window frame (position + size) across launches.
                // setFrameAutosaveName makes macOS automatically save the frame to
                // UserDefaults whenever it changes, so the window size is remembered
                // on close — not just on quit.
                window.setFrameAutosaveName("NookBrowserWindow")
            }
        }
        return view
    }
    func updateNSView(_ nsView: NSView, context: Context) {
        guard let window = nsView.window else { return }
        // Only re-apply if somehow reset (e.g., view transition flash)
        guard !window.titlebarAppearsTransparent else { return }
        window.titlebarAppearsTransparent = true
        window.titleVisibility = .hidden
    }
}


```

## /App/NookCommands.swift

```swift path="/App/NookCommands.swift" 
//
//  NookCommands.swift
//  Nook
//
//  Menu bar commands for the Nook browser application
//

import AppKit
import SwiftUI
import WebKit

struct NookCommands: Commands {
    let browserManager: BrowserManager
    let windowRegistry: WindowRegistry
    let shortcutManager: KeyboardShortcutManager
    let tabOrganizerManager: TabOrganizerManager
    @Environment(\.openWindow) private var openWindow
    @Environment(\.nookSettings) var nookSettings
    @NSApplicationDelegateAdaptor(AppDelegate.self) private var appDelegate

    init(browserManager: BrowserManager, windowRegistry: WindowRegistry, shortcutManager: KeyboardShortcutManager, tabOrganizerManager: TabOrganizerManager) {
        self.browserManager = browserManager
        self.windowRegistry = windowRegistry
        self.shortcutManager = shortcutManager
        self.tabOrganizerManager = tabOrganizerManager
    }

    // MARK: - Dynamic Keyboard Shortcuts

    /// Returns the key equivalent for a given action, or nil if disabled
    private func keyEquivalent(for action: ShortcutAction) -> KeyEquivalent? {
        guard let shortcut = shortcutManager.shortcut(for: action),
              shortcut.isEnabled else { return nil }
        // Handle special keys
        switch shortcut.keyCombination.key.lowercased() {
        case "return", "enter": return .return
        case "escape", "esc": return .escape
        case "delete", "backspace": return .delete
        case "tab": return .tab
        case "space": return .space
        case "up", "uparrow": return .upArrow
        case "down", "downarrow": return .downArrow
        case "left", "leftarrow": return .leftArrow
        case "right", "rightarrow": return .rightArrow
        case "home": return .home
        case "end": return .end
        case "pageup": return .pageUp
        case "pagedown": return .pageDown
        case "clear": return .clear
        default:
            // Handle single character keys
            if shortcut.keyCombination.key.count == 1,
               let char = shortcut.keyCombination.key.first {
                return KeyEquivalent(char)
            }
            return nil
        }
    }

    /// Returns the event modifiers for a given action
    private func eventModifiers(for action: ShortcutAction) -> EventModifiers {
        guard let shortcut = shortcutManager.shortcut(for: action),
              shortcut.isEnabled else { return [] }
        var modifiers: EventModifiers = []
        if shortcut.keyCombination.modifiers.contains(.command) { modifiers.insert(.command) }
        if shortcut.keyCombination.modifiers.contains(.shift) { modifiers.insert(.shift) }
        if shortcut.keyCombination.modifiers.contains(.option) { modifiers.insert(.option) }
        if shortcut.keyCombination.modifiers.contains(.control) { modifiers.insert(.control) }
        return modifiers
    }

    /// View extension to apply dynamic keyboard shortcut if enabled
    private func dynamicShortcut(_ action: ShortcutAction) -> some ViewModifier {
        DynamicShortcutModifier(
            keyEquivalent: keyEquivalent(for: action),
            modifiers: eventModifiers(for: action)
        )
    }

    var body: some Commands {
        CommandGroup(replacing: .newItem) {}
        CommandGroup(replacing: .windowList) {}

        // Replace the native Settings menu item to open our custom sidebar settings window
        CommandGroup(replacing: .appSettings) {
            Button("Settings...") {
                openWindow(id: "nook-settings")
            }
            .keyboardShortcut(",", modifiers: .command)

            Button("Import from another Browser") {
                browserManager.dialogManager.showDialog(
                    BrowserImportDialog(
                        onCancel: {
                            browserManager.dialogManager.closeDialog()
                        }
                    )
                )
            }
        }

        // Replace the standard Quit menu item to route through showQuitDialog(),
        // which respects the "warn before quitting" setting
        CommandGroup(replacing: .appTermination) {
            Button("Quit Nook") {
                browserManager.showQuitDialog()
            }
            .keyboardShortcut("q", modifiers: .command)
        }

        // App Menu Section (under Nook)
        CommandGroup(after: .appInfo) {
            Divider()
            Button("Make Nook Default Browser") {
                browserManager.setAsDefaultBrowser()
            }

            Button("Check for Updates...") {
                appDelegate.updaterController.checkForUpdates(nil)
            }
        }
        

        // Edit Section
        CommandGroup(replacing: .undoRedo) {
            Button("Undo Close Tab") {
                browserManager.undoCloseTab()
            }
            .modifier(dynamicShortcut(.undoCloseTab))

            Button("Reopen Closed Tab") {
                browserManager.undoCloseTab()
            }
            .keyboardShortcut("t", modifiers: [.command, .shift])
        }

        // File Section
        CommandGroup(after: .newItem) {
            Button("New Tab") {
                windowRegistry.activeWindow?.commandPalette?.open()
            }
            .modifier(dynamicShortcut(.newTab))
            Button("New Window") {
                browserManager.createNewWindow()
            }
            .modifier(dynamicShortcut(.newWindow))
            
            Button("New Incognito Window") {
                browserManager.createIncognitoWindow()
            }
            .keyboardShortcut("n", modifiers: [.command, .shift])
            
            Divider()
            Button("Open Command Bar") {
                let currentURL = browserManager.currentTabForActiveWindow()?.url.absoluteString ?? ""
                windowRegistry.activeWindow?.commandPalette?.open(prefill: currentURL, navigateCurrentTab: true)
            }
            .modifier(dynamicShortcut(.focusAddressBar))
            .disabled(browserManager.currentTabForActiveWindow() == nil)

            Button("Copy Current URL") {
                browserManager.copyCurrentURL()
            }
            .modifier(dynamicShortcut(.copyCurrentURL))
            .disabled(browserManager.currentTabForActiveWindow() == nil)
        }

        // Sidebar commands
        CommandGroup(after: .sidebar) {
            Button("Toggle Sidebar") {
                browserManager.toggleSidebar()
            }
            .modifier(dynamicShortcut(.toggleSidebar))

            Button("Toggle AI Assistant") {
                browserManager.toggleAISidebar()
            }
            .modifier(dynamicShortcut(.toggleAIAssistant))
            .disabled(!nookSettings.showAIAssistant)

            Button("Toggle Picture in Picture") {
                browserManager.requestPiPForCurrentTabInActiveWindow()
            }
            .modifier(dynamicShortcut(.togglePictureInPicture))
            .disabled(
                browserManager.currentTabForActiveWindow() == nil
                    || !(browserManager.currentTabHasVideoContent()
                        || browserManager.currentTabHasPiPActive())
            )

            Divider()

            Button("Organize Tabs") {
                let targetSpace =
                    windowRegistry.activeWindow?.currentSpaceId.flatMap { id in
                        browserManager.tabManager.spaces.first(where: { $0.id == id })
                    } ?? browserManager.tabManager.currentSpace
                if let space = targetSpace {
                    Task {
                        await tabOrganizerManager.organizeTabs(
                            in: space,
                            using: browserManager.tabManager
                        )
                    }
                }
            }
            .modifier(dynamicShortcut(.organizeTabs))
            .disabled(
                tabOrganizerManager.isOrganizing
                    || browserManager.tabManager.currentSpace == nil
            )
        }

        // View commands
        CommandGroup(after: .windowSize) {

            Button("Find in Page") {
                browserManager.showFindBar()
            }
            .modifier(dynamicShortcut(.findInPage))
            .disabled(browserManager.currentTabForActiveWindow() == nil)

            Button("Reload Page") {
                browserManager.refreshCurrentTabInActiveWindow()
            }
            .modifier(dynamicShortcut(.refresh))
            .disabled(browserManager.currentTabForActiveWindow() == nil)

            Divider()

            Button("Zoom In") {
                browserManager.zoomInCurrentTab()
            }
            .modifier(dynamicShortcut(.zoomIn))
            .disabled(browserManager.currentTabForActiveWindow() == nil)

            Button("Zoom Out") {
                browserManager.zoomOutCurrentTab()
            }
            .modifier(dynamicShortcut(.zoomOut))
            .disabled(browserManager.currentTabForActiveWindow() == nil)

            Button("Actual Size") {
                browserManager.resetZoomCurrentTab()
            }
            .modifier(dynamicShortcut(.actualSize))
            .disabled(browserManager.currentTabForActiveWindow() == nil)

            Divider()

            Button("Hard Reload (Ignore Cache)") {
                browserManager.hardReloadCurrentPage()
            }
            .modifier(dynamicShortcut(.hardReload))
            .disabled(browserManager.currentTabForActiveWindow() == nil)

            Divider()

            Button("Web Inspector") {
                browserManager.openWebInspector()
            }
            .modifier(dynamicShortcut(.openDevTools))
            .disabled(browserManager.currentTabForActiveWindow() == nil)

            Divider()

            Button(browserManager.currentTabIsMuted() ? "Unmute Audio" : "Mute Audio") {
                browserManager.toggleMuteCurrentTabInActiveWindow()
            }
            .modifier(dynamicShortcut(.muteUnmuteAudio))
            .disabled(
                browserManager.currentTabForActiveWindow() == nil
                    || !browserManager.currentTabHasAudioContent())
        }

        Group {
            CommandMenu("Privacy") {
                Menu("Clear Cookies") {
                    Button("Clear Cookies for Current Site") {
                        browserManager.clearCurrentPageCookies()
                    }
                    .disabled(browserManager.currentTabForActiveWindow()?.url.host == nil)

                    Button("Clear Expired Cookies") {
                        browserManager.clearExpiredCookies()
                    }

                    Divider()

                    Button("Clear All Cookies") {
                        browserManager.clearAllCookies()
                    }

                    Divider()

                    Button("Clear Third-Party Cookies") {
                        browserManager.clearThirdPartyCookies()
                    }

                    Button("Clear High-Risk Cookies") {
                        browserManager.clearHighRiskCookies()
                    }
                }

                Menu("Clear Cache") {
                    Button("Clear Cache for Current Site") {
                        browserManager.clearCurrentPageCache()
                    }
                    .disabled(browserManager.currentTabForActiveWindow()?.url.host == nil)

                    Button("Clear Stale Cache") {
                        browserManager.clearStaleCache()
                    }

                    Button("Clear Disk Cache") {
                        browserManager.clearDiskCache()
                    }

                    Button("Clear Memory Cache") {
                        browserManager.clearMemoryCache()
                    }

                    Divider()

                    Button("Clear All Cache") {
                        browserManager.clearAllCache()
                    }

                    Divider()

                    Button("Clear Personal Data Cache") {
                        browserManager.clearPersonalDataCache()
                    }

                    Button("Clear Favicon Cache") {
                        browserManager.clearFaviconCache()
                    }
                }

                Divider()

                Button("Privacy Cleanup") {
                    browserManager.performPrivacyCleanup()
                }

                Button("Clear Browsing History") {
                    browserManager.historyManager.clearHistory()
                }

                Button("Clear All Website Data") {
                    Task {
                        let dataStore = WKWebsiteDataStore.default()
                        let dataTypes = WKWebsiteDataStore.allWebsiteDataTypes()
                        await dataStore.removeData(ofTypes: dataTypes, modifiedSince: Date.distantPast)
                    }
                }
            }

            if #available(macOS 15.5, *) {
                CommandMenu("Extensions") {
                    Button("Toggle Extension Library") {
                        browserManager.toggleExtensionLibrary()
                    }
                    .keyboardShortcut("e", modifiers: [.command, .shift])

                    Divider()

                    Button("Install Extension...") {
                        browserManager.showExtensionInstallDialog()
                    }
                    .modifier(dynamicShortcut(.installExtension))

                    Button("Manage Extensions...") {
                        nookSettings.currentSettingsTab = .extensions
                        openWindow(id: "nook-settings")
                    }

                    Divider()

                    Button("Chrome Web Store") {
                        if let tab = browserManager.currentTabForActiveWindow() {
                            tab.loadURL("https://chromewebstore.google.com")
                        }
                    }

                    #if DEBUG
                    Divider()
                    Button("Open Popup Console") {
                        browserManager.extensionManager?.showPopupConsole()
                    }
                    #endif
                }
            }

            CommandMenu("Appearance") {
                Button("Customize Space Gradient...") {
                    browserManager.showGradientEditor()
                }
                .modifier(dynamicShortcut(.customizeSpaceGradient))
                .disabled(browserManager.tabManager.currentSpace == nil)

                Divider()

                Button("Create Boosts") {
                    browserManager.showBoostsDialog()
                }
                .modifier(dynamicShortcut(.createBoost))
                .disabled(browserManager.currentTabForActiveWindow() == nil)
            }
        }
    }
}

// MARK: - Dynamic Shortcut Modifier

/// View modifier that conditionally applies a keyboard shortcut based on user preferences
struct DynamicShortcutModifier: ViewModifier {
    let keyEquivalent: KeyEquivalent?
    let modifiers: EventModifiers

    func body(content: Content) -> some View {
        if let keyEquivalent = keyEquivalent {
            content.keyboardShortcut(keyEquivalent, modifiers: modifiers)
        } else {
            content
        }
    }
}

```

## /App/Window/WindowView.swift

```swift path="/App/Window/WindowView.swift" 
//
//  WindowView.swift
//  Nook
//
//  Created by Maciek Bagiński on 30/07/2025.
//  Updated by Aether Aurelia on 15/11/2025.
//

import SwiftUI

/// Main window view that orchestrates the browser UI layout
struct WindowView: View {
    @EnvironmentObject var browserManager: BrowserManager
    @EnvironmentObject var tabManager: TabManager
    @Environment(BrowserWindowState.self) private var windowState
    @Environment(CommandPalette.self) private var commandPalette
    @Environment(WindowRegistry.self) private var windowRegistry
    @Environment(AIService.self) private var aiService
    @Environment(TabOrganizerManager.self) private var tabOrganizerManager
    @Environment(\.nookSettings) var nookSettings
    @StateObject private var hoverSidebarManager = HoverSidebarManager()
    @Environment(\.colorScheme) var colorScheme
    
    var body: some View {
        ZStack {
            WindowBackground()
                .contextMenu {
                    Button("Customize Space Gradient...") {
                        browserManager.showGradientEditor()
                    }
                    .disabled(tabManager.currentSpace == nil)
                }

            SidebarWebViewStack()

            CommandPaletteView()
            DialogView()

            // Peek overlay for external link previews
            PeekOverlayView()

            // Find bar - always rendered (24/7), visibility controlled via opacity
            FindBarView(findManager: browserManager.findManager)
                .zIndex(10000)

        }
        // System notification toasts - top trailing corner
        .overlay(alignment: .topTrailing) {
            VStack(spacing: 8) {
                // Profile switch toast
                if windowState.isShowingProfileSwitchToast,
                   let toast = windowState.profileSwitchToast
                {
                    ProfileSwitchToastView(toast: toast)
                        .environment(windowState)
                        .environmentObject(browserManager)
                }

                // Tab closure toast
                if browserManager.showTabClosureToast && browserManager.tabClosureToastCount > 0 {
                    TabClosureToast()
                        .environmentObject(browserManager)
                }

                // Copy URL toast
                if windowState.isShowingCopyURLToast {
                    CopyURLToast()
                        .environment(windowState)
                }
                
                // Shortcut conflict toast
                if windowState.isShowingShortcutConflictToast,
                   let conflictInfo = windowState.shortcutConflictInfo
                {
                    ShortcutConflictToast(conflictInfo: conflictInfo)
                        .environment(windowState)
                }
            }
            .padding(10)
            // Animate toast insertions/removals
            .animation(.smooth(duration: 0.25), value: windowState.isShowingProfileSwitchToast)
            .animation(.smooth(duration: 0.25), value: browserManager.showTabClosureToast)
            .animation(.smooth(duration: 0.25), value: windowState.isShowingCopyURLToast)
            .animation(.smooth(duration: 0.25), value: windowState.isShowingShortcutConflictToast)
        }
        // Zoom control popup - separate from system toasts
        .overlay(alignment: .topTrailing) {
            if browserManager.shouldShowZoomPopup {
                ZoomPopupView(
                    zoomManager: browserManager.zoomManager,
                    onZoomIn: { browserManager.zoomInCurrentTab() },
                    onZoomOut: { browserManager.zoomOutCurrentTab() },
                    onZoomReset: { browserManager.resetZoomCurrentTab() },
                    onZoomPresetSelected: { zoomLevel in browserManager.applyZoomLevel(zoomLevel) },
                    onDismiss: { browserManager.shouldShowZoomPopup = false }
                )
                .transition(.scale(scale: 0.0, anchor: .top))
                .animation(.spring(response: 0.5, dampingFraction: 0.8), value: browserManager.shouldShowZoomPopup)
                .onTapGesture {
                    browserManager.shouldShowZoomPopup = false
                }
                .padding(10)
            }
        }
        // Lifecycle management
        .onAppear {
            hoverSidebarManager.attach(browserManager: browserManager)
            hoverSidebarManager.windowRegistry = windowRegistry
            hoverSidebarManager.nookSettings = nookSettings
            hoverSidebarManager.start()
            windowState.hoverSidebarManager = hoverSidebarManager
        }
        .onDisappear {
            hoverSidebarManager.stop()
        }
        // Handle shortcut conflict notifications
        .onReceive(NotificationCenter.default.publisher(for: .shortcutConflictDetected)) { notification in
            if let conflictInfo = notification.userInfo?["conflictInfo"] as? ShortcutConflictInfo,
               conflictInfo.windowId == windowState.id {
                windowState.shortcutConflictInfo = conflictInfo
                windowState.isShowingShortcutConflictToast = true
                
                // Auto-dismiss after 1.5 seconds (slightly longer than the 1s timeout)
                DispatchQueue.main.asyncAfter(deadline: .now() + 1.5) {
                    if windowState.shortcutConflictInfo?.timestamp == conflictInfo.timestamp {
                        windowState.isShowingShortcutConflictToast = false
                    }
                }
            }
        }
        // Handle shortcut conflict dismissal
        .onReceive(NotificationCenter.default.publisher(for: .shortcutConflictDismissed)) { notification in
            if let windowId = notification.userInfo?["windowId"] as? UUID,
               windowId == windowState.id {
                windowState.isShowingShortcutConflictToast = false
            }
        }
        // Handle organize tabs notification from keyboard shortcut manager
        .onReceive(NotificationCenter.default.publisher(for: .organizeTabsRequested)) { _ in
            guard windowRegistry.activeWindow?.id == windowState.id else { return }
            let targetSpace =
                windowState.currentSpaceId.flatMap { id in
                    browserManager.tabManager.spaces.first(where: { $0.id == id })
                } ?? browserManager.tabManager.currentSpace
            if let space = targetSpace {
                Task {
                    await tabOrganizerManager.organizeTabs(
                        in: space,
                        using: browserManager.tabManager
                    )
                }
            }
        }
        .environmentObject(browserManager)
        .environmentObject(browserManager.gradientColorManager)
        .environmentObject(browserManager.splitManager)
        .environmentObject(hoverSidebarManager)
        .preferredColorScheme(resolvedColorScheme)
    }

    private var resolvedColorScheme: ColorScheme? {
        switch nookSettings.appearanceMode {
        case .light: return .light
        case .dark: return .dark
        case .system: return nil  // Follow system appearance
        }
    }

    // MARK: - Layout Components

    @ViewBuilder
    private func WindowBackground() -> some View {
        ZStack {
            
            BlurEffectView(material: nookSettings.currentMaterial, state: .followsWindowActiveState)
                .frame(maxWidth: .infinity, maxHeight: .infinity)
            SpaceGradientBackgroundView()


        }
        .backgroundDraggable()
        .environment(windowState)
    }

    @ViewBuilder
    private func SidebarWebViewStack() -> some View {
        let aiVisible = windowState.isSidebarAIChatVisible
        let sidebarVisible = windowState.isSidebarVisible
        let sidebarOnLeft = nookSettings.sidebarPosition == .left

        // Fixed-order layout: [LeftSpacer] [WebContent] [RightSpacer]
        // WebContent always stays in the middle with stable view identity.
        // Spacer widths push content based on what's on each side.
        let leftWidth: CGFloat = {
            if sidebarOnLeft {
                return sidebarVisible ? windowState.sidebarWidth : 0
            } else {
                return aiVisible ? windowState.aiSidebarWidth : 0
            }
        }()

        let rightWidth: CGFloat = {
            if sidebarOnLeft {
                return aiVisible ? windowState.aiSidebarWidth : 0
            } else {
                return sidebarVisible ? windowState.sidebarWidth : 0
            }
        }()

        // Determine edge padding: remove padding when sidebar/AI is visible on that side
        let hasLeftContent = (sidebarOnLeft && sidebarVisible) || (!sidebarOnLeft && aiVisible)
        let hasRightContent = (!sidebarOnLeft && sidebarVisible) || (sidebarOnLeft && aiVisible)

        ZStack {
            // When pinned: sidebar sits below web content (zIndex 0) so position
            // swaps slide it under. When floating: above (zIndex 2) so it hovers.
            UnifiedSidebar()
                .zIndex(windowState.isSidebarVisible ? 0 : 2)

            if aiVisible {
                AISidebar()
                    .frame(maxWidth: .infinity, maxHeight: .infinity,
                           alignment: sidebarOnLeft ? .trailing : .leading)
                    .zIndex(0)
            }

            // Web content column — above pinned sidebars so they slide under it
            HStack(spacing: 0) {
                Color.clear
                    .frame(width: leftWidth)
                    .allowsHitTesting(false)
                WebContent()
                Color.clear
                    .frame(width: rightWidth)
                    .allowsHitTesting(false)
            }
            .padding(.leading, hasLeftContent ? 0 : 8)
            .padding(.trailing, hasRightContent ? 0 : 8)
            .zIndex(1)
        }
        .animation(.smooth(duration: 0.3), value: nookSettings.sidebarPosition)
    }

    /// Single sidebar instance rendered as an overlay — always the same view identity.
    /// When floating, uses offset to slide in/out (preserving view identity without removal).
    @ViewBuilder
    private func UnifiedSidebar() -> some View {
        let isPinned = windowState.isSidebarVisible
        let isFloatingVisible = hoverSidebarManager.isOverlayVisible && !isPinned
        let shouldShow = isPinned || isFloatingVisible
        let onLeft = nookSettings.sidebarPosition == .left
        // Slide offset: push sidebar fully off-screen in the appropriate direction
        let slideOffset: CGFloat = {
            if isPinned || isFloatingVisible { return 0 }
            // Slide out to the left or right edge
            return onLeft ? -(windowState.sidebarWidth + 14) : (windowState.sidebarWidth + 14)
        }()

        ZStack(alignment: onLeft ? .leading : .trailing) {
            // Edge hover trigger zone — always present when sidebar is unpinned
            if !isPinned {
                Color.clear
                    .frame(width: hoverSidebarManager.triggerWidth)
                    .contentShape(Rectangle())
                    .onHover { isIn in
                        if isIn && !windowState.isSidebarVisible {
                            withAnimation(.easeInOut(duration: 0.12)) {
                                hoverSidebarManager.isOverlayVisible = true
                            }
                        }
                        NSCursor.arrow.set()
                    }
                    .frame(maxWidth: .infinity, maxHeight: .infinity,
                           alignment: onLeft ? .leading : .trailing)
            }

            // The single sidebar panel — slides in/out when floating, always visible when pinned
            sidebarPanel(isPinned: isPinned)
                .offset(x: isPinned ? 0 : slideOffset)
                .allowsHitTesting(shouldShow)
        }
        .frame(maxWidth: .infinity, maxHeight: .infinity,
               alignment: onLeft ? .leading : .trailing)
        .animation(.easeInOut(duration: 0.15), value: isFloatingVisible)
        .animation(.smooth(duration: 0.3), value: nookSettings.sidebarPosition)
        // Briefly flash the floating sidebar on its new side after a position swap
        .onChange(of: nookSettings.sidebarPosition) { _, _ in
            guard !isPinned else { return }
            hoverSidebarManager.peekOverlay(for: 2.0)
        }
    }

    /// Wraps `SpacesSideBarView` with mode-dependent styling.
    @ViewBuilder
    private func sidebarPanel(isPinned: Bool) -> some View {
        let cornerRadius: CGFloat = isPinned ? 0 : 12
        let inset: CGFloat = isPinned ? 0 : 7
        let resizeHandleAlignment: Alignment = nookSettings.sidebarPosition == .left ? .trailing : .leading

        SpacesSideBarView()
            .frame(width: windowState.sidebarWidth)
            .frame(maxHeight: .infinity)
            .overlay(alignment: resizeHandleAlignment) {
                SidebarResizeView()
                    .frame(maxHeight: .infinity)
                    .environmentObject(browserManager)
                    .environment(windowState)
                    .zIndex(2000)
                    .opacity(isPinned ? 1 : 0)
                    .allowsHitTesting(isPinned)
            }
            .background {
                if !isPinned {
                    SpaceGradientBackgroundView()
                        .environmentObject(browserManager)
                        .environmentObject(browserManager.gradientColorManager)
                        .environment(windowState)
                        .clipShape(.rect(cornerRadius: cornerRadius))

                    Rectangle()
                        .fill(Color.clear)
                        .universalGlassEffect(.regular.tint(Color(.windowBackgroundColor).opacity(0.35)), in: .rect(cornerRadius: cornerRadius))
                }
            }
            .padding(nookSettings.sidebarPosition == .left ? .leading : .trailing, inset)
            .padding(.vertical, inset)
            .environmentObject(browserManager)
            .environment(windowState)
            .environment(commandPalette)
            .environmentObject(browserManager.gradientColorManager)
    }

    @ViewBuilder
    private func WebContent() -> some View {
        let cornerRadius: CGFloat = {
            if #available(macOS 26.0, *) {
                return 8
            } else {
                return 8
            }
        }()
        
        let hasTopBar = nookSettings.topBarAddressView
        
        ZStack(alignment: .top) {
            VStack(spacing: 0) {
                if hasTopBar {
                    WebsiteLoadingIndicator()
                        .zIndex(3000)
                    
                    TopBarView()
                        .environmentObject(browserManager)
                        .environment(windowState)
                        .zIndex(2500)
                } else {
                    WebsiteLoadingIndicator()
                }
                
                WebsiteView()
                    .zIndex(2000)
            }
            
            // Shadow shape positioned behind both top bar and webview
            // The webview will block the bottom shadow, leaving only top/left/right shadows visible
            if hasTopBar {
                UnevenRoundedRectangle(
                    topLeadingRadius: cornerRadius + 1,
                    bottomLeadingRadius: 0,
                    bottomTrailingRadius: 0,
                    topTrailingRadius: cornerRadius + 1,
                    style: .continuous
                )
                .frame(height: TopBarMetrics.height)
                .frame(maxWidth: .infinity)
                .offset(y: 8)
                .shadow(color: Color.black.opacity(0.3), radius: 4, x: 0, y: 0)
                .allowsHitTesting(false)
                .zIndex(-1)
            }
        }
        .overlay {
            if aiService.isExecutingTools {
                ToolExecutionGlowView()
                    .transition(.opacity.animation(.easeInOut(duration: 0.3)))
                    .allowsHitTesting(false)
            }
        }
        .padding(.bottom, 8)
        .frame(maxWidth: .infinity, maxHeight: .infinity)
    }

    @ViewBuilder
    private func AISidebar() -> some View {
        let handleAlignment: Alignment = nookSettings.sidebarPosition == .left ? .leading : .trailing
        
        SidebarAIChat()
            .frame(width: windowState.aiSidebarWidth)
            .overlay(alignment: handleAlignment) {
                AISidebarResizeView()
                    .frame(maxHeight: .infinity)
                    .environmentObject(browserManager)
                    .environment(windowState)
            }
            .transition(
                .move(edge: nookSettings.sidebarPosition == .left ? .trailing : .leading)
                .combined(with: .opacity)
            )
            .environmentObject(browserManager)
            .environment(windowState)
            .environment(nookSettings)
    }

    private func websiteColumnClipShape(cornerRadius: CGFloat, hasTopBar: Bool) -> AnyShape {
        if hasTopBar {
            return AnyShape(UnevenRoundedRectangle(
                topLeadingRadius: 0,
                bottomLeadingRadius: cornerRadius,
                bottomTrailingRadius: cornerRadius,
                topTrailingRadius: 0,
                style: .continuous
            ))
        } else {
            return AnyShape(RoundedRectangle(cornerRadius: cornerRadius, style: .continuous))
        }
    }
}

// MARK: - Profile Switch Toast View
private struct ProfileSwitchToastView: View {
    let toast: BrowserManager.ProfileSwitchToast
    @Environment(BrowserWindowState.self) private var windowState
    @EnvironmentObject var browserManager: BrowserManager

    var body: some View {
        ToastView {
            HStack {
                Text("Switched to \(toast.toProfile.name)")
                    .font(.system(size: 12, weight: .medium))
                    .foregroundStyle(.white)
                Image(systemName: "person.crop.circle")
                    .font(.system(size: 12, weight: .medium))
                    .foregroundStyle(.white)
                    .frame(width: 14, height: 14)
                    .padding(4)
                    .background(Color.white.opacity(0.2))
                    .clipShape(RoundedRectangle(cornerRadius: 6))
                    .overlay {
                        RoundedRectangle(cornerRadius: 6)
                            .stroke(.white.opacity(0.4), lineWidth: 1)
                    }
            }
        }
        .transition(.toast)
        .onAppear {
            // Auto-dismiss after 2 seconds
            DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
                browserManager.hideProfileSwitchToast(for: windowState)
            }
        }
        .onTapGesture {
            browserManager.hideProfileSwitchToast(for: windowState)
        }
    }
}

```

## /CLAUDE.md

# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Project Overview

Nook is a fast, minimal macOS browser with sidebar-first design. Built with Swift 6, SwiftUI, and WKWebView. Requires macOS 15.5+ and Xcode to build.

## Build & Run

```bash
# Open in Xcode (single scheme: "Nook")
open Nook.xcodeproj

# Build from command line
xcodebuild -scheme Nook -configuration Debug -arch arm64 -derivedDataPath build

# Release build (universal)
xcodebuild -scheme Nook -configuration Release -arch arm64 -arch x86_64 -derivedDataPath build
```

You must set your personal Development Team in Xcode Signing settings to build locally.

**Test target**: `NookUITests` (UI tests only, run via Xcode or `xcodebuild test -scheme Nook`).

**No SPM**: All dependencies are embedded locally in `Nook/ThirdParty/` — no `swift package resolve` needed.

## Git Workflow

- **PRs must target `dev`**, not `main` (enforced by CI). Branch from `dev` for all work.
- Releases are tagged `v*` on `main`, triggering notarized DMG builds via GitHub Actions.
- AI assistance in contributions must be disclosed per CONTRIBUTING.md.

## Architecture

### Manager-Based Pattern

The app uses specialized **Managers** for each feature domain, coordinated through environment injection:

- **BrowserManager** (`Nook/Managers/BrowserManager/`) — Current "god object" (~2800 lines) that connects all managers. Being refactored toward independent, environment-injected managers.
- **TabManager** — Tab lifecycle, persistence (atomic snapshots via `PersistenceActor`), spaces, folders, pin management.
- **ProfileManager** — Persistent profiles (each with isolated `WKWebsiteDataStore`) and ephemeral/incognito profiles using `WKWebsiteDataStore.nonPersistent()`.
- **ExtensionManager** — `WKWebExtensionController` integration (macOS 15.4+). Singleton pattern.
- **WindowRegistry** — Multi-window state tracking. Single source of truth for all open windows.
- **WebViewCoordinator** — WebView pool management for multi-window tab display.

### State Management

- **`@Observable`** (Swift Observation): `Profile`, `Space`, `Tab`, `BrowserWindowState`, `WebViewCoordinator`, `WindowRegistry`
- **`@Published` / `ObservableObject`** (Combine): `BrowserManager`, `Tab` (dual — uses both patterns), `ExtensionManager`
- **SwiftData**: Persistence layer for `SpaceEntity`, `ProfileEntity`, `TabEntity`, `FolderEntity`, `HistoryEntity`, `ExtensionEntity`
- **All state is `@MainActor`** confined for thread safety.

### App Entry & Window Hierarchy

```
NookApp.swift          — @main entry, creates WindowGroup scene
  └─ ContentView.swift — Per-window container, registers with WindowRegistry
       └─ WindowView   — Main browser: Sidebar + WebsiteView + TopBar + StatusBar
```

**Environment injection flow**: `NookApp` creates `BrowserManager`, `WindowRegistry`, `WebViewCoordinator`, `NookSettingsService` and injects them as `@EnvironmentObject` / `@Environment`. Each window gets its own `BrowserWindowState`.

### Top-Level Modules

| Directory | Purpose |
|-----------|---------|
| `App/` | Entry point, AppDelegate, ContentView, window management, NookCommands |
| `Nook/Managers/` | ~30 feature managers (business logic) |
| `Nook/Models/` | Data models and entities |
| `Nook/Components/` | SwiftUI view components |
| `Nook/Protocols/` | Protocol definitions (e.g., `TabListDataSource`) |
| `Nook/Utils/` | Utilities, WebKit extensions, Metal shaders, debug tools |
| `Nook/ThirdParty/` | Embedded dependencies (BigUIPaging, HTSymbolHook, MuteableWKWebView, swift-atomics, swift-numerics) |
| `Settings/` | Settings module |
| `CommandPalette/` | Command palette UI |
| `UI/` | Shared UI components |
| `Navigation/` | Navigation models |

## Extension System (WKWebExtension, macOS 15.4+)

The extension system is the most complex subsystem. All extension code requires `@available(macOS 15.4, *)` guards. Content script injection specifically requires macOS 15.5+.

### Key Files

| File | Purpose |
|------|---------|
| `Nook/Managers/ExtensionManager/ExtensionManager.swift` | Core manager (~3800 lines), singleton, handles full lifecycle |
| `Nook/Managers/ExtensionManager/ExtensionBridge.swift` | `WKWebExtensionTab` / `WKWebExtensionWindow` protocol adapters |
| `Nook/Models/Extension/ExtensionModels.swift` | `ExtensionEntity` (SwiftData) + `InstalledExtension` runtime model |
| `Nook/Models/BrowserConfig/BrowserConfig.swift` | Shared `WKWebViewConfiguration` factory — extension controller lives here |
| `Nook/Components/Extensions/ExtensionActionView.swift` | Toolbar buttons, popup anchor positioning |
| `Nook/Components/Extensions/ExtensionPermissionView.swift` | Permission grant/deny dialogs |
| `Nook/Components/Extensions/PopupConsoleWindow.swift` | Debug console for extension popups |
| `Nook/Utils/ExtensionUtils.swift` | Manifest validation, version checks |

### Critical: WebView Config Derivation

Tab webview configs **MUST** derive from the same `WKWebViewConfiguration` that the `WKWebExtensionController` was configured with (via `.copy()`). Creating a fresh `WKWebViewConfiguration()` and just setting `webExtensionController` on it is **NOT** enough — WebKit needs the config to share the same process pool / internal state. See `BrowserConfig.swift:webViewConfiguration(for:)`.

The chain: `BrowserConfig.shared.webViewConfiguration` (base) → ExtensionManager sets `.webExtensionController` on it → `webViewConfiguration(for: profile)` calls `.copy()` + sets profile-specific data store → tab gets that derived config.

### Installation Flow

Supported formats: `.zip`, `.appex` (Safari extension bundle), `.app` (scans `Contents/PlugIns/` for `.appex`), bare directories.

1. Extract/resolve source to get `manifest.json`
2. `ExtensionUtils.validateManifest()` — checks required fields
3. MV3 validation — verifies `background.service_worker` exists
4. `patchManifestForWebKit()` — patches world isolation, injects externally_connectable bridge
5. Create temporary `WKWebExtension` to get `uniqueIdentifier`
6. Move to `~/Library/Application Support/Nook/Extensions/{extensionId}/`
7. Grant ALL manifest permissions + host_permissions at install time (Chrome-like model)
8. Load background service worker immediately
9. Extract icon (128/64/48/32/16px from manifest icons), resolve `__MSG_key__` locale strings

### Externally Connectable Bridge

**Problem**: Pages like `account.proton.me` call `browser.runtime.sendMessage(SAFARI_EXT_ID, msg)` but Safari extension IDs don't match WKWebExtension IDs.

**Solution** (`setupExternallyConnectableBridge`): Two-layer bridge injected as content scripts:
- **PAGE world script**: Wraps `browser.runtime.sendMessage()` and `.connect()`, relays via `window.postMessage()` to the isolated world
- **ISOLATED world script** (`nook_bridge.js`): Receives postMessages, calls the real `browser.runtime.sendMessage()`, forwards responses back

`patchManifestForWebKit()` auto-injects the bridge content script entry into `manifest.json` when `externally_connectable` is present.

### Extension Bridge (ExtensionBridge.swift)

- **`ExtensionWindowAdapter`** implements `WKWebExtensionWindow`: exposes active tab, tab list, window state (minimized/maximized/fullscreen), focus/close operations, privacy status.
- **`ExtensionTabAdapter`** implements `WKWebExtensionTab`: exposes url, title, selection state, loading, pinned, muted, audio state. Returns `tab.assignedWebView` (does NOT trigger lazy init). Stable adapters cached in `tabAdapters` dictionary by `Tab.id`.

### Tab ↔ Extension Notification

Tab notifies the extension system after webview creation:
```
Tab.setupWebView()
  → ExtensionManager.shared.notifyTabOpened(tab)  // controller.didOpenTab(adapter)
  → If active: notifyTabActivated()                // controller.didActivateTab(adapter)
  → tab.didNotifyOpenToExtensions = true
```

### Permission Model

- **Install-time**: ALL manifest `permissions` + `host_permissions` auto-granted (matching Chrome behavior)
- **On load (existing extensions)**: Grants both requested + optional permissions/match patterns, enables Web Inspector
- **Runtime** (`chrome.permissions.request`): Triggers `ExtensionPermissionView` dialog via delegate

### Storage Isolation

- Extensions installed globally (`~/Library/Application Support/Nook/Extensions/{id}/`)
- Runtime storage (`chrome.storage.*`, cookies, indexedDB) isolated per profile via separate `WKWebsiteDataStore`
- On profile switch: `controller.configuration.defaultWebsiteDataStore` updated to profile-specific store

### Native Messaging

Looks up host manifests in order: `~/Library/Application Support/Nook/NativeMessagingHosts/`, then Chrome, Chromium, Edge, Brave, Mozilla paths. Protocol: 4-byte native-endian length prefix + JSON. Supports single-shot (5s timeout) and long-lived `MessagePort` connections.

### Delegate Methods (WKWebExtensionControllerDelegate)

Key delegate implementations in ExtensionManager:
- **Action popup**: Grants permissions, wakes MV3 service worker, positions popover via registered anchor views
- **Open tab/window**: Creates tabs for extension pages, handles OAuth popup flows
- **Options page**: Resolves URL from manifest (`options_ui.page` / `options_page`), opens in separate NSWindow with extension's webViewConfiguration. Includes path traversal protection.
- **Permission prompts**: `promptForPermissions()` and `promptForPermissionToAccess()` for runtime permission requests

### Diagnostics

- `probeBackgroundHealth()` — Runs at +3s and +8s after background load; uses KVC to access `_backgroundWebView` and evaluates capability probe (available APIs, permissions, errors)
- `diagnoseExtensionState()` — Full diagnostic on content scripts + messaging per extension
- Memory debug logging uses `🔍 [MEMDEBUG]` prefix

## Key Patterns

- **Lazy WebView**: `Tab.webView` is lazily initialized on first access. Tabs can exist without a loaded webview to save memory.
- **Multi-window webviews**: Same tab shown in multiple windows gets separate webview instances managed by `WebViewCoordinator`. Primary window owns the "real" webview; others get clones.
- **Profile data isolation**: Each `Profile` owns a unique `WKWebsiteDataStore`. Ephemeral profiles use non-persistent stores that are destroyed on window close.
- **Atomic persistence**: `TabManager` uses a Swift `actor` (`PersistenceActor`) for coalesced, atomic snapshot writes with backup recovery.


## /CODE_OF_CONDUCT.md

# Code of Conduct

## Our Commitment

We are committed to providing a welcoming and inclusive environment for all contributors to Nook, regardless of experience level, background, or identity.

Nook is built by volunteers who contribute their time and expertise out of passion for creating great software. We appreciate everyone who takes time to contribute to making Nook better.

## Expected Behavior

- Be respectful, welcoming, and considerate in all interactions
- Discuss ideas, give/receive constructive criticism gracefully
- Focus on what's best for the community and project
- Show empathy toward other community members
- Help create a positive environment for learning and collaboration

## Unacceptable Behavior

- Harassment, discrimination, or intimidation of any kind
- Offensive, derogatory, or discriminatory comments
- Personal attacks or trolling
- Publishing others' private information without permission
- Spam or off-topic discussions
- Any conduct that would be inappropriate in a professional setting

## Scope

This Code of Conduct applies to all project spaces, including:
- GitHub repository (issues, PRs, discussions)
- Discord community
- Any other official Nook communication channels

## Enforcement

Project maintainers are responsible for clarifying standards and will take appropriate action in response to violations. This may include:
- Warning the individual
- Temporary or permanent ban from project spaces (GitHub, Discord, etc.)
- Reporting to appropriate platforms (GitHub, Discord, etc.)

## Reporting

If you experience or witness unacceptable behavior, please report it by:
- Opening a GitHub issue (for public matters)
- Contacting maintainers directly on Discord

All reports will be handled confidentially and reviewed promptly.

## Attribution

This Code of Conduct is adapted from the [Contributor Covenant](https://www.contributor-covenant.org/), version 2.1.

---

By participating in the Nook community, you agree to abide by this Code of Conduct.


## /CONTRIBUTING.md

# Contributing to Nook

Thank you for your interest in contributing to Nook! This document provides guidelines and instructions for contributing to the project.

## AI Assistance Notice

If you are using any kind of AI assistance to contribute, this must be disclosed in your pull request, commit, or issue, including the extent to which AI assistance was used (e.g. docs only vs. code generation). If PR responses, commit messages, or comments are being generated by an AI, please disclose that as well.

As a small exception, trivial tab-completion doesn't need to be disclosed, so long as it is limited to single keywords or short phrases.

When using AI assistance, we expect contributors to understand the code that is produced and be able to answer critical questions about it. It isn't a maintainer's job to review a PR so broken that it requires significant rework to be acceptable.

Please be respectful to maintainers and disclose AI assistance.

## 🚀 Development Setup

1. **Clone the repository**
   ```bash
   git clone https://github.com/yourusername/Nook.git
   cd Nook
   ```

2. **Switch to the dev branch**
   ```bash
   git checkout dev
   ```

3. **Open in Xcode**
   ```bash
   open Nook.xcodeproj
   ```

4. **Build and run**
   - Select your target device/simulator
   - Press `Cmd + R` to build and run

## 📝 Code Style & Standards

### Conventions

- Follow existing SwiftUI/AppKit patterns in the codebase
- Use the established file organization structure
- Prefer modern Swift/SwiftUI APIs when possible

### Compatibility & OS Versions

- **Maintain backward compatibility**: The current minimum deployment target is macOS 15.5
- **New features requiring newer OS versions** should use `@available` or `#if available` checks to preserve compatibility
- **Discuss before raising minimum version**: If a feature would significantly benefit from raising the minimum OS version, open an issue to discuss the trade-offs before implementation

## 📋 Pull Request Process

### Before You Start

- **Check for duplicates**: Search existing issues and PRs to ensure no one is already working on the same feature or bug
- **Avoid duplicate work**: Comment on relevant issues to indicate you're working on them
- **For major changes**, consider opening an issue first to discuss the approach

### Creating Pull Requests

**IMPORTANT**: All development work must be done on the `dev` branch:

1. **Create your branch from `dev`**, not `main`:
   ```bash
   git checkout dev
   git pull origin dev
   git checkout -b feature/your-feature-name
   ```

2. **Make your changes** and commit them with clear, descriptive messages

3. **Push to your fork**:
   ```bash
   git push origin feature/your-feature-name
   ```

4. **Open a Pull Request to the `dev` branch**, not `main`
   - Provide a clear description of the changes
   - Reference any related issues
   - Include screenshots or videos for UI changes
   - Disclose any AI assistance used (see AI Assistance Notice above)

5. **Respond to review feedback** promptly and constructively

## 🐛 Issue Reporting

### Bug Reports

Be descriptive and thorough. Include:

- **macOS version**
- **Nook version**
- **Clear steps to reproduce the issue**
- **Expected vs actual behavior**
- **Screenshots if applicable**
- **Relevant console/error messages**

### Feature Requests

Provide detailed context. Include:

- **Clear description of the feature and its use case**
- **Explain how it fits with Nook's goals** (fast, secure, beautiful)
- **Consider implementation complexity and user impact**
- **Check that similar functionality doesn't already exist**

## 💬 Community Guidelines

- Be respectful and constructive
- Help maintain a welcoming environment for all contributors
- Focus on the code and ideas, not individuals

## License

By contributing to Nook, you agree that your contributions will be licensed under the GPL-3.0 License.


## /CommandPalette/CommandPalette Accessories/CommandPaletteSuggestionView.swift

```swift path="/CommandPalette/CommandPalette Accessories/CommandPaletteSuggestionView.swift" 
//
//  CommandPaletteSuggestionView.swift
//  Nook
//
//  Created by Maciek Bagiński on 31/07/2025.
//

import SwiftUI
import FaviconFinder

struct CommandPaletteSuggestionView: View {
    var favicon: SwiftUI.Image
    var text: String
    var secondaryText: String? = nil
    var isTabSuggestion: Bool = false
    var isSelected: Bool = false
    var historyURL: URL? = nil
    @State private var isHovered: Bool = false
    @State private var resolvedFavicon: SwiftUI.Image? = nil
    
    var body: some View {
        HStack(alignment: .center,spacing: 12) {
            (resolvedFavicon ?? favicon)
                .resizable()
                .scaledToFit()
                .frame(width: 14, height: 14)
                .foregroundStyle(.white.opacity(0.2))
            if let secondary = secondaryText, !secondary.isEmpty {
                HStack(spacing: 6) {
                    Text(text)
                        .font(.system(size: 14, weight: .medium))
                        .lineLimit(1)
                        .truncationMode(.tail)
                    Text("-")
                        .font(.system(size: 14, weight: .medium))
                        .foregroundStyle(.white.opacity(0.35))
                    Text(secondary)
                        .font(.system(size: 14, weight: .medium))
                        .foregroundStyle(.white.opacity(0.5))
                        .lineLimit(1)
                        .truncationMode(.tail)
                }
            } else {
                Text(text)
                    .font(.system(size: 14, weight: .medium))
                    .lineLimit(1)
                    .truncationMode(.tail)
            }
            
            Spacer()
            
            if isTabSuggestion {
                HStack(spacing: 6) {
                    Text("Switch to Tab")
                        .font(.system(size: 12, weight: .medium))
                        .foregroundStyle(.white.opacity(0.5))
                    
                    Image(systemName: "arrow.right")
                        .font(.system(size: 8, weight: .medium))
                        .foregroundStyle(.white.opacity(0.5))
                }
            }
        }
        .padding(.horizontal, 16)
        .padding(.vertical, 12)
        .frame(maxWidth: .infinity)
        .background(backgroundColor)
        .clipShape(RoundedRectangle(cornerRadius: 8))
            
        .onHoverTracking { hovering in
            withAnimation(.easeInOut(duration: 0.15)) {
                isHovered = hovering
            }
        }
        .onAppear {
            guard let url = historyURL else { return }
            Task { await fetchFavicon(for: url) }
        }
    }
    
    private var backgroundColor: Color {
        if isSelected {
            return Color.white.opacity(0.25)
        } else if isHovered {
            return Color.white.opacity(0.15)
        } else {
            return Color.clear
        }
    }
    
    // MARK: - Favicon Fetching (for history items)
    private func fetchFavicon(for url: URL) async {
        let defaultFavicon = SwiftUI.Image(systemName: "globe")
        // Skip favicon fetching for non-web schemes
        guard url.scheme == "http" || url.scheme == "https", url.host != nil else {
            await MainActor.run { self.resolvedFavicon = defaultFavicon }
            return
        }
        
        // Check cache first
        let cacheKey = url.host ?? url.absoluteString
        if let cachedFavicon = Tab.getCachedFavicon(for: cacheKey) {
            await MainActor.run { self.resolvedFavicon = cachedFavicon }
            return
        }
        
        do {
            let favicon = try await FaviconFinder(url: url)
                .fetchFaviconURLs()
                .download()
                .largest()
            if let faviconImage = favicon.image {
                let nsImage = faviconImage.image
                let swiftUIImage = SwiftUI.Image(nsImage: nsImage)
                
                // Cache the favicon
                Tab.cacheFavicon(swiftUIImage, for: cacheKey)
                
                await MainActor.run { self.resolvedFavicon = swiftUIImage }
            } else {
                await MainActor.run { self.resolvedFavicon = defaultFavicon }
            }
        } catch {
            await MainActor.run { self.resolvedFavicon = defaultFavicon }
        }
    }
    
}

```

## /CommandPalette/CommandPalette Accessories/GenericSuggestionItem.swift

```swift path="/CommandPalette/CommandPalette Accessories/GenericSuggestionItem.swift" 
//
//  GenericSuggestionItem.swift
//  Nook
//
//  Created by Maciek Bagiński on 18/08/2025.
//

import SwiftUI

struct GenericSuggestionItem: View {
    let icon: Image
    let text: String
    var isSelected: Bool = false
    
    @Environment(\.colorScheme) var colorScheme

    var body: some View {
        let isDark = colorScheme == .dark
        HStack(alignment: .center, spacing: 12) {
            ZStack {
                icon
                    .resizable()
                    .scaledToFit()
                    .frame(width: 14, height: 14)
                    .foregroundStyle(isSelected ? .white : isDark ? .white.opacity(0.7) : .black.opacity(0.7))
            }
            .frame(width: 24, height: 24)
            .clipShape(RoundedRectangle(cornerRadius: 4))

            Text(text)
                .font(.system(size: 13, weight: .semibold))
                .foregroundStyle(isSelected ? .white : isDark ? .white.opacity(0.6) : .black.opacity(0.8))
                .lineLimit(1)
                .truncationMode(.tail)
            Spacer()
        }
        .frame(maxWidth: .infinity)
    }
}

```

## /CommandPalette/CommandPalette Accessories/HistorySuggestionItem.swift

```swift path="/CommandPalette/CommandPalette Accessories/HistorySuggestionItem.swift" 
//
//  HistorySuggestionItem.swift
//  Nook
//
//  Created by Maciek Bagiński on 18/08/2025.
//

import SwiftUI
import FaviconFinder

struct HistorySuggestionItem: View {
    let entry: HistoryEntry
    var isSelected: Bool = false
    
    @State private var isHovered: Bool = false
    @State private var resolvedFavicon: SwiftUI.Image? = nil
    @Environment(\.colorScheme) var colorScheme
    
    // Color configuration
    private var colors: ColorConfig {
        ColorConfig(
            isDark: colorScheme == .dark,
            isSelected: isSelected,
            isHovered: isHovered
        )
    }
    
    var body: some View {
        HStack(alignment: .center, spacing: 9) {
            ZStack {
                (resolvedFavicon ?? Image(systemName: "globe"))
                    .resizable()
                    .scaledToFit()
                    .foregroundStyle(colors.faviconColor)
                    .frame(width: 14, height: 14)
            }
            .frame(width: 24, height: 24)
            .background(colors.faviconBackground)
            .clipShape(RoundedRectangle(cornerRadius: 4))
            
            HStack(spacing: 4) {
                Text(entry.displayTitle)
                    .font(.system(size: 13, weight: .semibold))
                    .foregroundStyle(colors.titleColor)
                    .lineLimit(1)
                    .truncationMode(.tail)
                
                Text("-")
                    .font(.system(size: 13, weight: .semibold))
                    .foregroundStyle(colors.urlColor)
                
                Text(entry.displayURL)
                    .font(.system(size: 13, weight: .semibold))
                    .foregroundStyle(colors.urlColor)
                    .lineLimit(1)
                    .truncationMode(.tail)
            }
            Spacer()
        }
        .frame(maxWidth: .infinity)
        .onHoverTracking { hovering in
            withAnimation(.easeInOut(duration: 0.15)) {
                isHovered = hovering
            }
        }
        .onAppear {
            Task { await fetchFavicon(for: entry.url) }
        }
    }
    
    private func fetchFavicon(for url: URL) async {
        let defaultFavicon = SwiftUI.Image(systemName: "globe")
        guard url.scheme == "http" || url.scheme == "https", url.host != nil else {
            await MainActor.run { self.resolvedFavicon = defaultFavicon }
            return
        }
        
        let cacheKey = url.host ?? url.absoluteString
        if let cachedFavicon = Tab.getCachedFavicon(for: cacheKey) {
            await MainActor.run { self.resolvedFavicon = cachedFavicon }
            return
        }
        
        do {
            let favicon = try await FaviconFinder(url: url)
                .fetchFaviconURLs()
                .download()
                .largest()
            if let faviconImage = favicon.image {
                let nsImage = faviconImage.image
                let swiftUIImage = SwiftUI.Image(nsImage: nsImage)
                
                Tab.cacheFavicon(swiftUIImage, for: cacheKey)
                
                await MainActor.run { self.resolvedFavicon = swiftUIImage }
            } else {
                await MainActor.run { self.resolvedFavicon = defaultFavicon }
            }
        } catch {
            await MainActor.run { self.resolvedFavicon = defaultFavicon }
        }
    }
}

// MARK: - Colors simplified
private struct ColorConfig {
    let isDark: Bool
    let isSelected: Bool
    let isHovered: Bool
    
    var titleColor: Color {
        if isSelected {
            return .white
        }
        return isDark ? .white : .black
    }
    
    var urlColor: Color {
        if isSelected {
            return .white.opacity(0.5)
        }
        return isDark ? .white.opacity(0.3) : .black.opacity(0.3)
    }
    
    var faviconColor: Color {
        return .white.opacity(0.5)
    }
    
    var faviconBackground: Color {
        return isSelected ? .white : .clear
    }
}

```

## /CommandPalette/CommandPalette Accessories/TabSuggestionItem.swift

```swift path="/CommandPalette/CommandPalette Accessories/TabSuggestionItem.swift" 
//
//  TabSuggestionItem.swift
//  Nook
//
//  Created by Maciek Bagiński on 18/08/2025.
//

import SwiftUI

struct TabSuggestionItem: View {
    let tab: Tab
    var isSelected: Bool = false
    
    @State private var isHovered: Bool = false
    @Environment(\.colorScheme) var colorScheme
    @EnvironmentObject var gradientColorManager: GradientColorManager
    
    var body: some View {
        let isDark = colorScheme == .dark
        
        HStack(alignment: .center, spacing: 0) {
            HStack(spacing: 9) {
                ZStack {
                    tab.favicon
                        .resizable()
                        .scaledToFit()
                        .foregroundStyle(.white.opacity(0.5))
                        .frame(width: 14, height: 14)
                }
                .frame(width: 24, height: 24)
                .background(isSelected ? .white : .clear)
                .clipShape(
                    RoundedRectangle(cornerRadius: 4)
                )
                Text(tab.name)
                    .font(.system(size: 13, weight: .semibold))
                    .foregroundStyle(isSelected ? .white : isDark ? .white.opacity(0.6) : .black.opacity(0.8))
                    .lineLimit(1)
                    .truncationMode(.tail)
            }
            Spacer()
            HStack(spacing: 10) {
                Text("Switch to Tab")
                    .font(.system(size: 12, weight: .medium))
                    .foregroundStyle(isSelected ? .white : isDark ? .white.opacity(0.3) : .black.opacity(0.3))
                ZStack {
                    Image(systemName: "arrow.right")
                        .font(.system(size: 13, weight: .semibold))
                        .foregroundStyle(isSelected ? gradientColorManager.primaryColor : isDark ? .white.opacity(0.5) : .black.opacity(0.5))
                        .frame(width: 16, height: 16)
                }
                .frame(width: 24, height: 24)
                .background(isSelected ? .white : isDark ? .white.opacity(0.05) : .black.opacity(0.05))
                .clipShape(RoundedRectangle(cornerRadius: 4))

            }
        }
        .frame(maxWidth: .infinity)
        .onHoverTracking { hovering in
            withAnimation(.easeInOut(duration: 0.15)) {
                isHovered = hovering
            }
        }
    }
}

```

## /CommandPalette/CommandPalette.swift

```swift path="/CommandPalette/CommandPalette.swift" 
//
//  CommandPalette.swift
//  Nook
//
//  Per-window command palette state and actions
//

import Foundation
import SwiftUI

@MainActor
@Observable
class CommandPalette {
    /// Whether the command palette is visible
    var isVisible: Bool = false

    /// Text to prefill in the command palette
    var prefilledText: String = ""

    /// Whether pressing Return should navigate the current tab (vs creating new tab)
    var shouldNavigateCurrentTab: Bool = false

    // MARK: - Actions

    /// Open the command palette with optional prefill text
    func open(prefill: String = "", navigateCurrentTab: Bool = false) {
        prefilledText = prefill
        self.shouldNavigateCurrentTab = navigateCurrentTab
        DispatchQueue.main.async {
            self.isVisible = true
        }
    }

    /// Open the command palette with the current tab's URL
    func openWithCurrentURL(_ url: URL) {
        open(prefill: url.absoluteString, navigateCurrentTab: true)
    }

    /// Close the command palette
    func close() {
        isVisible = false
        shouldNavigateCurrentTab = false
        prefilledText = ""
    }

    /// Toggle the command palette visibility
    func toggle() {
        if isVisible {
            close()
        } else {
            open()
        }
    }
}

```

## /CommandPalette/CommandPaletteView.swift

```swift path="/CommandPalette/CommandPaletteView.swift" 
//
//  CommandPaletteView.swift
//  Nook
//
//  Created by Maciek Bagiński on 28/07/2025.
//

import AppKit
import SwiftUI
import Garnish

struct CommandPaletteView: View {
    @EnvironmentObject var browserManager: BrowserManager
    @Environment(BrowserWindowState.self) private var windowState
    @Environment(CommandPalette.self) private var commandPalette
    @EnvironmentObject var gradientColorManager: GradientColorManager
    @State private var searchManager = SearchManager()
    @Environment(\.colorScheme) var colorScheme
    @Environment(\.nookSettings) var nookSettings

    @FocusState private var isSearchFocused: Bool
    @State private var text: String = ""
    @State private var selectedSuggestionIndex: Int = -1
    @State private var hoveredSuggestionIndex: Int? = nil
    @State private var userTypedText: String = ""
    @State private var isNavigatingSuggestion: Bool = false
    @State private var activeSiteSearch: SiteSearchEntry? = nil

    private var siteSearchMatch: SiteSearchEntry? {
        guard activeSiteSearch == nil else { return nil }
        return SiteSearchEntry.match(for: text, in: nookSettings.siteSearchEntries)
    }

    private var visibleSuggestions: [SearchManager.SearchSuggestion] {
        if activeSiteSearch != nil {
            return searchManager.suggestions.filter {
                if case .search = $0.type { return true }
                return false
            }
        }
        return searchManager.suggestions
    }

    let commandPaletteWidth: CGFloat = 765
    let commandPaletteHorizontalPadding: CGFloat = 10
    
    /// Active window width
    private var currentWindowWidth: CGFloat {
        return NSApplication.shared.keyWindow?.frame.width ?? 0
    }
    
    /// Check if the command palette fits in the window
    private var isWindowTooNarrow: Bool {
        let requiredWidth = commandPaletteWidth + (commandPaletteHorizontalPadding * 2)
        return currentWindowWidth <= requiredWidth
    }
    
    /// Caclulate the correct command palette width
    private var effectiveCommandPaletteWidth: CGFloat {
        if isWindowTooNarrow {
            return max(200, currentWindowWidth - (commandPaletteHorizontalPadding * 2))
        } else {
            return commandPaletteWidth
        }
    }

    var body: some View {
        let isDark = colorScheme == .dark
        let isVisible = commandPalette.isVisible
        let textFieldColor: Color = text.isEmpty
            ? (isDark ? .white.opacity(0.25) : .black.opacity(0.25))
            : (isDark ? .white.opacity(0.9) : .black.opacity(0.9))

        return ZStack {
            Color.clear
                .ignoresSafeArea()
                .contentShape(Rectangle())
                .onTapGesture {
                    commandPalette.close()
                }
                .gesture(WindowDragGesture())

            VStack {
                Spacer()
                HStack {
                    Spacer()
                    VStack {
                        VStack(alignment: .center,spacing: 6) {
                            HStack(spacing: 15) {
                                Image(
                                    systemName: activeSiteSearch != nil
                                        ? "magnifyingglass"
                                        : isLikelyURL(text)
                                            ? "globe" : "magnifyingglass"
                                )
                                .id(activeSiteSearch != nil ? "magnifyingglass" : isLikelyURL(text) ? "globe" : "magnifyingglass")
                                .transition(.blur(intensity: 2, scale: 0.6).animation(.smooth(duration: 0.3)))
                                .font(.system(size: 14, weight: .regular))
                                .foregroundStyle(isDark ? .white : .black)
                                .frame(width: 15)

                                if let site = activeSiteSearch {
                                    Text(site.name)
                                        .font(.system(size: 13, weight: .semibold))
                                        .foregroundStyle(Garnish.contrastingShade(of: site.color, targetRatio: 4.5, blendStyle: .strong) ?? .white)
                                        .padding(.horizontal, 10)
                                        .padding(.vertical, 4)
                                        .background(site.color)
                                        .clipShape(Capsule())
                                        .transition(
                                            .blur(intensity: 8, scale: 0.6)
                                            .animation(.spring(response: 0.35, dampingFraction: 0.75))
                                        )
                                }

                                ZStack(alignment: .trailing) {
                                    TextField(
                                        activeSiteSearch != nil
                                            ? "Search \(activeSiteSearch!.name)..."
                                            : "Search or enter URL...",
                                        text: $text
                                    )
                                    .textFieldStyle(.plain)
                                    .font(.system(size: 18, weight: .medium))
                                    .foregroundColor(textFieldColor)
                                    .tint(gradientColorManager.primaryColor)
                                    .overlay(alignment: .leading) {
                                        if let suffix = inlineCompletionSuffix {
                                            (Text(text).foregroundColor(.clear) + Text(suffix).foregroundColor(isDark ? .white.opacity(0.25) : .black.opacity(0.25)))
                                                .font(.system(size: 18, weight: .medium))
                                                .lineLimit(1)
                                                .allowsHitTesting(false)
                                        }
                                    }
                                    .focused($isSearchFocused)
                                    .onKeyPress(.tab) {
                                        if let match = siteSearchMatch, activeSiteSearch == nil {
                                            withAnimation(.spring(response: 0.35, dampingFraction: 0.75)) {
                                                activeSiteSearch = match
                                            }
                                            text = ""
                                            return .handled
                                        }
                                        // Tab accepts the top suggestion when nothing is selected
                                        if selectedSuggestionIndex < 0 && !visibleSuggestions.isEmpty {
                                            selectedSuggestionIndex = 0
                                            isNavigatingSuggestion = true
                                            text = displayTextForSuggestion(visibleSuggestions[0])
                                            return .handled
                                        }
                                        // Tab with a selected suggestion = accept it (same as Enter)
                                        if selectedSuggestionIndex >= 0 && selectedSuggestionIndex < visibleSuggestions.count {
                                            let suggestion = visibleSuggestions[selectedSuggestionIndex]
                                            selectSuggestion(suggestion)
                                            return .handled
                                        }
                                        return .ignored
                                    }
                                    .onKeyPress(.return) {
                                        handleReturn()
                                        return .handled
                                    }
                                    .onKeyPress(.upArrow) {
                                        navigateSuggestions(direction: -1)
                                        return .handled
                                    }
                                    .onKeyPress(.downArrow) {
                                        navigateSuggestions(direction: 1)
                                        return .handled
                                    }
                                    .onKeyPress(.escape) {
                                        if activeSiteSearch != nil {
                                            withAnimation(.smooth(duration: 0.25)) {
                                                activeSiteSearch = nil
                                            }
                                            return .handled
                                        }
                                        commandPalette.close()
                                        return .handled
                                    }
                                    .onKeyPress(.delete) {
                                        if activeSiteSearch != nil && text.isEmpty {
                                            withAnimation(.smooth(duration: 0.25)) {
                                                activeSiteSearch = nil
                                            }
                                            return .handled
                                        }
                                        return .ignored
                                    }
                                    .onKeyPress(characters: CharacterSet(charactersIn: "\u{7F}")) { _ in
                                        if activeSiteSearch != nil && text.isEmpty {
                                            withAnimation(.smooth(duration: 0.25)) {
                                                activeSiteSearch = nil
                                            }
                                            return .handled
                                        }
                                        return .ignored
                                    }
                                    .onChange(of: text) { _, newValue in
                                        if isNavigatingSuggestion {
                                            isNavigatingSuggestion = false
                                            return
                                        }
                                        userTypedText = newValue
                                        searchManager.searchSuggestions(
                                            for: newValue
                                        )
                                        selectedSuggestionIndex = -1
                                    }

                                    if activeSiteSearch == nil, let match = siteSearchMatch {
                                        HStack(spacing: 6) {
                                            Text("Search \(match.name)")
                                                .font(.system(size: 14, weight: .medium))
                                                .foregroundStyle(isDark ? .white.opacity(0.3) : .black.opacity(0.3))

                                            Text("Tab")
                                                .font(.system(size: 11, weight: .medium))
                                                .foregroundStyle(isDark ? .white.opacity(0.4) : .black.opacity(0.4))
                                                .padding(.horizontal, 6)
                                                .padding(.vertical, 2)
                                                .background(
                                                    RoundedRectangle(cornerRadius: 4)
                                                        .fill(isDark ? .white.opacity(0.1) : .black.opacity(0.08))
                                                )
                                                .overlay(
                                                    RoundedRectangle(cornerRadius: 4)
                                                        .stroke(isDark ? .white.opacity(0.15) : .black.opacity(0.12), lineWidth: 0.5)
                                                )
                                        }
                                        .allowsHitTesting(false)
                                        .transition(
                                            .blur(intensity: 4, scale: 0.92)
                                            .animation(.smooth(duration: 0.3))
                                        )
                                    }
                                }
                            }
                            .animation(.spring(response: 0.35, dampingFraction: 0.75), value: activeSiteSearch != nil)
                            .padding(.vertical, 8)
                            .padding(.horizontal, 8)

                            if !visibleSuggestions.isEmpty {
                                RoundedRectangle(cornerRadius: 100)
                                    .fill(
                                        isDark
                                            ? Color.white.opacity(0.4)
                                            : Color.black.opacity(0.4)
                                    )
                                    .frame(height: 0.5)
                                    .frame(maxWidth: .infinity)
                            }

                            if !visibleSuggestions.isEmpty {
                                CommandPaletteSuggestionsListView(
                                    suggestions: visibleSuggestions,
                                    selectedIndex: $selectedSuggestionIndex,
                                    hoveredIndex: $hoveredSuggestionIndex,
                                    onSelect: { suggestion in
                                        selectSuggestion(suggestion)
                                    }
                                )
                            }
                        }
                        .padding(10)
                        .frame(maxWidth: .infinity)
                        .frame(width: effectiveCommandPaletteWidth)
                        .background(Color(.windowBackgroundColor).opacity(0.35))
                        .clipShape(.rect(cornerRadius: 26))
                        .nookGlassEffect(in: .rect(cornerRadius: 26))
                        .animation(
                            .easeInOut(duration: 0.15),
                            value: searchManager.suggestions.count
                        )
                        Spacer()
                    }
                    .frame(
                        width: effectiveCommandPaletteWidth,
                        height: 328
                    )

                    Spacer()
                }
                Spacer()
            }

        }
        .allowsHitTesting(isVisible)
        .opacity(isVisible ? 1.0 : 0.0)
        .onChange(of: commandPalette.isVisible) { _, newVisible in
            if newVisible {
                searchManager.setTabManager(browserManager.tabManager)
                searchManager.setHistoryManager(browserManager.historyManager)
                searchManager.updateProfileContext()

                text = commandPalette.prefilledText
                userTypedText = commandPalette.prefilledText

                DispatchQueue.main.async {
                    isSearchFocused = true
                    DispatchQueue.main.async {
                        NSApplication.shared.sendAction(
                            #selector(NSText.selectAll(_:)),
                            to: nil,
                            from: nil
                        )
                    }
                }
            } else {
                isSearchFocused = false
                searchManager.clearSuggestions()
                text = ""
                userTypedText = ""
                activeSiteSearch = nil
                selectedSuggestionIndex = -1
            }
        }
        .onChange(of: browserManager.currentProfile?.id) { _, _ in
            if commandPalette.isVisible {
                searchManager.updateProfileContext()
                searchManager.clearSuggestions()
            }
        }
        .onChange(of: searchManager.suggestions.count) { _, _ in
            let count = visibleSuggestions.count
            if count == 0 {
                selectedSuggestionIndex = -1
            } else if selectedSuggestionIndex >= count {
                selectedSuggestionIndex = count - 1
            }
        }
        .animation(.easeInOut(duration: 0.15), value: selectedSuggestionIndex)
        .onChange(of: commandPalette.prefilledText) { _, newValue in
            if isVisible {
                text = newValue
                userTypedText = newValue
                DispatchQueue.main.async {
                    isSearchFocused = true
                }
            }
        }
    }

    private func isEmoji(_ string: String) -> Bool {
        return string.unicodeScalars.contains { scalar in
            (scalar.value >= 0x1F300 && scalar.value <= 0x1F9FF)
                || (scalar.value >= 0x2600 && scalar.value <= 0x26FF)
                || (scalar.value >= 0x2700 && scalar.value <= 0x27BF)
        }
    }

    // MARK: - Suggestions List Subview
    private struct CommandPaletteSuggestionsListView: View {
        @EnvironmentObject var gradientColorManager: GradientColorManager
        let suggestions: [SearchManager.SearchSuggestion]
        @Binding var selectedIndex: Int
        @Binding var hoveredIndex: Int?
        @Environment(\.colorScheme) var colorScheme
        let onSelect: (SearchManager.SearchSuggestion) -> Void

        var body: some View {
            let isDark = colorScheme == .dark
            LazyVStack(spacing: 5) {
                ForEach(suggestions.indices, id: \.self) { index in
                    let suggestion = suggestions[index]
                    let isHovered = hoveredIndex == index
                    row(for: suggestion, isSelected: selectedIndex == index)
                        .padding(.horizontal, 10)
                        .padding(.vertical, 11)
                        .background(
                            selectedIndex == index
                                ? gradientColorManager.primaryColor
                                : isHovered
                                    ? isDark
                                        ? .white.opacity(0.05)
                                        : .black.opacity(0.05) : .clear
                        )
                        .clipShape(
                            RoundedRectangle(cornerRadius: 6)
                        )
                        .font(.system(size: 13, weight: .semibold))
                        .foregroundStyle(.white)
                        .contentShape(RoundedRectangle(cornerRadius: 6))
                        .onHoverTracking { hovering in
                            withAnimation(.easeInOut(duration: 0.12)) {
                                if hovering {
                                    hoveredIndex = index
                                } else {
                                    hoveredIndex = nil
                                }
                            }
                        }
                        .onTapGesture { onSelect(suggestion) }
                }
            }
        }

        @ViewBuilder
        private func row(
            for suggestion: SearchManager.SearchSuggestion,
            isSelected: Bool
        ) -> some View {
            switch suggestion.type {
            case .tab(let tab):
                TabSuggestionItem(tab: tab, isSelected: isSelected)
            case .history(let entry):
                HistorySuggestionItem(entry: entry, isSelected: isSelected)
            case .url:
                GenericSuggestionItem(
                    icon: Image(systemName: "link"),
                    text: suggestion.text,
                    isSelected: isSelected
                )
            case .search:
                GenericSuggestionItem(
                    icon: Image(systemName: "magnifyingglass"),
                    text: suggestion.text,
                    isSelected: isSelected
                )
            }
        }
    }

    private func handleReturn() {
        if let site = activeSiteSearch {
            let query: String
            if selectedSuggestionIndex >= 0 && selectedSuggestionIndex < visibleSuggestions.count {
                query = visibleSuggestions[selectedSuggestionIndex].text
            } else {
                query = text
            }
            guard !query.isEmpty else { return }
            let navigateURL: String
            if let url = site.searchURL(for: query) {
                navigateURL = url.absoluteString
            } else {
                // Fallback: search on the site's domain directly
                navigateURL = "https://\(site.domain)/search?q=\(query.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? query)"
            }
            if commandPalette.shouldNavigateCurrentTab
                && browserManager.currentTab(for: windowState) != nil
            {
                browserManager.currentTab(for: windowState)?.loadURL(navigateURL)
            } else {
                browserManager.createNewTab(in: windowState, url: navigateURL)
            }
            text = ""
            activeSiteSearch = nil
            selectedSuggestionIndex = -1
            commandPalette.close()
            return
        }

        if selectedSuggestionIndex >= 0
            && selectedSuggestionIndex < visibleSuggestions.count
        {
            let suggestion = visibleSuggestions[selectedSuggestionIndex]
            selectSuggestion(suggestion)
        } else {
            let newSuggestion = SearchManager.SearchSuggestion(
                text: text,
                type: isLikelyURL(text) ? .url : .search
            )
            selectSuggestion(newSuggestion)
        }
    }

    private func selectSuggestion(_ suggestion: SearchManager.SearchSuggestion)
    {
        switch suggestion.type {
        case .tab(let existingTab):
            browserManager.selectTab(existingTab, in: windowState)
        case .history(let historyEntry):
            if commandPalette.shouldNavigateCurrentTab
                && browserManager.currentTab(for: windowState) != nil
            {
                browserManager.currentTab(for: windowState)?.loadURL(
                    historyEntry.url.absoluteString
                )
            } else {
                browserManager.createNewTab(in: windowState, url: historyEntry.url.absoluteString)
            }
        case .url, .search:
            if commandPalette.shouldNavigateCurrentTab
                && browserManager.currentTab(for: windowState) != nil
            {
                browserManager.currentTab(for: windowState)?.navigateToURL(
                    suggestion.text
                )
            } else {
                // Normalize the URL/search query first, then create the tab with
                // the correct URL so the webview loads it directly without a race.
                let template = browserManager.nookSettings?.resolvedSearchEngineTemplate ?? SearchProvider.google.queryTemplate
                let resolved = normalizeURL(suggestion.text, queryTemplate: template)
                browserManager.createNewTab(in: windowState, url: resolved)
            }
        }

        text = ""
        activeSiteSearch = nil
        selectedSuggestionIndex = -1
        commandPalette.close()
    }

    private func navigateSuggestions(direction: Int) {
        let maxIndex = visibleSuggestions.count - 1

        if direction > 0 {
            selectedSuggestionIndex = min(selectedSuggestionIndex + 1, maxIndex)
        } else {
            selectedSuggestionIndex = max(selectedSuggestionIndex - 1, -1)
        }

        // Update text field to show selected suggestion's info
        isNavigatingSuggestion = true
        if selectedSuggestionIndex >= 0 && selectedSuggestionIndex < visibleSuggestions.count {
            text = displayTextForSuggestion(visibleSuggestions[selectedSuggestionIndex])
        } else {
            text = userTypedText
        }
    }

    private func stripScheme(_ urlString: String) -> String {
        for prefix in ["https://", "http://"] {
            if urlString.hasPrefix(prefix) {
                return String(urlString.dropFirst(prefix.count))
            }
        }
        return urlString
    }

    private func displayTextForSuggestion(_ suggestion: SearchManager.SearchSuggestion) -> String {
        switch suggestion.type {
        case .tab(let tab):
            return stripScheme(tab.url.absoluteString)
        case .history(let entry):
            return stripScheme(entry.url.absoluteString)
        case .url, .search:
            return suggestion.text
        }
    }

    private var inlineCompletionSuffix: String? {
        guard text == userTypedText,
              !text.isEmpty,
              selectedSuggestionIndex >= 0,
              selectedSuggestionIndex < visibleSuggestions.count else { return nil }

        let suggestion = visibleSuggestions[selectedSuggestionIndex]
        let target = suggestion.text

        guard target.lowercased().hasPrefix(text.lowercased()),
              target.count > text.count else { return nil }

        return String(target.dropFirst(text.count))
    }

    private func iconForSuggestion(_ suggestion: SearchManager.SearchSuggestion)
        -> Image
    {
        switch suggestion.type {
        case .tab(let tab):
            return tab.favicon
        case .history:
            return Image(systemName: "globe")
        case .url:
            return Image(systemName: "link")
        case .search:
            return Image(systemName: "magnifyingglass")
        }
    }

    @ViewBuilder
    private func suggestionRow(
        for suggestion: SearchManager.SearchSuggestion,
        isSelected: Bool
    ) -> some View {
        switch suggestion.type {
        case .tab(let tab):
            TabSuggestionItem(tab: tab, isSelected: isSelected)
                .foregroundStyle(AppColors.textPrimary)
        case .history(let entry):
            HistorySuggestionItem(entry: entry, isSelected: isSelected)
                .foregroundStyle(AppColors.textPrimary)
        case .url:
            GenericSuggestionItem(
                icon: Image(systemName: "link"),
                text: suggestion.text,
                isSelected: isSelected
            )
            .foregroundStyle(AppColors.textPrimary)
        case .search:
            GenericSuggestionItem(
                icon: Image(systemName: "magnifyingglass"),
                text: suggestion.text,
                isSelected: isSelected
            )
            .foregroundStyle(AppColors.textPrimary)
        }
    }

    private func urlForSuggestion(_ suggestion: SearchManager.SearchSuggestion)
        -> URL?
    {
        switch suggestion.type {
        case .history(let entry):
            return entry.url
        default:
            return nil
        }
    }

    private func isTabSuggestion(_ suggestion: SearchManager.SearchSuggestion)
        -> Bool
    {
        switch suggestion.type {
        case .tab:
            return true
        case .search, .url, .history:
            return false
        }
    }
}

struct BackdropView: NSViewRepresentable {
    func makeNSView(context: Context) -> NSVisualEffectView {
        let view = NSVisualEffectView()
        view.material = .popover
        view.blendingMode = .withinWindow
        view.state = .active
        return view
    }
    
    func updateNSView(_ nsView: NSVisualEffectView, context: Context) { }
}

```

## /Navigation/Sidebar/SidebarBottomBar.swift

```swift path="/Navigation/Sidebar/SidebarBottomBar.swift" 
//
//  SidebarBottomBar.swift
//  Nook
//
//  Created by Aether on 15/11/2025.
//

import SwiftUI

/// Bottom bar of the sidebar containing menu button, spaces list, and new space button
struct SidebarBottomBar: View {
    @EnvironmentObject var browserManager: BrowserManager
    @EnvironmentObject var tabManager: TabManager
    @Environment(BrowserWindowState.self) private var windowState
    @Binding var isMenuButtonHovered: Bool
    let onMenuTap: () -> Void
    let onNewSpaceTap: () -> Void
    let onMenuHover: (Bool) -> Void
    
    var body: some View {
        HStack(alignment: .bottom, spacing: 10) {
            menuButton
            
            // Hide spaces list in incognito windows (only one ephemeral space)
            if !windowState.isIncognito {
                SpacesList()
                    .frame(maxWidth: .infinity)
                    .environmentObject(browserManager)
                    .environment(windowState)
            }
            
            // Hide new space button in incognito windows
            if !windowState.isIncognito {
                newSpaceButton
            }
        }.fixedSize(horizontal: false, vertical: true)
        .padding(.horizontal, 8)
    }
    
    private var menuButton: some View {
        ZStack {
            Button("Menu", systemImage: "archivebox") {
                onMenuTap()
            }
            .labelStyle(.iconOnly)
            .buttonStyle(NavButtonStyle())
            .foregroundStyle(Color.primary)
            .onHoverTracking { isHovered in
                isMenuButtonHovered = isHovered
                onMenuHover(isHovered)
            }
            
            DownloadIndicator()
                .offset(x: 12, y: -12)
        }
    }
    
    private var newSpaceButton: some View {
        Menu{
            Button("New Space", systemImage: "square.grid.2x2") {
                onNewSpaceTap()
            }
            
            Button("New Folder", systemImage: "folder.badge.plus") {
                if let currentSpace = tabManager.currentSpace {
                    tabManager.createFolder(for: currentSpace.id)
                }
            }
            
            Divider()
            
            Button("New Profile", systemImage: "person.badge.plus") {
                // TODO: Show profile creation dialog
            }
        } label:{
            Label("Actions", systemImage: "plus")
                .labelStyle(.iconOnly)
        }
        .menuStyle(.button)
        .buttonStyle(NavButtonStyle())
        .foregroundStyle(Color.primary)
    }
}

```

## /Navigation/Sidebar/SidebarHeader.swift

```swift path="/Navigation/Sidebar/SidebarHeader.swift" 
//
//  SidebarHeader.swift
//  Nook
//
//  Created by Aether on 15/11/2025.
//

import SwiftUI

/// Header section of the sidebar (window controls, navigation buttons, URL bar)
struct SidebarHeader: View {
    @EnvironmentObject var browserManager: BrowserManager
    @EnvironmentObject var hoverSidebarManager: HoverSidebarManager
    @Environment(BrowserWindowState.self) private var windowState
    @Environment(\.nookSettings) var nookSettings
    let isSidebarHovered: Bool
    @State private var sidebarWidth: CGFloat = 0

    var body: some View {
        VStack(spacing: 8) {
            if nookSettings.topBarAddressView {
                windowControls
            }

            if !nookSettings.topBarAddressView {
                navigationButtons
                urlBar
            }
        }
        .onGeometryChange(for: CGFloat.self) { proxy in
            proxy.size.width
        } action: { newWidth in
            sidebarWidth = newWidth
        }
    }

    private var windowControls: some View {
        SidebarWindowControlsView()
            .environmentObject(browserManager)
            .environment(windowState)
            .padding(.horizontal, 8)
    }

    private var navigationButtons: some View {
        HStack(spacing: 2) {
            NavButtonsView(effectiveSidebarWidth: sidebarWidth)
        }
        .padding(.horizontal, 8)
        .frame(height: 30)
    }

    private var urlBar: some View {
        URLBarView(isSidebarHovered: isSidebarHovered)
            .padding(.horizontal, 8)
    }
}

// MARK: - Sidebar Window Controls (Top Bar Mode)
struct SidebarWindowControlsView: View {
    @EnvironmentObject var browserManager: BrowserManager
    @EnvironmentObject var hoverSidebarManager: HoverSidebarManager
    @Environment(BrowserWindowState.self) private var windowState
    @Environment(\.nookSettings) var nookSettings

    var body: some View {
        HStack(spacing: 8) {
            MacButtonsView()
                .frame(width: 70)

            Button("Toggle Sidebar", systemImage: nookSettings.sidebarPosition == .left ? "sidebar.left" : "sidebar.right") {
                browserManager.toggleSidebar(for: windowState, floatingVisible: hoverSidebarManager.isOverlayVisible)
            }
            .labelStyle(.iconOnly)
            .buttonStyle(NavButtonStyle())
            .foregroundStyle(Color.primary)

            if nookSettings.showAIAssistant {
                Button("Toggle AI Assistant", systemImage: "sparkle") {
                    browserManager.toggleAISidebar(for: windowState)
                }
                .labelStyle(.iconOnly)
                .buttonStyle(NavButtonStyle())
                .foregroundStyle(Color.primary)
            }

            Spacer()
        }
        .frame(height: 28)
    }
}

```

## /Navigation/Sidebar/SpaceContextMenu.swift

```swift path="/Navigation/Sidebar/SpaceContextMenu.swift" 
//
//  SpaceContextMenu.swift
//  Nook
//
//  Created by Aether on 15/11/2025.
//

import SwiftUI

/// Shared context menu for spaces (used in SpaceTitle and SpacesList)
struct SpaceContextMenu: View {
    @EnvironmentObject var browserManager: BrowserManager
    @EnvironmentObject var tabManager: TabManager
    let space: Space
    let canDelete: Bool
    let onEditName: (() -> Void)?
    let onEditIcon: (() -> Void)?
    let onOpenSettings: () -> Void
    let onDeleteSpace: () -> Void

    var body: some View {
        Group {
            // Profile picker
            Picker(
                currentProfileName,
                systemImage: currentProfileIcon,
                selection: Binding(
                    get: {
                        space.profileId ?? browserManager.profileManager.profiles.first?.id ?? UUID()
                    },
                    set: { newProfileId in
                        tabManager.assign(spaceId: space.id, toProfile: newProfileId)
                    }
                )
            ) {
                ForEach(browserManager.profileManager.profiles, id: \.id) { profile in
                    Label(profile.name, systemImage: profile.icon).tag(profile.id)
                }
            }

            Divider()

            // Rename (optional - only available for SpaceTitle)
            if let onEditName = onEditName {
                Button {
                    onEditName()
                } label: {
                    Label("Rename", systemImage: "textformat")
                }
            }

            // Change icon (optional - only available for SpaceTitle)
            if let onEditIcon = onEditIcon {
                Button {
                    onEditIcon()
                } label: {
                    Label("Change Icon", systemImage: "face.smiling")
                }
            }

            // Customize appearance
            Button {
                browserManager.showGradientEditor()
            } label: {
                Label("Customize Appearance", systemImage: "paintpalette")
            }

            Divider()

            // Space settings
            Button {
                onOpenSettings()
            } label: {
                Label("Space Settings", systemImage: "gear")
            }

            Divider()

            // Delete space
            if canDelete {
                Button(role: .destructive) {
                    showDeleteConfirmation()
                } label: {
                    Label("Delete Space", systemImage: "trash")
                }
            }
        }
    }

    // MARK: - Helper Methods

    private func showDeleteConfirmation() {
        // Count both regular and space-pinned tabs
        let regularTabsCount = tabManager.tabsBySpace[space.id]?.count ?? 0
        let spacePinnedTabsCount = tabManager.spacePinnedTabs(for: space.id).count
        let tabsCount = regularTabsCount + spacePinnedTabsCount

        browserManager.dialogManager.showDialog(
            SpaceDeleteConfirmationDialog(
                spaceName: space.name,
                spaceIcon: space.icon,
                tabsCount: tabsCount,
                isLastSpace: tabManager.spaces.count <= 1,
                onDelete: {
                    onDeleteSpace()
                    browserManager.dialogManager.closeDialog()
                },
                onCancel: {
                    browserManager.dialogManager.closeDialog()
                }
            )
        )
    }

    // MARK: - Helper Properties

    private var currentProfileName: String {
        guard let profileId = space.profileId,
              let profile = browserManager.profileManager.profiles.first(where: { $0.id == profileId })
        else {
            return browserManager.profileManager.profiles.first?.name ?? "Default"
        }
        return profile.name
    }

    private var currentProfileIcon: String {
        guard let profileId = space.profileId,
              let profile = browserManager.profileManager.profiles.first(where: { $0.id == profileId })
        else {
            return browserManager.profileManager.profiles.first?.icon ?? "person.circle"
        }
        return profile.icon
    }
}

```

## /Navigation/Sidebar/SpacesList/SpacesList.swift

```swift path="/Navigation/Sidebar/SpacesList/SpacesList.swift" 
//
//  SpacesList.swift
//  Nook
//
//  Created by Maciek Bagiński on 04/08/2025.
//  Refactored by Aether on 15/11/2025.
//

import SwiftUI

struct SpacesList: View {
    @EnvironmentObject var browserManager: BrowserManager
    @EnvironmentObject var tabManager: TabManager
    @Environment(BrowserWindowState.self) private var windowState
    @State private var availableWidth: CGFloat = 0
    @State private var hoveredSpaceId: UUID?
    @State private var showPreview: Bool = false
    @State private var isHoveringList: Bool = false

    private var layoutMode: SpacesListLayoutMode {
        let spaces = windowState.isIncognito
            ? windowState.ephemeralSpaces
            : tabManager.spaces
        return SpacesListLayoutMode.determine(
            spacesCount: spaces.count,
            availableWidth: availableWidth
        )
    }

    private var visibleSpaces: [Space] {
        if windowState.isIncognito {
            return windowState.ephemeralSpaces
        }
        return tabManager.spaces
    }

    var body: some View {
        Color.clear
            .onGeometryChange(for: CGFloat.self) { proxy in
                proxy.size.width
            } action: { newWidth in
                availableWidth = newWidth
            }
            .overlay{
                    HStack(spacing: 0) {
                        ForEach(Array(visibleSpaces.enumerated()), id: \.element.id) { index, space in
                            SpacesListItem(
                                space: space,
                                isActive: windowState.currentSpaceId == space.id,
                                compact: layoutMode == .compact,
                                isFaded: false,
                                onHoverChange: { isHovering in
                                    if isHovering {
                                        hoveredSpaceId = space.id
                                        if showPreview {
                                        } else {
                                            DispatchQueue.main.asyncAfter(deadline: .now() + 0.7) {
                                                if hoveredSpaceId == space.id && isHoveringList {
                                                    withAnimation(.easeInOut(duration: 0.2)) {
                                                        showPreview = true
                                                    }
                                                }
                                            }
                                        }
                                    } else if hoveredSpaceId == space.id {
                                        hoveredSpaceId = nil
                                    }
                                }
                            )
                            .environmentObject(browserManager)
                            .environment(windowState)
                            .id(space.id)
                            .transition(.asymmetric(
                                insertion: .scale.combined(with: .opacity),
                                removal: .scale.combined(with: .opacity)
                            ))
                            
                            if index != visibleSpaces.count - 1 {
                                Spacer()
                                    .frame(minWidth: 1, maxWidth: 8)
                                    .layoutPriority(-1)
                            }
                        }
                    }
                    .onHoverTracking { hovering in
                        isHoveringList = hovering
                        if !hovering {
                            showPreview = false
                            hoveredSpaceId = nil
                        }
                    }
                    .overlay(alignment: .top) {
                        if showPreview,
                           let hoveredId = hoveredSpaceId,
                           hoveredId != windowState.currentSpaceId,
                           let hoveredSpace = visibleSpaces.first(where: { $0.id == hoveredId }) {
                            Text(hoveredSpace.name)
                                .font(.caption)
                                .foregroundStyle(previewTextColor)
                                .opacity(0.7)
                                .lineLimit(1)
                                .id(hoveredSpace.id)
                                .transition(.blur.animation(.smooth(duration: 0.2)))
                                .offset(y: -20)
                        }
                    }
            }
            .animation(.easeInOut(duration: 0.3), value: visibleSpaces.count)
    }

    private var previewTextColor: Color {
        browserManager.gradientColorManager.isDark
            ? AppColors.spaceTabTextDark
            : AppColors.spaceTabTextLight
    }

}

// MARK: - Layout Mode

enum SpacesListLayoutMode {
    case normal    // Full icons with spacing
    case compact   // Dots for inactive, icons for active

    static func determine(spacesCount: Int, availableWidth: CGFloat) -> Self {
        guard spacesCount > 0 else { return .normal }

        // Measurements for NavButtonStyle button with default .regular control size
        let buttonSize: CGFloat = 32.0  // NavButtonStyle .regular = 32pt
        let minSpacing: CGFloat = 4.0

        // Normal mode: all icons visible with minimum spacing
        let normalMinWidth = (CGFloat(spacesCount) * buttonSize) + (CGFloat(spacesCount - 1) * minSpacing)

        // Compact mode: 1 active icon + (n-1) dots with minimum spacing
        let dotSize: CGFloat = 6.0
        let totalDots = spacesCount - 1
        let compactMinWidth = buttonSize + (CGFloat(totalDots) * dotSize) + (CGFloat(totalDots) * minSpacing)

        // Choose mode: switch to compact only when normal mode would be too cramped
        // Stay in normal as long as we have at least minimum spacing
        if availableWidth >= normalMinWidth {
            return .normal
        } else if availableWidth >= compactMinWidth {
            return .compact
        } else {
            // Even compact doesn't fit perfectly, but use compact anyway
            return .compact
        }
    }
}

```

## /Navigation/Sidebar/SpacesList/SpacesListItem.swift

```swift path="/Navigation/Sidebar/SpacesList/SpacesListItem.swift" 
//
//  SpacesListItem.swift
//  Nook
//
//  Created by Maciek Bagiński on 04/08/2025.
//  Refactored by Aether on 15/11/2025.
//

import SwiftUI

struct SpacesListItem: View {
    @EnvironmentObject var browserManager: BrowserManager
    @EnvironmentObject var tabManager: TabManager
    @Environment(BrowserWindowState.self) private var windowState

    let space: Space
    let isActive: Bool
    let compact: Bool
    let isFaded: Bool
    let onHoverChange: ((Bool) -> Void)?

    @State private var isHovering: Bool = false
    @StateObject private var emojiManager = EmojiPickerManager()

    private let dotSize: CGFloat = 6

    init(
        space: Space,
        isActive: Bool,
        compact: Bool,
        isFaded: Bool,
        onHoverChange: ((Bool) -> Void)? = nil
    ) {
        self.space = space
        self.isActive = isActive
        self.compact = compact
        self.isFaded = isFaded
        self.onHoverChange = onHoverChange
    }

    var body: some View {
        Button {
            NSHapticFeedbackManager.defaultPerformer.perform(.alignment, performanceTime: .now)
            withAnimation(.easeInOut(duration: 0.2)) {
                browserManager.setActiveSpace(space, in: windowState)
            }
        } label: {
            spaceIcon
                .opacity(isActive ? 1.0 : 0.7)
                .frame(maxWidth: .infinity)

        }
        .labelStyle(.iconOnly)
        .buttonStyle(SpaceListItemButtonStyle())
        .layoutPriority(2)
        .foregroundStyle(Color.primary)
        .layoutPriority(isActive ? 1 : 0)
        .opacity(isFaded ? 0.3 : 1.0)
        .onHoverTracking { hovering in
            isHovering = hovering
            onHoverChange?(hovering)
        }
        .contextMenu {
            spaceContextMenu
        }
    }

    // MARK: - Icon

    @ViewBuilder
    private var spaceIcon: some View {
        if compact && !isActive {
            // Compact mode: show dot
            Circle()
                .fill(iconColor)
                .frame(width: dotSize, height: dotSize)
        } else {
            // Normal mode: show icon or emoji
            if isEmoji(space.icon) {
                Text(space.icon)
                    .conditionally(if: !isActive, apply: { view in
                        view.colorMultiply(.gray).blendMode(.luminosity)
                    })
                    .background(EmojiPickerAnchor(manager: emojiManager))
                    .onChange(of: emojiManager.selectedEmoji) { _, newValue in
                        space.icon = newValue
                        tabManager.persistSnapshot()
                    }

            } else {
                Image(systemName: space.icon)
                    .foregroundStyle(iconColor)
                    .background(EmojiPickerAnchor(manager: emojiManager))
                    .onChange(of: emojiManager.selectedEmoji) { _, newValue in
                        space.icon = newValue
                        tabManager.persistSnapshot()
                    }
            }
        }
    }

    private var iconColor: Color {
        browserManager.gradientColorManager.isDark
            ? AppColors.spaceTabTextDark
            : AppColors.spaceTabTextLight
    }

    // MARK: - Context Menu

    @ViewBuilder
    private var spaceContextMenu: some View {
        Button {
            showSpaceEditDialog()
        } label: {
            Label("Space Settings", systemImage: "gear")
        }

        if tabManager.spaces.count > 1 {
            Button(role: .destructive) {
                showDeleteConfirmation()
            } label: {
                Label("Delete Space", systemImage: "trash")
            }
        }
    }

    // MARK: - Helper Methods

    private func showDeleteConfirmation() {
        // Count both regular and space-pinned tabs
        let regularTabsCount = tabManager.tabsBySpace[space.id]?.count ?? 0
        let spacePinnedTabsCount = tabManager.spacePinnedTabs(for: space.id).count
        let tabsCount = regularTabsCount + spacePinnedTabsCount

        browserManager.dialogManager.showDialog(
            SpaceDeleteConfirmationDialog(
                spaceName: space.name,
                spaceIcon: space.icon,
                tabsCount: tabsCount,
                isLastSpace: tabManager.spaces.count <= 1,
                onDelete: {
                    tabManager.removeSpace(space.id)
                    browserManager.dialogManager.closeDialog()
                },
                onCancel: {
                    browserManager.dialogManager.closeDialog()
                }
            )
        )
    }

    private func showSpaceEditDialog() {
        browserManager.dialogManager.showDialog(
            SpaceEditDialog(
                space: space,
                mode: .icon,
                onSave: { newName, newIcon, newProfileId in
                    do {
                        if newIcon != space.icon {
                            try tabManager.updateSpaceIcon(
                                spaceId: space.id,
                                icon: newIcon
                            )
                        }

                        if newName != space.name {
                            try tabManager.renameSpace(
                                spaceId: space.id,
                                newName: newName
                            )
                        }

                        // Update profile if changed
                        if newProfileId != space.profileId, let profileId = newProfileId {
                            tabManager.assign(spaceId: space.id, toProfile: profileId)
                        }

                        browserManager.dialogManager.closeDialog()
                    } catch {
                    }
                },
                onCancel: {
                    browserManager.dialogManager.closeDialog()
                }
            )
        )
    }

    private func isEmoji(_ string: String) -> Bool {
        string.unicodeScalars.contains { scalar in
            (scalar.value >= 0x1F300 && scalar.value <= 0x1F9FF) // Emoticons & pictographs
                || (scalar.value >= 0x2600 && scalar.value <= 0x26FF) // Miscellaneous symbols
                || (scalar.value >= 0x2700 && scalar.value <= 0x27BF) // Dingbats
        }
    }
}

struct SpaceListItemButtonStyle: ButtonStyle {
    @Environment(\.colorScheme) var colorScheme
    @Environment(\.isEnabled) var isEnabled
    @Environment(\.controlSize) var controlSize
    @State private var isHovering: Bool = false
    
    func makeBody(configuration: Configuration) -> some View {
        ZStack {
            RoundedRectangle(cornerRadius: cornerRadius)
                .fill(.primary.opacity(backgroundColorOpacity(isPressed: configuration.isPressed)))

            configuration.label
                .foregroundStyle(.primary)
        }
        .frame(height: size)
        .frame(maxWidth: size)
        .opacity(isEnabled ? 1.0 : 0.3)
        
        .contentTransition(.symbolEffect(.replace.upUp.byLayer, options: .nonRepeating))
        .scaleEffect(configuration.isPressed && isEnabled ? 0.95 : 1.0)
        .animation(.easeInOut(duration: 0.1), value: configuration.isPressed)
        .animation(.easeInOut(duration: 0.15), value: isHovering)
        .onHoverTracking { hovering in
            isHovering = hovering
        }
    }
    
    private var size: CGFloat {
        switch controlSize {
        case .mini: 24
        case .small: 28
        case .regular: 32
        case .large: 40
        case .extraLarge: 48
        @unknown default: 32
        }
    }
    
    private var cornerRadius: CGFloat {
        8
    }
    
    private func backgroundColorOpacity(isPressed: Bool) -> Double {
        if (isHovering || isPressed) && isEnabled {
            return colorScheme == .dark ? 0.2 : 0.1
        } else {
            return 0.0
        }
    }
}

```

## /Navigation/Sidebar/SpacesSideBarView.swift

```swift path="/Navigation/Sidebar/SpacesSideBarView.swift" 
//
//  SpacesSideBarView.swift
//  Nook
//
//  Created by Maciek Bagiński on 30/07/2025.
//  Refactored by Aether on 15/11/2025.
//

import AppKit
import SwiftUI
import UniformTypeIdentifiers
import Sparkle

struct SpacesSideBarView: View {
    @EnvironmentObject var browserManager: BrowserManager
    @EnvironmentObject var tabManager: TabManager
    @Environment(BrowserWindowState.self) private var windowState
    @Environment(WindowRegistry.self) private var windowRegistry
    @Environment(\.nookSettings) var nookSettings
    @Environment(CommandPalette.self) var commandPalette
    @Environment(TabOrganizerManager.self) var tabOrganizerManager

    // Space navigation
    @State private var activeSpaceIndex: Int = 0
    @State private var activeTabRefreshTrigger: Bool = false

    // Hover states
    @State private var isSidebarHovered: Bool = false
    @State private var isMenuButtonHovered = false
    @State private var isDownloadsHovered = false
    @State private var showDownloadsMenu = false
    @State private var animateDownloadsMenu: Bool = false

    var body: some View {
        sidebarContent
            .contentShape(Rectangle())
            .onHoverTracking { state in
                isSidebarHovered = state
            }
            .contextMenu {
                sidebarContextMenu
            }
    }

    // MARK: - Main Content

    private var sidebarContent: some View {
        ZStack {
            if windowState.isSidebarMenuVisible {
                SidebarMenu()
                    .transition(menuTransition)
            } else {
                mainSidebarContent
                    .transition(.opacity)
            }
        }
    }

    @ObservedObject private var dragSession = NookDragSessionManager.shared

    private var mainSidebarContent: some View {
        return VStack(spacing: 8) {
            // Header (window controls, nav buttons, URL bar)
            SidebarHeader(isSidebarHovered: isSidebarHovered)
                .environmentObject(browserManager)
                .environment(windowState)

            // Spaces page view with draggable spacer
            ZStack {
                spacesPageView
                    .zIndex(1)

                // Bottom spacer for window dragging
                Color.clear
                    .contentShape(Rectangle())
                    .conditionalWindowDrag()
                    .frame(minHeight: 40)
                    .zIndex(0)
            }

            // Downloads menu hover overlay
            if showDownloadsMenu {
                downloadsMenuOverlay
            }

            // Update notification
            SidebarUpdateNotification(downloadsMenuVisible: showDownloadsMenu)
                .environmentObject(browserManager)
                .environment(windowState)
                .environment(nookSettings)
                .padding(.horizontal, 8)
                .padding(.bottom, 8)

            // Media controls
            MediaControlsView()
                .environmentObject(browserManager)
                .environment(windowState)

            // Bottom bar (menu, spaces indicators, new space)
            SidebarBottomBar(
                isMenuButtonHovered: $isMenuButtonHovered,
                onMenuTap: handleMenuTap,
                onNewSpaceTap: showSpaceCreationDialog,
                onMenuHover: handleMenuHover
            )
            .environmentObject(browserManager)
            .environment(windowState)
        }
        // Extra top padding when sidebar is on the left to avoid overlapping native traffic light buttons
        .padding(.top, nookSettings.sidebarPosition == .left ? 30 : 8)
        .padding(.bottom, 8)
        .background(
            GeometryReader { geo in
                Color.clear
                    .onAppear {
                        updateSidebarScreenFrame(geo)
                    }
                    .onChange(of: geo.frame(in: .global)) { _, _ in
                        updateSidebarScreenFrame(geo)
                    }
            }
        )
    }

    private func updateSidebarScreenFrame(_ geo: GeometryProxy) {
        let frame = geo.frame(in: .global)
        guard let window = windowState.window ?? NSApp.windows.first(where: { $0.isVisible }),
              let contentView = window.contentView else { return }
        let appKitY = contentView.bounds.height - frame.maxY
        let bottomLeft = NSPoint(x: frame.origin.x, y: appKitY)
        let screenBottomLeft = window.convertPoint(toScreen: bottomLeft)
        dragSession.sidebarScreenFrame = CGRect(
            x: screenBottomLeft.x,
            y: screenBottomLeft.y,
            width: frame.width,
            height: frame.height
        )
    }

    // MARK: - Spaces Page View

    private var spacesPageView: some View {
        let spaces = windowState.isIncognito
            ? windowState.ephemeralSpaces
            : tabManager.spaces

        return Group {
            if spaces.isEmpty {
                emptyStateView
            } else {
                spacesContent(spaces: spaces)
            }
        }
    }

    private func spacesContent(spaces: [Space]) -> some View {
        PageView(selection: $activeSpaceIndex) {
            ForEach(spaces.indices, id: \.self) { index in
                if index >= 0 && index < spaces.count {
                    makeSpaceView(for: spaces[index], index: index)
                } else {
                    EmptyView()
                }
            }
        }
        .pageViewStyle(.scroll)
        .contentShape(Rectangle())
        .id(activeTabRefreshTrigger)
        .onAppear {
            if let targetIndex = spaces.firstIndex(where: { $0.id == windowState.currentSpaceId }) {
                activeSpaceIndex = targetIndex
            }
            browserManager.setActiveSpace(spaces[0], in: windowState)
        }
        .onChange(of: activeSpaceIndex) { _, newIndex in
            handleSpaceIndexChange(newIndex, spaces: spaces)
        }
        .onChange(of: windowState.currentSpaceId) { _, _ in
            if let targetIndex = spaces.firstIndex(where: { $0.id == windowState.currentSpaceId }) {
                activeSpaceIndex = targetIndex
            }
            activeTabRefreshTrigger.toggle()
        }
        .onChange(of: windowState.sidebarContentWidth) { _, _ in
            activeTabRefreshTrigger.toggle()
        }
    }

    private var emptyStateView: some View {
        VStack(spacing: 16) {
            Image(systemName: "square.grid.2x2")
                .font(.system(size: 48))
                .foregroundColor(.secondary)
            VStack(spacing: 8) {
                Text("No Spaces")
                    .font(.title2)
                    .fontWeight(.semibold)
                Text("Create a space to start browsing")
                    .font(.body)
                    .foregroundColor(.secondary)
            }
            Button(action: showSpaceCreationDialog) {
                Label("Create Space", systemImage: "plus")
            }
            .buttonStyle(.borderedProminent)
        }
        .padding()
    }

    // MARK: - Downloads Menu

    private var downloadsMenuOverlay: some View {
        SidebarMenuHoverDownloads(isVisible: animateDownloadsMenu)
            .onHoverTracking { isHovered in
                isDownloadsHovered = isHovered
                if isHovered {
                    showDownloadsMenu = true
                    animateDownloadsMenu = true
                } else {
                    hideMenuAfterDelay()
                }
            }
    }

    // MARK: - Context Menu

    private var sidebarContextMenu: some View {
        Group {
            Button {
                commandPalette.open()
            } label: {
                Label("New Tab", systemImage: "plus")
            }

            Button {
                if let currentSpace = tabManager.currentSpace {
                    tabManager.createFolder(for: currentSpace.id)
                }
            } label: {
                Label("New Folder", systemImage: "folder.badge.plus")
            }

            Divider()

            Menu {
                ForEach(SidebarPosition.allCases) { position in
                    Toggle(isOn: Binding(
                        get: { nookSettings.sidebarPosition == position },
                        set: { _ in
                            withAnimation(.smooth(duration: 0.3)) {
                                nookSettings.sidebarPosition = position
                            }
                        }
                    )) {
                        Label(position.displayName, systemImage: position.icon)
                    }
                }
            } label: {
                Label("Position", systemImage: nookSettings.sidebarPosition.icon)
            }
        }
    }

    // MARK: - Helper Functions

    private func handleMenuTap() {
        withAnimation(.easeInOut(duration: 0.2)) {
            windowState.isSidebarMenuVisible = true
            windowState.isSidebarAIChatVisible = false
            let previousWidth = windowState.sidebarWidth
            windowState.savedSidebarWidth = previousWidth
            let newWidth: CGFloat = 400
            windowState.sidebarWidth = newWidth
            windowState.sidebarContentWidth = max(newWidth - 16, 0)
        }
    }

    private func handleMenuHover(_ isHovered: Bool) {
        if isHovered {
            showDownloadsMenu = true
            animateDownloadsMenu = true
        } else {
            hideMenuAfterDelay()
        }
    }

    private func hideMenuAfterDelay() {
        DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
            if !isMenuButtonHovered, !isDownloadsHovered {
                animateDownloadsMenu = false
                DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
                    showDownloadsMenu = false
                }
            }
        }
    }

    private func handleSpaceIndexChange(_ newIndex: Int, spaces: [Space]) {
        guard newIndex >= 0 && newIndex < spaces.count else {
            return
        }

        let space = spaces[newIndex]

        // Trigger haptic feedback
        NSHapticFeedbackManager.defaultPerformer.perform(.alignment, performanceTime: .default)

        // Activate the space
        browserManager.setActiveSpace(space, in: windowState)
    }

    @ViewBuilder
    private func makeSpaceView(for space: Space, index: Int) -> some View {
        VStack(spacing: 0) {
            if !windowState.isIncognito {
                PinnedGrid(
                    width: windowState.sidebarContentWidth,
                    profileId: space.profileId ?? browserManager.currentProfile?.id
                )
                .environmentObject(browserManager)
                .environmentObject(tabManager)
                .environment(windowState)
                .environment(windowRegistry)
                .environment(nookSettings)
                .padding(.horizontal, 8)
                .padding(.bottom, 8)
                .modifier(FallbackDropBelowEssentialsModifier())
            }

            SpaceView(
                space: space,
                isActive: windowState.currentSpaceId == space.id,
                isSidebarHovered: $isSidebarHovered,
                onActivateTab: { browserManager.selectTab($0, in: windowState) },
                onCloseTab: { tabManager.removeTab($0.id) },
                onPinTab: { tabManager.pinTab($0) },
                onMoveTabUp: { tabManager.moveTabUp($0.id) },
                onMoveTabDown: { tabManager.moveTabDown($0.id) },
                onMuteTab: { $0.toggleMute() }
            )
            .environmentObject(browserManager)
            .environmentObject(tabManager)
            .environment(windowState)
            .environment(windowRegistry)
            .environment(commandPalette)
            .environment(tabOrganizerManager)
            .environment(nookSettings)
            .environmentObject(browserManager.gradientColorManager)
            .environmentObject(browserManager.splitManager)
            .id(space.id.uuidString + "-w\(Int(windowState.sidebarContentWidth))")
            Spacer()
        }
        .tag(index)
    }

    // MARK: - Dialogs

    private func showSpaceCreationDialog() {
        browserManager.dialogManager.showDialog(
            SpaceCreationDialog(
                onCreate: { name, icon, profileId in
                    let finalName = name.isEmpty ? "New Space" : name
                    let finalIcon = icon.isEmpty ? "✨" : icon
                    let newSpace = tabManager.createSpace(
                        name: finalName,
                        icon: finalIcon
                    )

                    // Assign profile if one was selected
                    if let profileId = profileId {
                        tabManager.assign(spaceId: newSpace.id, toProfile: profileId)
                    }

                    if let targetIndex = tabManager.spaces.firstIndex(where: { $0.id == newSpace.id }) {
                        activeSpaceIndex = targetIndex
                    }

                    browserManager.dialogManager.closeDialog()
                },
                onCancel: {
                    browserManager.dialogManager.closeDialog()
                }
            )
        )
    }

    private func showSpaceEditDialog(mode: SpaceEditDialog.Mode) {
        guard let targetSpace = resolveCurrentSpace() else { return }

        browserManager.dialogManager.showDialog(
            SpaceEditDialog(
                space: targetSpace,
                mode: mode,
                onSave: { newName, newIcon, newProfileId in
                    let spaceId = targetSpace.id

                    do {
                        if newIcon != targetSpace.icon {
                            try tabManager.updateSpaceIcon(
                                spaceId: spaceId,
                                icon: newIcon
                            )
                        }

                        if newName != targetSpace.name {
                            try tabManager.renameSpace(
                                spaceId: spaceId,
                                newName: newName
                            )
                        }

                        // Update profile if changed
                        if newProfileId != targetSpace.profileId, let profileId = newProfileId {
                            tabManager.assign(spaceId: spaceId, toProfile: profileId)
                        }

                        browserManager.dialogManager.closeDialog()
                    } catch {
                    }
                },
                onCancel: {
                    browserManager.dialogManager.closeDialog()
                }
            )
        )
    }

    private func resolveCurrentSpace() -> Space? {
        // For incognito windows, use ephemeral spaces
        if windowState.isIncognito {
            if let currentId = windowState.currentSpaceId {
                return windowState.ephemeralSpaces.first { $0.id == currentId }
            }
            return windowState.ephemeralSpaces.first
        }
        
        if let current = tabManager.currentSpace {
            return current
        }
        if let currentId = windowState.currentSpaceId {
            return tabManager.spaces.first { $0.id == currentId }
        }
        return tabManager.spaces.first
    }

    // MARK: - Computed Properties

    private var menuTransition: AnyTransition {
        .move(edge: nookSettings.sidebarPosition == .left ? .leading : .trailing)
            .combined(with: .opacity)
    }
}

```

## /Nook.xcodeproj/project.pbxproj

```pbxproj path="/Nook.xcodeproj/project.pbxproj" 
// !$*UTF8*$!
{
	archiveVersion = 1;
	classes = {
	};
	objectVersion = 77;
	objects = {

/* Begin PBXBuildFile section */
		2C16A0262E87430B0070894B /* Sparkle in Frameworks */ = {isa = PBXBuildFile; productRef = 2C16A0252E87430B0070894B /* Sparkle */; };
		2D0822D82EC8A61800C302AC /* UniversalGlass in Frameworks */ = {isa = PBXBuildFile; productRef = 2D0822D72EC8A61800C302AC /* UniversalGlass */; };
		3600212C2EC6EADB0016A41E /* ComplexModule in Frameworks */ = {isa = PBXBuildFile; productRef = 3600212B2EC6EADB0016A41E /* ComplexModule */; };
		3600212E2EC6EADB0016A41E /* Numerics in Frameworks */ = {isa = PBXBuildFile; productRef = 3600212D2EC6EADB0016A41E /* Numerics */; };
		360021302EC6EADB0016A41E /* RealModule in Frameworks */ = {isa = PBXBuildFile; productRef = 3600212F2EC6EADB0016A41E /* RealModule */; };
		360021332EC6EAEB0016A41E /* Atomics in Frameworks */ = {isa = PBXBuildFile; productRef = 360021322EC6EAEB0016A41E /* Atomics */; };
		360021362EC6EC150016A41E /* Highlightr in Frameworks */ = {isa = PBXBuildFile; productRef = 360021352EC6EC150016A41E /* Highlightr */; };
		360021392EC6EC2F0016A41E /* Fuzi in Frameworks */ = {isa = PBXBuildFile; productRef = 360021382EC6EC2F0016A41E /* Fuzi */; };
		3600213C2EC6EC450016A41E /* Reeeed in Frameworks */ = {isa = PBXBuildFile; productRef = 3600213B2EC6EC450016A41E /* Reeeed */; };
		3600213F2EC6EC710016A41E /* LRUCache in Frameworks */ = {isa = PBXBuildFile; productRef = 3600213E2EC6EC710016A41E /* LRUCache */; };
		360021422EC6EC7F0016A41E /* Graphing in Frameworks */ = {isa = PBXBuildFile; productRef = 360021412EC6EC7F0016A41E /* Graphing */; };
		360021442EC6EC7F0016A41E /* Motion in Frameworks */ = {isa = PBXBuildFile; productRef = 360021432EC6EC7F0016A41E /* Motion */; };
		564997D82E9B24CC00D89F78 /* Garnish in Frameworks */ = {isa = PBXBuildFile; productRef = 564997D72E9B24CC00D89F78 /* Garnish */; };
		56C82BA92E9C2DB500DDD0D6 /* UniversalGlass in Frameworks */ = {isa = PBXBuildFile; productRef = 56C82BA82E9C2DB500DDD0D6 /* UniversalGlass */; };
		7FAFC5DA2E3ADDCD009D7DC4 /* FaviconFinder in Frameworks */ = {isa = PBXBuildFile; productRef = 7FAFC5D92E3ADDCD009D7DC4 /* FaviconFinder */; };
		7FE9E0EB2EE59D3500584E16 /* ColorfulX in Frameworks */ = {isa = PBXBuildFile; productRef = 7FE9E0EA2EE59D3500584E16 /* ColorfulX */; };
		A1B2C3D62F0E6A0100ABCDEF /* MLXLLM in Frameworks */ = {isa = PBXBuildFile; productRef = A1B2C3D52F0E6A0100ABCDEF /* MLXLLM */; };
		A7A6FB442F70F4B8007C79C8 /* ContentBlockerConverter in Frameworks */ = {isa = PBXBuildFile; productRef = B1F2E3D52F0F8B0100FACADE /* ContentBlockerConverter */; };
/* End PBXBuildFile section */

/* Begin PBXFileReference section */
		7F8340FC2E37F39400674A5D /* Nook.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Nook.app; sourceTree = BUILT_PRODUCTS_DIR; };
/* End PBXFileReference section */

/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */
		2CAC4DD12E82457B00870189 /* Exceptions for "Nook" folder in "Nook" target */ = {
			isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
			membershipExceptions = (
				Info.plist,
				ThirdParty/BigUIPaging/README.md,
				ThirdParty/HTSymbolHook/README.md,
				ThirdParty/MuteableWKWebView/README.md,
			);
			target = 7F8340FB2E37F39400674A5D /* Nook */;
		};
		2D0834BF2EC8CE1300C302AC /* Exceptions for "CommandPalette" folder in "Nook" target */ = {
			isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
			membershipExceptions = (
				"CommandPalette Accessories/CommandPaletteSuggestionView.swift",
				"CommandPalette Accessories/GenericSuggestionItem.swift",
				"CommandPalette Accessories/HistorySuggestionItem.swift",
				"CommandPalette Accessories/TabSuggestionItem.swift",
				CommandPalette.swift,
				CommandPaletteView.swift,
			);
			target = 7F8340FB2E37F39400674A5D /* Nook */;
		};
		2D0834D12EC8E0AD00C302AC /* Exceptions for "Navigation" folder in "Nook" target */ = {
			isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
			membershipExceptions = (
				Sidebar/SidebarBottomBar.swift,
				Sidebar/SidebarHeader.swift,
				Sidebar/SpaceContextMenu.swift,
				Sidebar/SpacesList/SpacesList.swift,
				Sidebar/SpacesList/SpacesListItem.swift,
				Sidebar/SpacesSideBarView.swift,
			);
			target = 7F8340FB2E37F39400674A5D /* Nook */;
		};
/* End PBXFileSystemSynchronizedBuildFileExceptionSet section */

/* Begin PBXFileSystemSynchronizedRootGroup section */
		2CAC4D282E82457B00870189 /* Nook */ = {
			isa = PBXFileSystemSynchronizedRootGroup;
			exceptions = (
				2CAC4DD12E82457B00870189 /* Exceptions for "Nook" folder in "Nook" target */,
			);
			path = Nook;
			sourceTree = "<group>";
		};
		2D0834B32EC8C43B00C302AC /* App */ = {
			isa = PBXFileSystemSynchronizedRootGroup;
			path = App;
			sourceTree = "<group>";
		};
		2D0834BE2EC8CE0C00C302AC /* CommandPalette */ = {
			isa = PBXFileSystemSynchronizedRootGroup;
			exceptions = (
				2D0834BF2EC8CE1300C302AC /* Exceptions for "CommandPalette" folder in "Nook" target */,
			);
			path = CommandPalette;
			sourceTree = "<group>";
		};
		2D0834C72EC8D90400C302AC /* Settings */ = {
			isa = PBXFileSystemSynchronizedRootGroup;
			path = Settings;
			sourceTree = "<group>";
		};
		2D0834CF2EC8E0AD00C302AC /* Navigation */ = {
			isa = PBXFileSystemSynchronizedRootGroup;
			exceptions = (
				2D0834D12EC8E0AD00C302AC /* Exceptions for "Navigation" folder in "Nook" target */,
			);
			path = Navigation;
			sourceTree = "<group>";
		};
		564997D02E9B125200D89F78 /* UI */ = {
			isa = PBXFileSystemSynchronizedRootGroup;
			path = UI;
			sourceTree = "<group>";
		};
		7F6409912F4729B600A02697 /* Onboarding */ = {
			isa = PBXFileSystemSynchronizedRootGroup;
			path = Onboarding;
			sourceTree = "<group>";
		};
/* End PBXFileSystemSynchronizedRootGroup section */

/* Begin PBXFrameworksBuildPhase section */
		7F8340F92E37F39400674A5D /* Frameworks */ = {
			isa = PBXFrameworksBuildPhase;
			buildActionMask = 2147483647;
			files = (
				360021442EC6EC7F0016A41E /* Motion in Frameworks */,
				3600213C2EC6EC450016A41E /* Reeeed in Frameworks */,
				3600213F2EC6EC710016A41E /* LRUCache in Frameworks */,
				564997D82E9B24CC00D89F78 /* Garnish in Frameworks */,
				56C82BA92E9C2DB500DDD0D6 /* UniversalGlass in Frameworks */,
				7FAFC5DA2E3ADDCD009D7DC4 /* FaviconFinder in Frameworks */,
				360021302EC6EADB0016A41E /* RealModule in Frameworks */,
				7FE9E0EB2EE59D3500584E16 /* ColorfulX in Frameworks */,
				360021392EC6EC2F0016A41E /* Fuzi in Frameworks */,
				2C16A0262E87430B0070894B /* Sparkle in Frameworks */,
				360021362EC6EC150016A41E /* Highlightr in Frameworks */,
				360021422EC6EC7F0016A41E /* Graphing in Frameworks */,
				3600212C2EC6EADB0016A41E /* ComplexModule in Frameworks */,
				2D0822D82EC8A61800C302AC /* UniversalGlass in Frameworks */,
				3600212E2EC6EADB0016A41E /* Numerics in Frameworks */,
				360021332EC6EAEB0016A41E /* Atomics in Frameworks */,
				A1B2C3D62F0E6A0100ABCDEF /* MLXLLM in Frameworks */,
				A7A6FB442F70F4B8007C79C8 /* ContentBlockerConverter in Frameworks */,
			);
			runOnlyForDeploymentPostprocessing = 0;
		};
/* End PBXFrameworksBuildPhase section */

/* Begin PBXGroup section */
		7F8340F32E37F39400674A5D = {
			isa = PBXGroup;
			children = (
				7F6409912F4729B600A02697 /* Onboarding */,
				2D0834C72EC8D90400C302AC /* Settings */,
				2D0834CF2EC8E0AD00C302AC /* Navigation */,
				2D0834BE2EC8CE0C00C302AC /* CommandPalette */,
				7F8340FD2E37F39400674A5D /* Products */,
				564997D02E9B125200D89F78 /* UI */,
				2CAC4D282E82457B00870189 /* Nook */,
				2D0834B32EC8C43B00C302AC /* App */,
			);
			sourceTree = "<group>";
		};
		7F8340FD2E37F39400674A5D /* Products */ = {
			isa = PBXGroup;
			children = (
				7F8340FC2E37F39400674A5D /* Nook.app */,
			);
			name = Products;
			sourceTree = "<group>";
		};
/* End PBXGroup section */

/* Begin PBXNativeTarget section */
		7F8340FB2E37F39400674A5D /* Nook */ = {
			isa = PBXNativeTarget;
			buildConfigurationList = 7F8341082E37F39500674A5D /* Build configuration list for PBXNativeTarget "Nook" */;
			buildPhases = (
				7F8340F82E37F39400674A5D /* Sources */,
				7F8340F92E37F39400674A5D /* Frameworks */,
				7F8340FA2E37F39400674A5D /* Resources */,
			);
			buildRules = (
			);
			dependencies = (
			);
			fileSystemSynchronizedGroups = (
				2CAC4D282E82457B00870189 /* Nook */,
				2D0834B32EC8C43B00C302AC /* App */,
				2D0834C72EC8D90400C302AC /* Settings */,
				564997D02E9B125200D89F78 /* UI */,
				7F6409912F4729B600A02697 /* Onboarding */,
			);
			name = Nook;
			packageProductDependencies = (
				7FAFC5D92E3ADDCD009D7DC4 /* FaviconFinder */,
				2C16A0252E87430B0070894B /* Sparkle */,
				564997D72E9B24CC00D89F78 /* Garnish */,
				3600212B2EC6EADB0016A41E /* ComplexModule */,
				3600212D2EC6EADB0016A41E /* Numerics */,
				3600212F2EC6EADB0016A41E /* RealModule */,
				360021322EC6EAEB0016A41E /* Atomics */,
				360021352EC6EC150016A41E /* Highlightr */,
				360021382EC6EC2F0016A41E /* Fuzi */,
				3600213B2EC6EC450016A41E /* Reeeed */,
				3600213E2EC6EC710016A41E /* LRUCache */,
				360021412EC6EC7F0016A41E /* Graphing */,
				360021432EC6EC7F0016A41E /* Motion */,
				2D0822D72EC8A61800C302AC /* UniversalGlass */,
				7FE9E0EA2EE59D3500584E16 /* ColorfulX */,
				A1B2C3D52F0E6A0100ABCDEF /* MLXLLM */,
				B1F2E3D52F0F8B0100FACADE /* ContentBlockerConverter */,
			);
			productName = Pulse;
			productReference = 7F8340FC2E37F39400674A5D /* Nook.app */;
			productType = "com.apple.product-type.application";
		};
/* End PBXNativeTarget section */

/* Begin PBXProject section */
		7F8340F42E37F39400674A5D /* Project object */ = {
			isa = PBXProject;
			attributes = {
				BuildIndependentTargetsInParallel = 1;
				LastSwiftUpdateCheck = 2600;
				LastUpgradeCheck = 1640;
				TargetAttributes = {
					7F8340FB2E37F39400674A5D = {
						CreatedOnToolsVersion = 16.4;
					};
				};
			};
			buildConfigurationList = 7F8340F72E37F39400674A5D /* Build configuration list for PBXProject "Nook" */;
			developmentRegion = en;
			hasScannedForEncodings = 0;
			knownRegions = (
				en,
				Base,
			);
			mainGroup = 7F8340F32E37F39400674A5D;
			minimizedProjectReferenceProxies = 1;
			packageReferences = (
				7FAFC5D82E3ADDCD009D7DC4 /* XCRemoteSwiftPackageReference "FaviconFinder" */,
				2C16A0242E87430B0070894B /* XCRemoteSwiftPackageReference "Sparkle" */,
				564997D62E9B24CC00D89F78 /* XCRemoteSwiftPackageReference "Garnish" */,
				3600212A2EC6EADB0016A41E /* XCRemoteSwiftPackageReference "swift-numerics" */,
				360021312EC6EAEB0016A41E /* XCRemoteSwiftPackageReference "swift-atomics" */,
				360021342EC6EC150016A41E /* XCRemoteSwiftPackageReference "Highlightr" */,
				360021372EC6EC2F0016A41E /* XCRemoteSwiftPackageReference "Fuzi" */,
				3600213A2EC6EC450016A41E /* XCRemoteSwiftPackageReference "reeeed" */,
				3600213D2EC6EC710016A41E /* XCRemoteSwiftPackageReference "LRUCache" */,
				360021402EC6EC7F0016A41E /* XCRemoteSwiftPackageReference "Motion" */,
				2D0822D62EC8A61800C302AC /* XCRemoteSwiftPackageReference "universalglass" */,
				7FE9E0E92EE59D3500584E16 /* XCRemoteSwiftPackageReference "ColorfulX" */,
				A1B2C3D42F0E6A0100ABCDEF /* XCRemoteSwiftPackageReference "mlx-swift-lm" */,
				B1F2E3D42F0F8B0100FACADE /* XCRemoteSwiftPackageReference "SafariConverterLib" */,
			);
			preferredProjectObjectVersion = 77;
			productRefGroup = 7F8340FD2E37F39400674A5D /* Products */;
			projectDirPath = "";
			projectRoot = "";
			targets = (
				7F8340FB2E37F39400674A5D /* Nook */,
			);
		};
/* End PBXProject section */

/* Begin PBXResourcesBuildPhase section */
		7F8340FA2E37F39400674A5D /* Resources */ = {
			isa = PBXResourcesBuildPhase;
			buildActionMask = 2147483647;
			files = (
			);
			runOnlyForDeploymentPostprocessing = 0;
		};
/* End PBXResourcesBuildPhase section */

/* Begin PBXSourcesBuildPhase section */
		7F8340F82E37F39400674A5D /* Sources */ = {
			isa = PBXSourcesBuildPhase;
			buildActionMask = 2147483647;
			files = (
			);
			runOnlyForDeploymentPostprocessing = 0;
		};
/* End PBXSourcesBuildPhase section */

/* Begin XCBuildConfiguration section */
		7F8341062E37F39500674A5D /* Debug */ = {
			isa = XCBuildConfiguration;
			buildSettings = {
				ALWAYS_SEARCH_USER_PATHS = NO;
				ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
				CLANG_ANALYZER_NONNULL = YES;
				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
				CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
				CLANG_ENABLE_MODULES = YES;
				CLANG_ENABLE_OBJC_ARC = YES;
				CLANG_ENABLE_OBJC_WEAK = YES;
				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
				CLANG_WARN_BOOL_CONVERSION = YES;
				CLANG_WARN_COMMA = YES;
				CLANG_WARN_CONSTANT_CONVERSION = YES;
				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
				CLANG_WARN_EMPTY_BODY = YES;
				CLANG_WARN_ENUM_CONVERSION = YES;
				CLANG_WARN_INFINITE_RECURSION = YES;
				CLANG_WARN_INT_CONVERSION = YES;
				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
				CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
				CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
				CLANG_WARN_STRICT_PROTOTYPES = YES;
				CLANG_WARN_SUSPICIOUS_MOVE = YES;
				CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
				CLANG_WARN_UNREACHABLE_CODE = YES;
				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
				COPY_PHASE_STRIP = NO;
				DEBUG_INFORMATION_FORMAT = dwarf;
				DEVELOPMENT_TEAM = 96M8ZZRJK6;
				ENABLE_STRICT_OBJC_MSGSEND = YES;
				ENABLE_TESTABILITY = YES;
				ENABLE_USER_SCRIPT_SANDBOXING = YES;
				GCC_C_LANGUAGE_STANDARD = gnu17;
				GCC_DYNAMIC_NO_PIC = NO;
				GCC_NO_COMMON_BLOCKS = YES;
				GCC_OPTIMIZATION_LEVEL = 0;
				GCC_PREPROCESSOR_DEFINITIONS = (
					"DEBUG=1",
					"$(inherited)",
				);
				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
				GCC_WARN_UNDECLARED_SELECTOR = YES;
				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
				GCC_WARN_UNUSED_FUNCTION = YES;
				GCC_WARN_UNUSED_VARIABLE = YES;
				LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
				MACOSX_DEPLOYMENT_TARGET = 15.5;
				MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
				MTL_FAST_MATH = YES;
				ONLY_ACTIVE_ARCH = YES;
				SDKROOT = macosx;
				SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)";
				SWIFT_OPTIMIZATION_LEVEL = "-Onone";
			};
			name = Debug;
		};
		7F8341072E37F39500674A5D /* Release */ = {
			isa = XCBuildConfiguration;
			buildSettings = {
				ALWAYS_SEARCH_USER_PATHS = NO;
				ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
				CLANG_ANALYZER_NONNULL = YES;
				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
				CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
				CLANG_ENABLE_MODULES = YES;
				CLANG_ENABLE_OBJC_ARC = YES;
				CLANG_ENABLE_OBJC_WEAK = YES;
				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
				CLANG_WARN_BOOL_CONVERSION = YES;
				CLANG_WARN_COMMA = YES;
				CLANG_WARN_CONSTANT_CONVERSION = YES;
				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
				CLANG_WARN_EMPTY_BODY = YES;
				CLANG_WARN_ENUM_CONVERSION = YES;
				CLANG_WARN_INFINITE_RECURSION = YES;
				CLANG_WARN_INT_CONVERSION = YES;
				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
				CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
				CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
				CLANG_WARN_STRICT_PROTOTYPES = YES;
				CLANG_WARN_SUSPICIOUS_MOVE = YES;
				CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
				CLANG_WARN_UNREACHABLE_CODE = YES;
				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
				COPY_PHASE_STRIP = NO;
				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
				DEVELOPMENT_TEAM = 96M8ZZRJK6;
				ENABLE_NS_ASSERTIONS = NO;
				ENABLE_STRICT_OBJC_MSGSEND = YES;
				ENABLE_USER_SCRIPT_SANDBOXING = YES;
				GCC_C_LANGUAGE_STANDARD = gnu17;
				GCC_NO_COMMON_BLOCKS = YES;
				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
				GCC_WARN_UNDECLARED_SELECTOR = YES;
				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
				GCC_WARN_UNUSED_FUNCTION = YES;
				GCC_WARN_UNUSED_VARIABLE = YES;
				LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
				MACOSX_DEPLOYMENT_TARGET = 15.5;
				MTL_ENABLE_DEBUG_INFO = NO;
				MTL_FAST_MATH = YES;
				SDKROOT = macosx;
				SWIFT_COMPILATION_MODE = wholemodule;
			};
			name = Release;
		};
		7F8341092E37F39500674A5D /* Debug */ = {
			isa = XCBuildConfiguration;
			buildSettings = {
				ASSETCATALOG_COMPILER_APPICON_NAME = "logo-dev";
				ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
				AUTOMATION_APPLE_EVENTS = NO;
				CODE_SIGN_ENTITLEMENTS = Nook/Nook.entitlements;
				CODE_SIGN_IDENTITY = "Apple Development";
				"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
				CODE_SIGN_STYLE = Automatic;
				COMBINE_HIDPI_IMAGES = YES;
				CURRENT_PROJECT_VERSION = 107;
				DEVELOPMENT_TEAM = 9DLM793N9T;
				ENABLE_APP_SANDBOX = NO;
				ENABLE_HARDENED_RUNTIME = YES;
				ENABLE_PREVIEWS = YES;
				ENABLE_RESOURCE_ACCESS_PHOTO_LIBRARY = NO;
				GENERATE_INFOPLIST_FILE = YES;
				INFOPLIST_FILE = Nook/Info.plist;
				INFOPLIST_KEY_CFBundleDisplayName = Nook;
				INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.productivity";
				INFOPLIST_KEY_NSHumanReadableCopyright = "";
				INFOPLIST_KEY_NSPrincipalClass = NSApplication;
				LD_RUNPATH_SEARCH_PATHS = (
					"$(inherited)",
					"@executable_path/../Frameworks",
				);
				MARKETING_VERSION = 1.0.7;
				PRODUCT_BUNDLE_IDENTIFIER = io.browsewithnook.nook;
				PRODUCT_NAME = "$(TARGET_NAME)";
				PROVISIONING_PROFILE_SPECIFIER = "";
				REGISTER_APP_GROUPS = YES;
				RUNTIME_EXCEPTION_ALLOW_DYLD_ENVIRONMENT_VARIABLES = YES;
				RUNTIME_EXCEPTION_ALLOW_JIT = YES;
				RUNTIME_EXCEPTION_ALLOW_UNSIGNED_EXECUTABLE_MEMORY = NO;
				RUNTIME_EXCEPTION_DEBUGGING_TOOL = YES;
				RUNTIME_EXCEPTION_DISABLE_EXECUTABLE_PAGE_PROTECTION = YES;
				RUNTIME_EXCEPTION_DISABLE_LIBRARY_VALIDATION = YES;
				SWIFT_EMIT_LOC_STRINGS = YES;
				SWIFT_OBJC_BRIDGING_HEADER = "$(PROJECT_DIR)/Nook/Supporting Files/Nook-Bridging-Header.h";
				SWIFT_VERSION = 5.0;
			};
			name = Debug;
		};
		7F83410A2E37F39500674A5D /* Release */ = {
			isa = XCBuildConfiguration;
			buildSettings = {
				ASSETCATALOG_COMPILER_APPICON_NAME = logo;
				ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
				AUTOMATION_APPLE_EVENTS = NO;
				CODE_SIGN_ENTITLEMENTS = Nook/Nook.entitlements;
				CODE_SIGN_IDENTITY = "Apple Development";
				"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
				CODE_SIGN_STYLE = Automatic;
				COMBINE_HIDPI_IMAGES = YES;
				CURRENT_PROJECT_VERSION = 107;
				DEVELOPMENT_TEAM = 9DLM793N9T;
				ENABLE_APP_SANDBOX = NO;
				ENABLE_HARDENED_RUNTIME = YES;
				ENABLE_PREVIEWS = YES;
				ENABLE_RESOURCE_ACCESS_AUDIO_INPUT = NO;
				ENABLE_RESOURCE_ACCESS_CALENDARS = NO;
				ENABLE_RESOURCE_ACCESS_CAMERA = NO;
				ENABLE_RESOURCE_ACCESS_CONTACTS = NO;
				ENABLE_RESOURCE_ACCESS_LOCATION = NO;
				ENABLE_RESOURCE_ACCESS_PHOTO_LIBRARY = NO;
				GENERATE_INFOPLIST_FILE = YES;
				INFOPLIST_FILE = Nook/Info.plist;
				INFOPLIST_KEY_CFBundleDisplayName = Nook;
				INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.productivity";
				INFOPLIST_KEY_NSHumanReadableCopyright = "";
				INFOPLIST_KEY_NSPrincipalClass = NSApplication;
				LD_RUNPATH_SEARCH_PATHS = (
					"$(inherited)",
					"@executable_path/../Frameworks",
				);
				MARKETING_VERSION = 1.0.7;
				PRODUCT_BUNDLE_IDENTIFIER = io.browsewithnook.nook;
				PRODUCT_NAME = "$(TARGET_NAME)";
				PROVISIONING_PROFILE_SPECIFIER = "";
				REGISTER_APP_GROUPS = YES;
				RUNTIME_EXCEPTION_ALLOW_DYLD_ENVIRONMENT_VARIABLES = YES;
				RUNTIME_EXCEPTION_ALLOW_JIT = YES;
				RUNTIME_EXCEPTION_ALLOW_UNSIGNED_EXECUTABLE_MEMORY = NO;
				RUNTIME_EXCEPTION_DEBUGGING_TOOL = NO;
				RUNTIME_EXCEPTION_DISABLE_EXECUTABLE_PAGE_PROTECTION = NO;
				RUNTIME_EXCEPTION_DISABLE_LIBRARY_VALIDATION = YES;
				SWIFT_EMIT_LOC_STRINGS = YES;
				SWIFT_OBJC_BRIDGING_HEADER = "$(PROJECT_DIR)/Nook/Supporting Files/Nook-Bridging-Header.h";
				SWIFT_VERSION = 5.0;
			};
			name = Release;
		};
/* End XCBuildConfiguration section */

/* Begin XCConfigurationList section */
		7F8340F72E37F39400674A5D /* Build configuration list for PBXProject "Nook" */ = {
			isa = XCConfigurationList;
			buildConfigurations = (
				7F8341062E37F39500674A5D /* Debug */,
				7F8341072E37F39500674A5D /* Release */,
			);
			defaultConfigurationIsVisible = 0;
			defaultConfigurationName = Release;
		};
		7F8341082E37F39500674A5D /* Build configuration list for PBXNativeTarget "Nook" */ = {
			isa = XCConfigurationList;
			buildConfigurations = (
				7F8341092E37F39500674A5D /* Debug */,
				7F83410A2E37F39500674A5D /* Release */,
			);
			defaultConfigurationIsVisible = 0;
			defaultConfigurationName = Release;
		};
/* End XCConfigurationList section */

/* Begin XCRemoteSwiftPackageReference section */
		2C16A0242E87430B0070894B /* XCRemoteSwiftPackageReference "Sparkle" */ = {
			isa = XCRemoteSwiftPackageReference;
			repositoryURL = "https://github.com/sparkle-project/Sparkle";
			requirement = {
				kind = upToNextMajorVersion;
				minimumVersion = 2.8.0;
			};
		};
		2D0822D62EC8A61800C302AC /* XCRemoteSwiftPackageReference "universalglass" */ = {
			isa = XCRemoteSwiftPackageReference;
			repositoryURL = "https://github.com/aeastr/universalglass.git";
			requirement = {
				kind = upToNextMajorVersion;
				minimumVersion = 1.0.0;
			};
		};
		3600212A2EC6EADB0016A41E /* XCRemoteSwiftPackageReference "swift-numerics" */ = {
			isa = XCRemoteSwiftPackageReference;
			repositoryURL = "https://github.com/apple/swift-numerics.git";
			requirement = {
				kind = upToNextMajorVersion;
				minimumVersion = 1.1.1;
			};
		};
		360021312EC6EAEB0016A41E /* XCRemoteSwiftPackageReference "swift-atomics" */ = {
			isa = XCRemoteSwiftPackageReference;
			repositoryURL = "https://github.com/apple/swift-atomics.git";
			requirement = {
				kind = upToNextMajorVersion;
				minimumVersion = 1.3.0;
			};
		};
		360021342EC6EC150016A41E /* XCRemoteSwiftPackageReference "Highlightr" */ = {
			isa = XCRemoteSwiftPackageReference;
			repositoryURL = "https://github.com/raspu/Highlightr/";
			requirement = {
				kind = upToNextMajorVersion;
				minimumVersion = 2.3.0;
			};
		};
		360021372EC6EC2F0016A41E /* XCRemoteSwiftPackageReference "Fuzi" */ = {
			isa = XCRemoteSwiftPackageReference;
			repositoryURL = "https://github.com/cezheng/Fuzi";
			requirement = {
				kind = upToNextMajorVersion;
				minimumVersion = 3.1.3;
			};
		};
		3600213A2EC6EC450016A41E /* XCRemoteSwiftPackageReference "reeeed" */ = {
			isa = XCRemoteSwiftPackageReference;
			repositoryURL = "https://github.com/nate-parrott/reeeed";
			requirement = {
				kind = upToNextMajorVersion;
				minimumVersion = 1.0.1;
			};
		};
		3600213D2EC6EC710016A41E /* XCRemoteSwiftPackageReference "LRUCache" */ = {
			isa = XCRemoteSwiftPackageReference;
			repositoryURL = "https://github.com/nicklockwood/LRUCache.git";
			requirement = {
				kind = upToNextMajorVersion;
				minimumVersion = 1.2.0;
			};
		};
		360021402EC6EC7F0016A41E /* XCRemoteSwiftPackageReference "Motion" */ = {
			isa = XCRemoteSwiftPackageReference;
			repositoryURL = "https://github.com/b3ll/Motion";
			requirement = {
				branch = main;
				kind = branch;
			};
		};
		564997D62E9B24CC00D89F78 /* XCRemoteSwiftPackageReference "Garnish" */ = {
			isa = XCRemoteSwiftPackageReference;
			repositoryURL = "https://github.com/Aeastr/Garnish";
			requirement = {
				branch = main;
				kind = branch;
			};
		};
		56C82BA72E9C2DB500DDD0D6 /* XCRemoteSwiftPackageReference "UniversalGlass" */ = {
			isa = XCRemoteSwiftPackageReference;
			repositoryURL = "https://github.com/Aeastr/UniversalGlass";
			requirement = {
				branch = main;
				kind = branch;
			};
		};
		7FAFC5D82E3ADDCD009D7DC4 /* XCRemoteSwiftPackageReference "FaviconFinder" */ = {
			isa = XCRemoteSwiftPackageReference;
			repositoryURL = "https://github.com/will-lumley/FaviconFinder";
			requirement = {
				kind = upToNextMajorVersion;
				minimumVersion = 5.1.4;
			};
		};
		7FE9E0E92EE59D3500584E16 /* XCRemoteSwiftPackageReference "ColorfulX" */ = {
			isa = XCRemoteSwiftPackageReference;
			repositoryURL = "https://github.com/Lakr233/ColorfulX.git";
			requirement = {
				kind = upToNextMajorVersion;
				minimumVersion = 6.0.2;
			};
		};
		A1B2C3D42F0E6A0100ABCDEF /* XCRemoteSwiftPackageReference "mlx-swift-lm" */ = {
			isa = XCRemoteSwiftPackageReference;
			repositoryURL = "https://github.com/ml-explore/mlx-swift-lm";
			requirement = {
				kind = upToNextMajorVersion;
				minimumVersion = 2.30.6;
			};
		};
		B1F2E3D42F0F8B0100FACADE /* XCRemoteSwiftPackageReference "SafariConverterLib" */ = {
			isa = XCRemoteSwiftPackageReference;
			repositoryURL = "https://github.com/AdguardTeam/SafariConverterLib";
			requirement = {
				kind = upToNextMajorVersion;
				minimumVersion = 4.2.1;
			};
		};
/* End XCRemoteSwiftPackageReference section */

/* Begin XCSwiftPackageProductDependency section */
		2C16A0252E87430B0070894B /* Sparkle */ = {
			isa = XCSwiftPackageProductDependency;
			package = 2C16A0242E87430B0070894B /* XCRemoteSwiftPackageReference "Sparkle" */;
			productName = Sparkle;
		};
		2D0822D72EC8A61800C302AC /* UniversalGlass */ = {
			isa = XCSwiftPackageProductDependency;
			package = 2D0822D62EC8A61800C302AC /* XCRemoteSwiftPackageReference "universalglass" */;
			productName = UniversalGlass;
		};
		3600212B2EC6EADB0016A41E /* ComplexModule */ = {
			isa = XCSwiftPackageProductDependency;
			package = 3600212A2EC6EADB0016A41E /* XCRemoteSwiftPackageReference "swift-numerics" */;
			productName = ComplexModule;
		};
		3600212D2EC6EADB0016A41E /* Numerics */ = {
			isa = XCSwiftPackageProductDependency;
			package = 3600212A2EC6EADB0016A41E /* XCRemoteSwiftPackageReference "swift-numerics" */;
			productName = Numerics;
		};
		3600212F2EC6EADB0016A41E /* RealModule */ = {
			isa = XCSwiftPackageProductDependency;
			package = 3600212A2EC6EADB0016A41E /* XCRemoteSwiftPackageReference "swift-numerics" */;
			productName = RealModule;
		};
		360021322EC6EAEB0016A41E /* Atomics */ = {
			isa = XCSwiftPackageProductDependency;
			package = 360021312EC6EAEB0016A41E /* XCRemoteSwiftPackageReference "swift-atomics" */;
			productName = Atomics;
		};
		360021352EC6EC150016A41E /* Highlightr */ = {
			isa = XCSwiftPackageProductDependency;
			package = 360021342EC6EC150016A41E /* XCRemoteSwiftPackageReference "Highlightr" */;
			productName = Highlightr;
		};
		360021382EC6EC2F0016A41E /* Fuzi */ = {
			isa = XCSwiftPackageProductDependency;
			package = 360021372EC6EC2F0016A41E /* XCRemoteSwiftPackageReference "Fuzi" */;
			productName = Fuzi;
		};
		3600213B2EC6EC450016A41E /* Reeeed */ = {
			isa = XCSwiftPackageProductDependency;
			package = 3600213A2EC6EC450016A41E /* XCRemoteSwiftPackageReference "reeeed" */;
			productName = Reeeed;
		};
		3600213E2EC6EC710016A41E /* LRUCache */ = {
			isa = XCSwiftPackageProductDependency;
			package = 3600213D2EC6EC710016A41E /* XCRemoteSwiftPackageReference "LRUCache" */;
			productName = LRUCache;
		};
		360021412EC6EC7F0016A41E /* Graphing */ = {
			isa = XCSwiftPackageProductDependency;
			package = 360021402EC6EC7F0016A41E /* XCRemoteSwiftPackageReference "Motion" */;
			productName = Graphing;
		};
		360021432EC6EC7F0016A41E /* Motion */ = {
			isa = XCSwiftPackageProductDependency;
			package = 360021402EC6EC7F0016A41E /* XCRemoteSwiftPackageReference "Motion" */;
			productName = Motion;
		};
		564997D72E9B24CC00D89F78 /* Garnish */ = {
			isa = XCSwiftPackageProductDependency;
			package = 564997D62E9B24CC00D89F78 /* XCRemoteSwiftPackageReference "Garnish" */;
			productName = Garnish;
		};
		56C82BA82E9C2DB500DDD0D6 /* UniversalGlass */ = {
			isa = XCSwiftPackageProductDependency;
			package = 56C82BA72E9C2DB500DDD0D6 /* XCRemoteSwiftPackageReference "UniversalGlass" */;
			productName = UniversalGlass;
		};
		7FAFC5D92E3ADDCD009D7DC4 /* FaviconFinder */ = {
			isa = XCSwiftPackageProductDependency;
			package = 7FAFC5D82E3ADDCD009D7DC4 /* XCRemoteSwiftPackageReference "FaviconFinder" */;
			productName = FaviconFinder;
		};
		7FE9E0EA2EE59D3500584E16 /* ColorfulX */ = {
			isa = XCSwiftPackageProductDependency;
			package = 7FE9E0E92EE59D3500584E16 /* XCRemoteSwiftPackageReference "ColorfulX" */;
			productName = ColorfulX;
		};
		A1B2C3D52F0E6A0100ABCDEF /* MLXLLM */ = {
			isa = XCSwiftPackageProductDependency;
			package = A1B2C3D42F0E6A0100ABCDEF /* XCRemoteSwiftPackageReference "mlx-swift-lm" */;
			productName = MLXLLM;
		};
		B1F2E3D52F0F8B0100FACADE /* ContentBlockerConverter */ = {
			isa = XCSwiftPackageProductDependency;
			package = B1F2E3D42F0F8B0100FACADE /* XCRemoteSwiftPackageReference "SafariConverterLib" */;
			productName = ContentBlockerConverter;
		};
/* End XCSwiftPackageProductDependency section */
	};
	rootObject = 7F8340F42E37F39400674A5D /* Project object */;
}

```

## /Nook.xcodeproj/project.xcworkspace/contents.xcworkspacedata

```xcworkspacedata path="/Nook.xcodeproj/project.xcworkspace/contents.xcworkspacedata" 
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
   version = "1.0">
   <FileRef
      location = "self:">
   </FileRef>
</Workspace>

```

## /Nook.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings

```xcsettings path="/Nook.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings" 
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict/>
</plist>

```

## /Nook.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved

```resolved path="/Nook.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved" 
{
  "originHash" : "68d1d32f85ac866faef3e453f3d946881f9578c265728f3069236633b9a20eb9",
  "pins" : [
    {
      "identity" : "chronicle",
      "kind" : "remoteSourceControl",
      "location" : "https://github.com/Aeastr/Chronicle.git",
      "state" : {
        "revision" : "78f4d0d634c4834f13f7ab1bcca79c150c4c172a",
        "version" : "3.0.2"
      }
    },
    {
      "identity" : "colorfulx",
      "kind" : "remoteSourceControl",
      "location" : "https://github.com/Lakr233/ColorfulX.git",
      "state" : {
        "revision" : "2f008fcffc7f23811b2ba70eda77c57223815bc3",
        "version" : "6.0.2"
      }
    },
    {
      "identity" : "colorvector",
      "kind" : "remoteSourceControl",
      "location" : "https://github.com/Lakr233/ColorVector.git",
      "state" : {
        "revision" : "6da8726bf38d68eb943d0f2139ac2a1fac70e65b",
        "version" : "1.0.4"
      }
    },
    {
      "identity" : "faviconfinder",
      "kind" : "remoteSourceControl",
      "location" : "https://github.com/will-lumley/FaviconFinder",
      "state" : {
        "revision" : "11c2b27fbdc5fdfe0bf3addac01d27c40299a819",
        "version" : "5.1.5"
      }
    },
    {
      "identity" : "fuzi",
      "kind" : "remoteSourceControl",
      "location" : "https://github.com/cezheng/Fuzi",
      "state" : {
        "revision" : "f08c8323da21e985f3772610753bcfc652c2103f",
        "version" : "3.1.3"
      }
    },
    {
      "identity" : "garnish",
      "kind" : "remoteSourceControl",
      "location" : "https://github.com/Aeastr/Garnish",
      "state" : {
        "branch" : "main",
        "revision" : "ffbd0091ed25eb3e250ca47b5ccc8c4f9a6a3420"
      }
    },
    {
      "identity" : "highlightr",
      "kind" : "remoteSourceControl",
      "location" : "https://github.com/raspu/Highlightr/",
      "state" : {
        "revision" : "05e7fcc63b33925cd0c1faaa205cdd5681e7bbef",
        "version" : "2.3.0"
      }
    },
    {
      "identity" : "lrucache",
      "kind" : "remoteSourceControl",
      "location" : "https://github.com/nicklockwood/LRUCache.git",
      "state" : {
        "revision" : "cb5b2bd0da83ad29c0bec762d39f41c8ad0eaf3e",
        "version" : "1.2.1"
      }
    },
    {
      "identity" : "mlx-swift",
      "kind" : "remoteSourceControl",
      "location" : "https://github.com/ml-explore/mlx-swift",
      "state" : {
        "revision" : "6ba4827fb82c97d012eec9ab4b2de21f85c3b33d",
        "version" : "0.30.6"
      }
    },
    {
      "identity" : "mlx-swift-lm",
      "kind" : "remoteSourceControl",
      "location" : "https://github.com/ml-explore/mlx-swift-lm",
      "state" : {
        "revision" : "7e19e09027923d89ac47dd087d9627f610e5a91a",
        "version" : "2.30.6"
      }
    },
    {
      "identity" : "motion",
      "kind" : "remoteSourceControl",
      "location" : "https://github.com/b3ll/Motion",
      "state" : {
        "branch" : "main",
        "revision" : "c9a57f9d2b9d0a1e1b905753175e75a422d381d5"
      }
    },
    {
      "identity" : "msdisplaylink",
      "kind" : "remoteSourceControl",
      "location" : "https://github.com/Lakr233/MSDisplayLink.git",
      "state" : {
        "revision" : "ebf5823cb5fc1326639d9a05bc06d16bbe82989f",
        "version" : "2.0.8"
      }
    },
    {
      "identity" : "punycodeswift",
      "kind" : "remoteSourceControl",
      "location" : "https://github.com/gumob/PunycodeSwift.git",
      "state" : {
        "revision" : "30a462bdb4398ea835a3585472229e0d74b36ba5",
        "version" : "3.0.0"
      }
    },
    {
      "identity" : "reeeed",
      "kind" : "remoteSourceControl",
      "location" : "https://github.com/nate-parrott/reeeed",
      "state" : {
        "revision" : "567e2f50592f8a5fe41e9ff64784b42c77f35433",
        "version" : "1.0.1"
      }
    },
    {
      "identity" : "safariconverterlib",
      "kind" : "remoteSourceControl",
      "location" : "https://github.com/AdguardTeam/SafariConverterLib",
      "state" : {
        "revision" : "d4a4831943c82a838a04658f552b47c1beebecd7",
        "version" : "4.2.1"
      }
    },
    {
      "identity" : "sparkle",
      "kind" : "remoteSourceControl",
      "location" : "https://github.com/sparkle-project/Sparkle",
      "state" : {
        "revision" : "5581748cef2bae787496fe6d61139aebe0a451f6",
        "version" : "2.8.1"
      }
    },
    {
      "identity" : "springinterpolation",
      "kind" : "remoteSourceControl",
      "location" : "https://github.com/Lakr233/SpringInterpolation.git",
      "state" : {
        "revision" : "cdb556516daa9b43c16aae9436dd39e19ff930fd",
        "version" : "1.4.0"
      }
    },
    {
      "identity" : "swift-argument-parser",
      "kind" : "remoteSourceControl",
      "location" : "https://github.com/apple/swift-argument-parser",
      "state" : {
        "revision" : "41982a3656a71c768319979febd796c6fd111d5c",
        "version" : "1.5.0"
      }
    },
    {
      "identity" : "swift-asn1",
      "kind" : "remoteSourceControl",
      "location" : "https://github.com/apple/swift-asn1.git",
      "state" : {
        "revision" : "9f542610331815e29cc3821d3b6f488db8715517",
        "version" : "1.6.0"
      }
    },
    {
      "identity" : "swift-atomics",
      "kind" : "remoteSourceControl",
      "location" : "https://github.com/apple/swift-atomics.git",
      "state" : {
        "revision" : "b601256eab081c0f92f059e12818ac1d4f178ff7",
        "version" : "1.3.0"
      }
    },
    {
      "identity" : "swift-collections",
      "kind" : "remoteSourceControl",
      "location" : "https://github.com/apple/swift-collections.git",
      "state" : {
        "revision" : "6675bc0ff86e61436e615df6fc5174e043e57924",
        "version" : "1.4.1"
      }
    },
    {
      "identity" : "swift-crypto",
      "kind" : "remoteSourceControl",
      "location" : "https://github.com/apple/swift-crypto.git",
      "state" : {
        "revision" : "fa308c07a6fa04a727212d793e761460e41049c3",
        "version" : "4.3.0"
      }
    },
    {
      "identity" : "swift-jinja",
      "kind" : "remoteSourceControl",
      "location" : "https://github.com/huggingface/swift-jinja.git",
      "state" : {
        "revision" : "f731f03bf746481d4fda07f817c3774390c4d5b9",
        "version" : "2.3.2"
      }
    },
    {
      "identity" : "swift-log",
      "kind" : "remoteSourceControl",
      "location" : "https://github.com/apple/swift-log.git",
      "state" : {
        "revision" : "bbd81b6725ae874c69e9b8c8804d462356b55523",
        "version" : "1.10.1"
      }
    },
    {
      "identity" : "swift-numerics",
      "kind" : "remoteSourceControl",
      "location" : "https://github.com/apple/swift-numerics.git",
      "state" : {
        "revision" : "0c0290ff6b24942dadb83a929ffaaa1481df04a2",
        "version" : "1.1.1"
      }
    },
    {
      "identity" : "swift-psl",
      "kind" : "remoteSourceControl",
      "location" : "https://github.com/ameshkov/swift-psl",
      "state" : {
        "revision" : "1d8f7c69bd72abaceefceae90824a41bddf8afc0",
        "version" : "1.1.127"
      }
    },
    {
      "identity" : "swift-transformers",
      "kind" : "remoteSourceControl",
      "location" : "https://github.com/huggingface/swift-transformers",
      "state" : {
        "revision" : "150169bfba0889c229a2ce7494cf8949f18e6906",
        "version" : "1.1.9"
      }
    },
    {
      "identity" : "swiftsoup",
      "kind" : "remoteSourceControl",
      "location" : "https://github.com/scinfu/SwiftSoup.git",
      "state" : {
        "revision" : "d86f244ed497d48012782e2f59c985a55e77b3f5",
        "version" : "2.11.3"
      }
    },
    {
      "identity" : "universalglass",
      "kind" : "remoteSourceControl",
      "location" : "https://github.com/aeastr/universalglass.git",
      "state" : {
        "revision" : "9c084472801ac2c1b4c753668b275094c635b8a2",
        "version" : "1.1.0"
      }
    },
    {
      "identity" : "yyjson",
      "kind" : "remoteSourceControl",
      "location" : "https://github.com/ibireme/yyjson.git",
      "state" : {
        "revision" : "8b4a38dc994a110abaec8a400615567bd996105f",
        "version" : "0.12.0"
      }
    }
  ],
  "version" : 3
}

```

## /Nook.xcodeproj/xcshareddata/xcschemes/Nook.xcscheme

```xcscheme path="/Nook.xcodeproj/xcshareddata/xcschemes/Nook.xcscheme" 
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
   LastUpgradeVersion = "2600"
   version = "1.7">
   <BuildAction
      parallelizeBuildables = "YES"
      buildImplicitDependencies = "YES"
      buildArchitectures = "Automatic">
      <BuildActionEntries>
         <BuildActionEntry
            buildForTesting = "YES"
            buildForRunning = "YES"
            buildForProfiling = "YES"
            buildForArchiving = "YES"
            buildForAnalyzing = "YES">
            <BuildableReference
               BuildableIdentifier = "primary"
               BlueprintIdentifier = "7F8340FB2E37F39400674A5D"
               BuildableName = "Nook.app"
               BlueprintName = "Nook"
               ReferencedContainer = "container:Nook.xcodeproj">
            </BuildableReference>
         </BuildActionEntry>
      </BuildActionEntries>
   </BuildAction>
   <TestAction
      buildConfiguration = "Debug"
      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
      shouldUseLaunchSchemeArgsEnv = "YES"
      shouldAutocreateTestPlan = "YES">
      <Testables>
         <TestableReference
            skipped = "NO"
            parallelizable = "YES">
            <BuildableReference
               BuildableIdentifier = "primary"
               BlueprintIdentifier = "2C8925302E88DFF9002236DC"
               BuildableName = "NookUITests.xctest"
               BlueprintName = "NookUITests"
               ReferencedContainer = "container:Nook.xcodeproj">
            </BuildableReference>
         </TestableReference>
      </Testables>
   </TestAction>
   <LaunchAction
      buildConfiguration = "Debug"
      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
      launchStyle = "0"
      useCustomWorkingDirectory = "NO"
      ignoresPersistentStateOnLaunch = "NO"
      debugDocumentVersioning = "YES"
      debugServiceExtension = "internal"
      allowLocationSimulation = "YES">
      <BuildableProductRunnable
         runnableDebuggingMode = "0">
         <BuildableReference
            BuildableIdentifier = "primary"
            BlueprintIdentifier = "7F8340FB2E37F39400674A5D"
            BuildableName = "Nook.app"
            BlueprintName = "Nook"
            ReferencedContainer = "container:Nook.xcodeproj">
         </BuildableReference>
      </BuildableProductRunnable>
   </LaunchAction>
   <ProfileAction
      buildConfiguration = "Release"
      shouldUseLaunchSchemeArgsEnv = "YES"
      savedToolIdentifier = ""
      useCustomWorkingDirectory = "NO"
      debugDocumentVersioning = "YES">
      <BuildableProductRunnable
         runnableDebuggingMode = "0">
         <BuildableReference
            BuildableIdentifier = "primary"
            BlueprintIdentifier = "7F8340FB2E37F39400674A5D"
            BuildableName = "Nook.app"
            BlueprintName = "Nook"
            ReferencedContainer = "container:Nook.xcodeproj">
         </BuildableReference>
      </BuildableProductRunnable>
   </ProfileAction>
   <AnalyzeAction
      buildConfiguration = "Release">
   </AnalyzeAction>
   <ArchiveAction
      buildConfiguration = "Release"
      customArchiveName = "Nook Browser"
      revealArchiveInOrganizer = "YES">
   </ArchiveAction>
</Scheme>

```

## /Nook/Assets.xcassets/AccentColor.colorset/Contents.json

```json path="/Nook/Assets.xcassets/AccentColor.colorset/Contents.json" 
{
  "colors" : [
    {
      "idiom" : "universal"
    }
  ],
  "info" : {
    "author" : "xcode",
    "version" : 1
  }
}

```

## /Nook/Assets.xcassets/Browser Logos/Contents.json

```json path="/Nook/Assets.xcassets/Browser Logos/Contents.json" 
{
  "info" : {
    "author" : "xcode",
    "version" : 1
  }
}

```

## /Nook/Assets.xcassets/Browser Logos/arc-logo.imageset/Contents.json

```json path="/Nook/Assets.xcassets/Browser Logos/arc-logo.imageset/Contents.json" 
{
  "images" : [
    {
      "filename" : "Frame-2.png",
      "idiom" : "universal",
      "scale" : "1x"
    },
    {
      "idiom" : "universal",
      "scale" : "2x"
    },
    {
      "idiom" : "universal",
      "scale" : "3x"
    }
  ],
  "info" : {
    "author" : "xcode",
    "version" : 1
  }
}

```

## /Nook/Assets.xcassets/Browser Logos/arc-logo.imageset/Frame-2.png

Binary file available at https://raw.githubusercontent.com/nook-browser/Nook/refs/heads/main/Nook/Assets.xcassets/Browser Logos/arc-logo.imageset/Frame-2.png

## /Nook/Assets.xcassets/Browser Logos/chrome-logo.imageset/Contents.json

```json path="/Nook/Assets.xcassets/Browser Logos/chrome-logo.imageset/Contents.json" 
{
  "images" : [
    {
      "filename" : "Frame-1.png",
      "idiom" : "universal",
      "scale" : "1x"
    },
    {
      "idiom" : "universal",
      "scale" : "2x"
    },
    {
      "idiom" : "universal",
      "scale" : "3x"
    }
  ],
  "info" : {
    "author" : "xcode",
    "version" : 1
  }
}

```

## /Nook/Assets.xcassets/Browser Logos/chrome-logo.imageset/Frame-1.png

Binary file available at https://raw.githubusercontent.com/nook-browser/Nook/refs/heads/main/Nook/Assets.xcassets/Browser Logos/chrome-logo.imageset/Frame-1.png

## /Nook/Assets.xcassets/Browser Logos/dia-logo.imageset/Contents.json

```json path="/Nook/Assets.xcassets/Browser Logos/dia-logo.imageset/Contents.json" 
{
  "images" : [
    {
      "filename" : "Frame 255.png",
      "idiom" : "universal",
      "scale" : "1x"
    },
    {
      "idiom" : "universal",
      "scale" : "2x"
    },
    {
      "idiom" : "universal",
      "scale" : "3x"
    }
  ],
  "info" : {
    "author" : "xcode",
    "version" : 1
  }
}

```

## /Nook/Assets.xcassets/Browser Logos/dia-logo.imageset/Frame 255.png

Binary file available at https://raw.githubusercontent.com/nook-browser/Nook/refs/heads/main/Nook/Assets.xcassets/Browser Logos/dia-logo.imageset/Frame 255.png

## /Nook/Assets.xcassets/Browser Logos/firefox-logo.imageset/Contents.json

```json path="/Nook/Assets.xcassets/Browser Logos/firefox-logo.imageset/Contents.json" 
{
  "images" : [
    {
      "filename" : "Frame.png",
      "idiom" : "universal",
      "scale" : "1x"
    },
    {
      "idiom" : "universal",
      "scale" : "2x"
    },
    {
      "idiom" : "universal",
      "scale" : "3x"
    }
  ],
  "info" : {
    "author" : "xcode",
    "version" : 1
  }
}

```

## /Nook/Assets.xcassets/Browser Logos/firefox-logo.imageset/Frame.png

Binary file available at https://raw.githubusercontent.com/nook-browser/Nook/refs/heads/main/Nook/Assets.xcassets/Browser Logos/firefox-logo.imageset/Frame.png

## /Nook/Assets.xcassets/Browser Logos/safari-logo.imageset/Contents.json

```json path="/Nook/Assets.xcassets/Browser Logos/safari-logo.imageset/Contents.json" 
{
  "images" : [
    {
      "filename" : "safari-logo.png",
      "idiom" : "universal",
      "scale" : "1x"
    },
    {
      "idiom" : "universal",
      "scale" : "2x"
    },
    {
      "idiom" : "universal",
      "scale" : "3x"
    }
  ],
  "info" : {
    "author" : "xcode",
    "version" : 1
  }
}

```

## /Nook/Assets.xcassets/Browser Logos/safari-logo.imageset/safari-logo.png

Binary file available at https://raw.githubusercontent.com/nook-browser/Nook/refs/heads/main/Nook/Assets.xcassets/Browser Logos/safari-logo.imageset/safari-logo.png

## /Nook/Assets.xcassets/Browser Logos/zen-logo.imageset/Contents.json

```json path="/Nook/Assets.xcassets/Browser Logos/zen-logo.imageset/Contents.json" 
{
  "images" : [
    {
      "filename" : "zen-logo.png",
      "idiom" : "universal",
      "scale" : "1x"
    },
    {
      "idiom" : "universal",
      "scale" : "2x"
    },
    {
      "idiom" : "universal",
      "scale" : "3x"
    }
  ],
  "info" : {
    "author" : "xcode",
    "version" : 1
  }
}

```

## /Nook/Assets.xcassets/Browser Logos/zen-logo.imageset/zen-logo.png

Binary file available at https://raw.githubusercontent.com/nook-browser/Nook/refs/heads/main/Nook/Assets.xcassets/Browser Logos/zen-logo.imageset/zen-logo.png

## /Nook/Assets.xcassets/Contents.json

```json path="/Nook/Assets.xcassets/Contents.json" 
{
  "info" : {
    "author" : "xcode",
    "version" : 1
  }
}

```

## /Nook/Assets.xcassets/adblocker-off.imageset/Contents.json

```json path="/Nook/Assets.xcassets/adblocker-off.imageset/Contents.json" 
{
  "images" : [
    {
      "filename" : "adblocker-off.png",
      "idiom" : "universal",
      "scale" : "1x"
    },
    {
      "idiom" : "universal",
      "scale" : "2x"
    },
    {
      "idiom" : "universal",
      "scale" : "3x"
    }
  ],
  "info" : {
    "author" : "xcode",
    "version" : 1
  }
}

```

## /Nook/Assets.xcassets/adblocker-off.imageset/adblocker-off.png

Binary file available at https://raw.githubusercontent.com/nook-browser/Nook/refs/heads/main/Nook/Assets.xcassets/adblocker-off.imageset/adblocker-off.png

## /Nook/Assets.xcassets/adblocker-off.imageset/adblocker-on.png

Binary file available at https://raw.githubusercontent.com/nook-browser/Nook/refs/heads/main/Nook/Assets.xcassets/adblocker-off.imageset/adblocker-on.png

## /Nook/Assets.xcassets/adblocker-on.imageset/Contents.json

```json path="/Nook/Assets.xcassets/adblocker-on.imageset/Contents.json" 
{
  "images" : [
    {
      "filename" : "adblocker-on.png",
      "idiom" : "universal",
      "scale" : "1x"
    },
    {
      "idiom" : "universal",
      "scale" : "2x"
    },
    {
      "idiom" : "universal",
      "scale" : "3x"
    }
  ],
  "info" : {
    "author" : "xcode",
    "version" : 1
  }
}

```

## /Nook/Assets.xcassets/adblocker-on.imageset/adblocker-off.png

Binary file available at https://raw.githubusercontent.com/nook-browser/Nook/refs/heads/main/Nook/Assets.xcassets/adblocker-on.imageset/adblocker-off.png

## /Nook/Assets.xcassets/adblocker-on.imageset/adblocker-on.png

Binary file available at https://raw.githubusercontent.com/nook-browser/Nook/refs/heads/main/Nook/Assets.xcassets/adblocker-on.imageset/adblocker-on.png

## /Nook/Assets.xcassets/ai-chat-off.imageset/Contents.json

```json path="/Nook/Assets.xcassets/ai-chat-off.imageset/Contents.json" 
{
  "images" : [
    {
      "filename" : "ai-chat-off.png",
      "idiom" : "universal",
      "scale" : "1x"
    },
    {
      "idiom" : "universal",
      "scale" : "2x"
    },
    {
      "idiom" : "universal",
      "scale" : "3x"
    }
  ],
  "info" : {
    "author" : "xcode",
    "version" : 1
  }
}

```

## /Nook/Assets.xcassets/ai-chat-off.imageset/ai-chat-off.png

Binary file available at https://raw.githubusercontent.com/nook-browser/Nook/refs/heads/main/Nook/Assets.xcassets/ai-chat-off.imageset/ai-chat-off.png

## /Nook/Assets.xcassets/ai-chat-on.imageset/Contents.json

```json path="/Nook/Assets.xcassets/ai-chat-on.imageset/Contents.json" 
{
  "images" : [
    {
      "filename" : "ai-chat-on.png",
      "idiom" : "universal",
      "scale" : "1x"
    },
    {
      "idiom" : "universal",
      "scale" : "2x"
    },
    {
      "idiom" : "universal",
      "scale" : "3x"
    }
  ],
  "info" : {
    "author" : "xcode",
    "version" : 1
  }
}

```

## /Nook/Assets.xcassets/ai-chat-on.imageset/ai-chat-on.png

Binary file available at https://raw.githubusercontent.com/nook-browser/Nook/refs/heads/main/Nook/Assets.xcassets/ai-chat-on.imageset/ai-chat-on.png

## /Nook/Assets.xcassets/github.fill.symbolset/Contents.json

```json path="/Nook/Assets.xcassets/github.fill.symbolset/Contents.json" 
{
  "info" : {
    "author" : "xcode",
    "version" : 1
  },
  "symbols" : [
    {
      "filename" : "github.fill.svg",
      "idiom" : "universal"
    }
  ]
}

```

## /Nook/Assets.xcassets/noise_texture.imageset/Contents.json

```json path="/Nook/Assets.xcassets/noise_texture.imageset/Contents.json" 
{
  "images" : [
    {
      "filename" : "noise_texture.png",
      "idiom" : "universal",
      "scale" : "1x"
    },
    {
      "filename" : "noise_texture@2x.png",
      "idiom" : "universal",
      "scale" : "2x"
    },
    {
      "filename" : "noise_texture@3x.png",
      "idiom" : "universal",
      "scale" : "3x"
    }
  ],
  "info" : {
    "author" : "xcode",
    "version" : 1
  }
}


```

## /Nook/Assets.xcassets/noise_texture.imageset/noise_texture.png

Binary file available at https://raw.githubusercontent.com/nook-browser/Nook/refs/heads/main/Nook/Assets.xcassets/noise_texture.imageset/noise_texture.png

## /Nook/Assets.xcassets/noise_texture.imageset/noise_texture@2x.png

Binary file available at https://raw.githubusercontent.com/nook-browser/Nook/refs/heads/main/Nook/Assets.xcassets/noise_texture.imageset/noise_texture@2x.png

## /Nook/Assets.xcassets/noise_texture.imageset/noise_texture@3x.png

Binary file available at https://raw.githubusercontent.com/nook-browser/Nook/refs/heads/main/Nook/Assets.xcassets/noise_texture.imageset/noise_texture@3x.png

## /Nook/Assets.xcassets/nook-logo-1024.imageset/Contents.json

```json path="/Nook/Assets.xcassets/nook-logo-1024.imageset/Contents.json" 
{
  "images" : [
    {
      "filename" : "nook-logo-1024.png",
      "idiom" : "universal",
      "scale" : "1x"
    },
    {
      "idiom" : "universal",
      "scale" : "2x"
    },
    {
      "idiom" : "universal",
      "scale" : "3x"
    }
  ],
  "info" : {
    "author" : "xcode",
    "version" : 1
  }
}

```

## /Nook/Assets.xcassets/nook-logo-1024.imageset/nook-logo-1024.png

Binary file available at https://raw.githubusercontent.com/nook-browser/Nook/refs/heads/main/Nook/Assets.xcassets/nook-logo-1024.imageset/nook-logo-1024.png

## /Nook/Assets.xcassets/opencollective-fill.symbolset/Contents.json

```json path="/Nook/Assets.xcassets/opencollective-fill.symbolset/Contents.json" 
{
  "info" : {
    "author" : "xcode",
    "version" : 1
  },
  "symbols" : [
    {
      "filename" : "opencollective.symbols.svg",
      "idiom" : "universal"
    }
  ]
}

```

## /Nook/Assets.xcassets/opencollective-fill.symbolset/opencollective.symbols.svg

```svg path="/Nook/Assets.xcassets/opencollective-fill.symbolset/opencollective.symbols.svg" 
<?xml version="1.0" encoding="UTF-8"?>
<svg height="600" width="800" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
    <g id="Notes" font-family="'LucidaGrande', 'Lucida Grande', sans-serif" font-size="13">
        <rect fill="white" height="600.0" width="800.0" x="0.0" y="0.0" />
        <g font-size="13">
            <text x="18.0" y="176.0">Small</text>
            <text x="18.0" y="376.0">Medium</text>
            <text x="18.0" y="576.0">Large</text>
        </g>
        <g font-size="9">
            <text x="250.0" y="30.0">Ultralight</text>
            <text x="450.0" y="30.0">Regular</text>
            <text x="650.0" y="30.0">Black</text>
            <text id="template-version" fill="#505050" text-anchor="end" x="785.0" y="575.0">Template v.3.0</text>
            <a href="https://wangchujiang.com/#/app">
                <text fill="#505050" text-anchor="end" x="785.0" y="590.0">https://wangchujiang.com/#/app</text>
            </a>
        </g>
    </g>
    <g id="Guides" stroke="rgb(39, 170, 225)" stroke-width="0.5">
        <path id="Capline-S" d="M18,76 l800,0" />
        <path id="H-reference" d="M85,145.755 L87.685,145.755 L113.369,79.287 L114.052,79.287 L114.052,76 L112.148,76 L85,145.755 Z M95.693,121.536 L130.996,121.536 L130.263,119.313 L96.474,119.313 L95.693,121.536 Z M139.15,145.755 L141.787,145.755 L114.638,76 L113.466,76 L113.466,79.287 L139.15,145.755 Z" stroke="none" />
        <path id="Baseline-S" d="M18,146 l800,0" />
        <path id="left-margin-Ultralight-S" d="M211,56 l0,110" />
        <path id="right-margin-Ultralight-S" d="M319,56 l0,110" />
        <path id="left-margin-Regular-S" d="M411,56 l0,110" />
        <path id="right-margin-Regular-S" d="M519,56 l0,110" />
        <path id="left-margin-Black-S" d="M611,56 l0,110" />
        <path id="right-margin-Black-S" d="M719,56 l0,110" />
        <path id="Capline-M" d="M18,276 l800,0" />
        <path id="Baseline-M" d="M18,346 l800,0" />
        <path id="Capline-L" d="M18,476 l800,0" />
        <path id="Baseline-L" d="M18,546 l800,0" />
    </g>
    <g id="Symbols">
        <g id="Ultralight-S">
            <path d="M281.581,110.892 C281.581,114.143 280.606,117.286 278.98,119.887 L285.591,126.606 C288.842,122.271 290.901,116.744 290.901,110.892 C290.901,105.039 288.842,99.512 285.591,95.177 L278.98,101.897 C280.606,104.498 281.581,107.532 281.581,110.892 Z" />
            <path d="M265,127.581 C255.897,127.581 248.419,120.103 248.419,110.892 C248.419,101.68 255.897,94.202 265,94.202 C268.36,94.202 271.394,95.177 273.995,96.911 L280.606,90.192 C276.271,86.941 270.852,84.882 265,84.882 C250.695,84.882 238.99,96.478 238.99,111 C238.99,125.522 250.695,136.901 265,136.901 C270.961,136.901 276.379,134.842 280.823,131.591 L274.212,124.872 C271.611,126.498 268.468,127.473 265.108,127.473 L265,127.581 Z" />
        </g>
        <g id="Regular-S">
            <path d="M481.581,110.892 C481.581,114.143 480.606,117.286 478.98,119.887 L485.591,126.606 C488.842,122.271 490.901,116.744 490.901,110.892 C490.901,105.039 488.842,99.512 485.591,95.177 L478.98,101.897 C480.606,104.498 481.581,107.532 481.581,110.892 Z" />
            <path d="M465,127.581 C455.897,127.581 448.419,120.103 448.419,110.892 C448.419,101.68 455.897,94.202 465,94.202 C468.36,94.202 471.394,95.177 473.995,96.911 L480.606,90.192 C476.271,86.941 470.852,84.882 465,84.882 C450.695,84.882 438.99,96.478 438.99,111 C438.99,125.522 450.695,136.901 465,136.901 C470.961,136.901 476.379,134.842 480.823,131.591 L474.212,124.872 C471.611,126.498 468.468,127.473 465.108,127.473 L465,127.581 Z" />
        </g>
        <g id="Black-S">
            <path d="M681.581,110.892 C681.581,114.143 680.606,117.286 678.98,119.887 L685.591,126.606 C688.842,122.271 690.901,116.744 690.901,110.892 C690.901,105.039 688.842,99.512 685.591,95.177 L678.98,101.897 C680.606,104.498 681.581,107.532 681.581,110.892 Z" />
            <path d="M665,127.581 C655.897,127.581 648.419,120.103 648.419,110.892 C648.419,101.68 655.897,94.202 665,94.202 C668.36,94.202 671.394,95.177 673.995,96.911 L680.606,90.192 C676.271,86.941 670.852,84.882 665,84.882 C650.695,84.882 638.99,96.478 638.99,111 C638.99,125.522 650.695,136.901 665,136.901 C670.961,136.901 676.379,134.842 680.823,131.591 L674.212,124.872 C671.611,126.498 668.468,127.473 665.108,127.473 L665,127.581 Z" />
        </g>
    </g>
</svg>
```

## /Nook/Assets.xcassets/plainBackgroundColor.colorset/Contents.json

```json path="/Nook/Assets.xcassets/plainBackgroundColor.colorset/Contents.json" 
{
  "colors" : [
    {
      "color" : {
        "color-space" : "extended-gray",
        "components" : {
          "alpha" : "1.000",
          "white" : "0xFF"
        }
      },
      "idiom" : "mac"
    },
    {
      "appearances" : [
        {
          "appearance" : "luminosity",
          "value" : "dark"
        }
      ],
      "color" : {
        "color-space" : "extended-gray",
        "components" : {
          "alpha" : "1.000",
          "white" : "0x00"
        }
      },
      "idiom" : "mac"
    }
  ],
  "info" : {
    "author" : "xcode",
    "version" : 1
  }
}

```

## /Nook/Assets.xcassets/sidebar.imageset/Contents.json

```json path="/Nook/Assets.xcassets/sidebar.imageset/Contents.json" 
{
  "images" : [
    {
      "filename" : "sidebar.png",
      "idiom" : "universal",
      "scale" : "1x"
    },
    {
      "idiom" : "universal",
      "scale" : "2x"
    },
    {
      "idiom" : "universal",
      "scale" : "3x"
    }
  ],
  "info" : {
    "author" : "xcode",
    "version" : 1
  }
}

```

## /Nook/Assets.xcassets/sidebar.imageset/sidebar.png

Binary file available at https://raw.githubusercontent.com/nook-browser/Nook/refs/heads/main/Nook/Assets.xcassets/sidebar.imageset/sidebar.png

## /Nook/Assets.xcassets/top-of-window.imageset/Contents.json

```json path="/Nook/Assets.xcassets/top-of-window.imageset/Contents.json" 
{
  "images" : [
    {
      "filename" : "topofwindow.png",
      "idiom" : "universal",
      "scale" : "1x"
    },
    {
      "idiom" : "universal",
      "scale" : "2x"
    },
    {
      "idiom" : "universal",
      "scale" : "3x"
    }
  ],
  "info" : {
    "author" : "xcode",
    "version" : 1
  }
}

```

## /Nook/Assets.xcassets/top-of-window.imageset/topofwindow.png

Binary file available at https://raw.githubusercontent.com/nook-browser/Nook/refs/heads/main/Nook/Assets.xcassets/top-of-window.imageset/topofwindow.png

## /Nook/Assets.xcassets/tulips.imageset/Contents.json

```json path="/Nook/Assets.xcassets/tulips.imageset/Contents.json" 
{
  "images" : [
    {
      "idiom" : "universal",
      "scale" : "1x"
    },
    {
      "filename" : "michael-loftus-aK4Slh-4uhU-unsplash.jpg",
      "idiom" : "universal",
      "scale" : "2x"
    },
    {
      "idiom" : "universal",
      "scale" : "3x"
    }
  ],
  "info" : {
    "author" : "xcode",
    "version" : 1
  }
}

```

## /Nook/Assets.xcassets/tulips.imageset/michael-loftus-aK4Slh-4uhU-unsplash.jpg

Binary file available at https://raw.githubusercontent.com/nook-browser/Nook/refs/heads/main/Nook/Assets.xcassets/tulips.imageset/michael-loftus-aK4Slh-4uhU-unsplash.jpg

## /Nook/Assets.xcassets/url-in-sidebar.imageset/Contents.json

```json path="/Nook/Assets.xcassets/url-in-sidebar.imageset/Contents.json" 
{
  "images" : [
    {
      "filename" : "url-in-sidebar.png",
      "idiom" : "universal",
      "scale" : "1x"
    },
    {
      "idiom" : "universal",
      "scale" : "2x"
    },
    {
      "idiom" : "universal",
      "scale" : "3x"
    }
  ],
  "info" : {
    "author" : "xcode",
    "version" : 1
  }
}

```

## /Nook/Assets.xcassets/url-in-sidebar.imageset/url-in-sidebar.png

Binary file available at https://raw.githubusercontent.com/nook-browser/Nook/refs/heads/main/Nook/Assets.xcassets/url-in-sidebar.imageset/url-in-sidebar.png

## /Nook/Assets.xcassets/url-top-of-website.imageset/Contents.json

```json path="/Nook/Assets.xcassets/url-top-of-website.imageset/Contents.json" 
{
  "images" : [
    {
      "filename" : "url-top-of-website.png",
      "idiom" : "universal",
      "scale" : "1x"
    },
    {
      "idiom" : "universal",
      "scale" : "2x"
    },
    {
      "idiom" : "universal",
      "scale" : "3x"
    }
  ],
  "info" : {
    "author" : "xcode",
    "version" : 1
  }
}

```

## /Nook/Assets.xcassets/url-top-of-website.imageset/url-top-of-website.png

Binary file available at https://raw.githubusercontent.com/nook-browser/Nook/refs/heads/main/Nook/Assets.xcassets/url-top-of-website.imageset/url-top-of-website.png

## /Nook/Assets.xcassets/windowBackgroundColor.colorset/Contents.json

```json path="/Nook/Assets.xcassets/windowBackgroundColor.colorset/Contents.json" 
{
  "colors" : [
    {
      "color" : {
        "platform" : "osx",
        "reference" : "windowBackgroundColor"
      },
      "idiom" : "mac"
    },
    {
      "appearances" : [
        {
          "appearance" : "luminosity",
          "value" : "dark"
        }
      ],
      "color" : {
        "platform" : "osx",
        "reference" : "windowBackgroundColor"
      },
      "idiom" : "mac"
    }
  ],
  "info" : {
    "author" : "xcode",
    "version" : 1
  }
}

```

## /Nook/Components/Boosts - deprecated/BoostColorCanvas.swift

```swift path="/Nook/Components/Boosts - deprecated/BoostColorCanvas.swift" 
//
//  BoostColorCanvas.swift
//  Nook
//
//  Created by Jude on 11/11/2025.
//

import SwiftUI

#if canImport(AppKit)
    import AppKit
#endif

// MARK: - BoostColorCanvas
// Circular canvas for picking tint color (similar to GradientCanvasEditor but single color)
struct BoostColorCanvas: View {
    @Binding var selectedColor: Color
    var onColorChange: ((Color) -> Void)?

    @State private var handlePosition: CGPoint?
    @State private var lightness: Double = 0.6

    private let cornerRadius: CGFloat = 16

    var body: some View {
        GeometryReader { proxy in
            let width = proxy.size.width
            let height = proxy.size.height
            let padding: CGFloat = 24
            let center = CGPoint(x: width / 2, y: height / 2)
            let radius = min(width, height) / 2 - padding

            ZStack {
                // Dot grid background
                DotGrid()
                    .clipShape(RoundedRectangle(cornerRadius: cornerRadius, style: .continuous))
                    .allowsHitTesting(false)

                // Draggable color handle
                if let position = handlePosition {
                    let clamped = clampToCircle(point: position, center: center, radius: radius)

                    ColorHandle(color: selectedColor, size: 40)
                        .position(clamped)
                        .gesture(
                            DragGesture(minimumDistance: 0)
                                .onChanged { value in
                                    let clamped = clampToCircle(
                                        point: value.location, center: center, radius: radius)
                                    handlePosition = clamped
                                    updateColorFromPosition(clamped, center: center, radius: radius)
                                }
                        )
                }

                // Border stroke
                RoundedRectangle(cornerRadius: cornerRadius, style: .continuous)
                    .strokeBorder(Color.primary.opacity(0.15), lineWidth: 1)
                    .allowsHitTesting(false)
            }
            .onAppear {
                if handlePosition == nil {
                    // Initialize handle position from current color
                    handlePosition = positionFromColor(
                        selectedColor, center: center, radius: radius)
                }
            }
        }
        .frame(height: 280)
    }

    // MARK: - Helper Methods

    private func clampToCircle(point: CGPoint, center: CGPoint, radius: CGFloat) -> CGPoint {
        let dx = point.x - center.x
        let dy = point.y - center.y
        let distance = sqrt(dx * dx + dy * dy)

        if distance <= radius {
            return point
        } else {
            let angle = atan2(dy, dx)
            return CGPoint(
                x: center.x + cos(angle) * radius,
                y: center.y + sin(angle) * radius
            )
        }
    }

    private func updateColorFromPosition(_ position: CGPoint, center: CGPoint, radius: CGFloat) {
        let color = colorFromCircle(
            point: position, center: center, radius: radius, lightness: lightness)
        selectedColor = color
        onColorChange?(color)
    }

    private func colorFromCircle(
        point: CGPoint, center: CGPoint, radius: CGFloat, lightness: Double
    ) -> Color {
        let dx = point.x - center.x
        let dy = point.y - center.y
        let distance = sqrt(dx * dx + dy * dy)

        // Angle determines hue (0-360 degrees)
        var angle = atan2(dy, dx) * 180.0 / .pi
        if angle < 0 { angle += 360 }
        let hue = angle / 360.0

        // Distance from center determines saturation (0-1)
        let saturation = min(1.0, distance / radius)

        // Use fixed lightness
        return Color(hue: hue, saturation: saturation, brightness: lightness)
    }

    private func positionFromColor(_ color: Color, center: CGPoint, radius: CGFloat) -> CGPoint {
        #if canImport(AppKit)
            let nsColor = NSColor(color)
            var hue: CGFloat = 0
            var saturation: CGFloat = 0
            var brightness: CGFloat = 0
            var alpha: CGFloat = 0

            nsColor.usingColorSpace(.deviceRGB)?.getHue(
                &hue, saturation: &saturation, brightness: &brightness, alpha: &alpha)

            // Update lightness from color
            lightness = brightness

            // Convert hue to angle (0-360 degrees)
            let angle = hue * 360.0 * .pi / 180.0

            // Convert saturation to distance
            let distance = saturation * radius

            return CGPoint(
                x: center.x + cos(angle) * distance,
                y: center.y + sin(angle) * distance
            )
        #else
            return center
        #endif
    }
}

// MARK: - ColorHandle
private struct ColorHandle: View {
    let color: Color
    let size: CGFloat

    var body: some View {
        ZStack {
            Circle()
                .fill(color)
                .frame(width: size, height: size)

            Circle()
                .strokeBorder(Color.white, lineWidth: 3)
                .frame(width: size, height: size)

            Circle()
                .strokeBorder(Color.black.opacity(0.2), lineWidth: 1)
                .frame(width: size, height: size)
        }
        .shadow(color: Color.black.opacity(0.3), radius: 8, y: 4)
    }
}

// MARK: - DotGrid (reuse from GradientCanvasEditor)
private struct DotGrid: View {
    var body: some View {
        Canvas { context, size in
            let dotSize: CGFloat = 2
            let spacing: CGFloat = 12
            let dotColor = Color.primary.opacity(0.08)

            let cols = Int(size.width / spacing)
            let rows = Int(size.height / spacing)

            for row in 0..<rows {
                for col in 0..<cols {
                    let x = CGFloat(col) * spacing
                    let y = CGFloat(row) * spacing
                    let rect = CGRect(x: x, y: y, width: dotSize, height: dotSize)
                    context.fill(Path(ellipseIn: rect), with: .color(dotColor))
                }
            }
        }
    }
}

#Preview {
    @Previewable @State var color = Color.red

    return BoostColorCanvas(selectedColor: $color) { newColor in
        print("Color changed: \(newColor)")
    }
    .padding(40)
}

```

## /Nook/Components/Boosts/BoostCodeButton.swift

```swift path="/Nook/Components/Boosts/BoostCodeButton.swift" 
//
//  BoostCodeButton.swift
//  nook-components
//
//  Created by Maciek Bagiński on 12/11/2025.
//

import SwiftUI

struct BoostCodeButton: View {
    @State private var isHovered: Bool = false
    var isActive: Bool
    var onClick: () -> Void

    var body: some View {
        Button {
            onClick()
        } label: {
            HStack {
                Text("Code")
                    .font(
                        .system(size: 14, weight: .semibold, design: .rounded)
                    )
                    .foregroundStyle(
                        isActive ? .white.opacity(0.8) : .black.opacity(0.75)
                    )
                Spacer()
                Text("{}")
                    .font(
                        .system(size: 14, weight: .semibold, design: .rounded)
                    )
                    .foregroundStyle(
                        isActive ? .white.opacity(0.8) : .black.opacity(0.75)
                    )

            }
            .padding(.horizontal, 12)
            .padding(.vertical, 10)
            .background(
                isActive
                    ? .black.opacity(0.8)
                    : isHovered ? .black.opacity(0.1) : .black.opacity(0.07)
            )
            .clipShape(RoundedRectangle(cornerRadius: 8))
            .animation(.linear(duration: 0.1), value: isActive)
        }
        .buttonStyle(PlainButtonStyle())
        .onHover { state in
            isHovered = state
        }
    }
}

#Preview {
    BoostCodeButton(isActive: false, onClick: {})
        .frame(width: 300, height: 300)
}

```

## /Nook/Components/Boosts/BoostZapButton.swift

```swift path="/Nook/Components/Boosts/BoostZapButton.swift" 
//
//  BoostZapButton.swift
//  nook-components
//
//  Created by Maciek Bagiński on 12/11/2025.
//

import SwiftUI

struct BoostZapButton: View {
    @State private var isHovered: Bool = false
    @Binding var isActive: Bool
    var onClick: () -> Void

    var body: some View {
        Button {
            isActive.toggle()
            onClick()
        } label: {
            HStack {
                Text("Zap")
                    .font(
                        .system(size: 14, weight: .semibold, design: .rounded)
                    )
                    .foregroundStyle(
                        isActive ? .white.opacity(0.8) : .black.opacity(0.75)
                    )
                Spacer()
                Image(systemName: "bolt.fill")
                    .font(
                        .system(size: 14, weight: .semibold, design: .rounded)
                    )
                    .foregroundStyle(
                        isActive ? .white.opacity(0.8) : .black.opacity(0.75)
                    )

            }
            .padding(.horizontal, 12)
            .padding(.vertical, 10)
            .background(
                isActive
                    ? .black.opacity(0.8)
                    : isHovered ? .black.opacity(0.1) : .black.opacity(0.07)
            )
            .clipShape(RoundedRectangle(cornerRadius: 8))
            .animation(.linear(duration: 0.1), value: isActive)
        }
        .buttonStyle(PlainButtonStyle())
        .onHover { state in
            isHovered = state
        }
    }
}

#Preview {
    @Previewable @State var isActive = false
    BoostZapButton(isActive: $isActive, onClick: {})
        .frame(width: 300, height: 300)
}

```

## /Nook/Components/Sidebar/URLBarFramePreferenceKey.swift

```swift path="/Nook/Components/Sidebar/URLBarFramePreferenceKey.swift" 
import SwiftUI

struct URLBarFramePreferenceKey: PreferenceKey {
    static var defaultValue: CGRect = .zero
    static func reduce(value: inout CGRect, nextValue: () -> CGRect) {
        value = nextValue()
    }
}


```


The content has been capped at 50000 tokens. The user could consider applying other filters to refine the result. The better and more specific the context, the better the LLM can follow instructions. If the context seems verbose, the user can refine the filter using uithub. Thank you for using https://uithub.com - Perfect LLM context for any GitHub repo.
Copied!