Browse Source

Improve paywall layout and disable traffic lights while it is shown.

Pin footer links to the bottom with even spacing, add top content inset, and keep window controls visible but non-interactive during the paywall flow.

Co-authored-by: Cursor <cursoragent@cursor.com>
AhtashamShahzad1 3 weeks ago
parent
commit
35e6c9ddd4

+ 1 - 1
clone _of_clarus_ai_chat_bot/Utilities/MainHostingView.swift

@@ -8,7 +8,7 @@ private enum TrafficLightHitRegion {
 /// Passes mouse events in the traffic-light region through to the native window controls.
 final class MainHostingView<Content: View>: NSHostingView<Content> {
     override func hitTest(_ point: NSPoint) -> NSView? {
-        if isTrafficLightPoint(point) {
+        if WindowChromeState.allowsTrafficLightInteraction, isTrafficLightPoint(point) {
             return nil
         }
         return super.hitTest(point)

+ 5 - 0
clone _of_clarus_ai_chat_bot/Utilities/WindowChromeState.swift

@@ -0,0 +1,5 @@
+import Foundation
+
+enum WindowChromeState {
+    static var allowsTrafficLightInteraction = true
+}

+ 8 - 2
clone _of_clarus_ai_chat_bot/Utilities/WindowConfigurator.swift

@@ -8,10 +8,16 @@ enum WindowConfigurator {
         window.titleVisibility = .hidden
         window.isMovableByWindowBackground = true
         window.backgroundColor = NSColor(AppTheme.pageBackground)
+        setTrafficLightsEnabled(true, on: window)
+    }
+
+    static func setTrafficLightsEnabled(_ isEnabled: Bool, on window: NSWindow) {
+        WindowChromeState.allowsTrafficLightInteraction = isEnabled
 
         for buttonType in [NSWindow.ButtonType.closeButton, .miniaturizeButton, .zoomButton] {
-            window.standardWindowButton(buttonType)?.isEnabled = true
-            window.standardWindowButton(buttonType)?.isHidden = false
+            guard let button = window.standardWindowButton(buttonType) else { continue }
+            button.isHidden = false
+            button.isEnabled = isEnabled
         }
     }
 }

+ 23 - 0
clone _of_clarus_ai_chat_bot/Utilities/WindowTrafficLightsModifier.swift

@@ -0,0 +1,23 @@
+import AppKit
+import SwiftUI
+
+private struct WindowTrafficLightsAccessor: NSViewRepresentable {
+    let isEnabled: Bool
+
+    func makeNSView(context: Context) -> NSView {
+        NSView()
+    }
+
+    func updateNSView(_ nsView: NSView, context: Context) {
+        DispatchQueue.main.async {
+            guard let window = nsView.window else { return }
+            WindowConfigurator.setTrafficLightsEnabled(isEnabled, on: window)
+        }
+    }
+}
+
+extension View {
+    func windowTrafficLightsInteractive(_ isInteractive: Bool) -> some View {
+        background(WindowTrafficLightsAccessor(isEnabled: isInteractive))
+    }
+}

+ 5 - 2
clone _of_clarus_ai_chat_bot/Views/HomeView.swift

@@ -12,10 +12,13 @@ struct HomeView: View {
 
             if viewModel.isPaywallPresented, let paywallViewModel = viewModel.paywallViewModel {
                 PaywallView(viewModel: paywallViewModel)
-                .transition(.opacity.combined(with: .move(edge: .bottom)))
-                .zIndex(1)
+                    .frame(maxWidth: .infinity, maxHeight: .infinity)
+                    .ignoresSafeArea()
+                    .transition(.opacity.combined(with: .move(edge: .bottom)))
+                    .zIndex(1)
             }
         }
+        .windowTrafficLightsInteractive(!viewModel.isPaywallPresented)
         .animation(.easeInOut(duration: 0.25), value: viewModel.isPaywallPresented)
         .frame(minWidth: AppTheme.windowMinWidth, minHeight: AppTheme.windowMinHeight)
         .frame(maxWidth: .infinity, maxHeight: .infinity)

+ 17 - 5
clone _of_clarus_ai_chat_bot/Views/PaywallView.swift

@@ -23,14 +23,14 @@ struct PaywallView: View {
                     .padding(.bottom, 20)
 
                 ctaSection
-                    .padding(.bottom, 16)
-
-                legalLinks
 
                 Spacer(minLength: 0)
+
+                legalLinks
             }
             .padding(.horizontal, 48)
-            .padding(.vertical, 28)
+            .padding(.top, 82)
+            .padding(.bottom, 20)
             .frame(maxWidth: 920)
             .frame(maxWidth: .infinity, maxHeight: .infinity)
         }
@@ -185,22 +185,31 @@ struct PaywallView: View {
     // MARK: - Legal Links
 
     private var legalLinks: some View {
-        HStack(spacing: 16) {
+        HStack(spacing: 0) {
             PaywallLinkButton(title: "Continue with Free Plan") {
                 viewModel.dismiss()
             }
+            .frame(maxWidth: .infinity)
+
             PaywallLinkButton(title: "Restore Purchase") {
                 viewModel.restorePurchases()
             }
+            .frame(maxWidth: .infinity)
+
             PaywallLinkButton(title: "Privacy Policy") {
                 openURL(AppLinks.privacyPolicy)
             }
+            .frame(maxWidth: .infinity)
+
             PaywallLinkButton(title: "Support") {
                 openURL(AppLinks.support)
             }
+            .frame(maxWidth: .infinity)
+
             PaywallLinkButton(title: "Terms") {
                 openURL(AppLinks.termsOfUse)
             }
+            .frame(maxWidth: .infinity)
         }
         .frame(maxWidth: .infinity)
     }
@@ -388,6 +397,9 @@ private struct PaywallLinkButton: View {
                 .font(.system(size: 11))
                 .foregroundStyle(AppTheme.textSecondary)
                 .underline(false)
+                .multilineTextAlignment(.center)
+                .lineLimit(2)
+                .frame(maxWidth: .infinity)
         }
         .buttonStyle(.plain)
         .accessibilityLabel(title)