소스 검색

Align sidebar branding with tool page headers and clear traffic lights.

Introduce shared header inset constants so Gramora and page titles line up, while adding top clearance for the sidebar logo and nav spacing below it.

Co-authored-by: Cursor <cursoragent@cursor.com>
AhtashamShahzad1 1 개월 전
부모
커밋
5721c84719

+ 15 - 0
gramora/Utilities/AppTheme.swift

@@ -7,7 +7,22 @@ enum AppTheme {
     static let windowMinHeight: CGFloat = windowHeight
 
     static let sidebarWidth: CGFloat = 240
+    static let headerTopInset: CGFloat = 24
+    static let logoMarkSize: CGFloat = 36
+    /// Extra top inset so the sidebar logo clears the window traffic lights.
+    static let trafficLightClearance: CGFloat = 16
     static let contentPadding: CGFloat = 40
+
+    static var sidebarLogoTopInset: CGFloat {
+        headerTopInset + trafficLightClearance
+    }
+
+    static let sidebarLogoBottomSpacing: CGFloat = 20
+
+    /// Aligns tool page titles with the sidebar "Gramora" label.
+    static var brandLabelTopInset: CGFloat {
+        headerTopInset + (logoMarkSize - 22) / 2
+    }
     static let cardCornerRadius: CGFloat = 16
     static let buttonCornerRadius: CGFloat = 14
     static let navItemCornerRadius: CGFloat = 10

+ 1 - 2
gramora/Views/DictionaryView.swift

@@ -8,7 +8,6 @@ struct DictionaryView: View {
     var body: some View {
         GeometryReader { proxy in
             let dynamicHorizontalPadding = min(max(proxy.size.width * 0.045, AppTheme.contentPadding), 52)
-            let dynamicTopPadding = min(max(proxy.size.height * 0.05, 24), 44)
             let dynamicBottomPadding = min(max(proxy.size.height * 0.06, 28), 56)
 
             ScrollView(.vertical, showsIndicators: false) {
@@ -26,7 +25,7 @@ struct DictionaryView: View {
                 .frame(maxWidth: 1200, alignment: .topLeading)
                 .frame(maxWidth: .infinity, alignment: .topLeading)
                 .padding(.horizontal, dynamicHorizontalPadding)
-                .padding(.top, dynamicTopPadding)
+                .padding(.top, AppTheme.brandLabelTopInset)
                 .padding(.bottom, dynamicBottomPadding)
             }
         }

+ 1 - 2
gramora/Views/EmailWriterView.swift

@@ -14,7 +14,6 @@ struct EmailWriterView: View {
     var body: some View {
         GeometryReader { proxy in
             let dynamicHorizontalPadding = min(max(proxy.size.width * 0.045, AppTheme.contentPadding), 52)
-            let dynamicTopPadding = min(max(proxy.size.height * 0.015, 8), 16)
             let dynamicBottomPadding = min(max(proxy.size.height * 0.04, 20), 40)
 
             VStack(alignment: .leading, spacing: 0) {
@@ -41,7 +40,7 @@ struct EmailWriterView: View {
             .frame(maxWidth: 1200, maxHeight: .infinity, alignment: .topLeading)
             .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
             .padding(.horizontal, dynamicHorizontalPadding)
-            .padding(.top, dynamicTopPadding)
+            .padding(.top, AppTheme.brandLabelTopInset)
             .padding(.bottom, dynamicBottomPadding)
         }
         .onChange(of: viewModel.prompt) { newValue in

+ 2 - 3
gramora/Views/GrammarCheckerView.swift

@@ -16,7 +16,6 @@ struct GrammarCheckerView: View {
     var body: some View {
         GeometryReader { proxy in
             let dynamicHorizontalPadding = min(max(proxy.size.width * 0.045, AppTheme.contentPadding), 52)
-            let dynamicTopPadding = min(max(proxy.size.height * 0.015, 8), 16)
             let dynamicBottomPadding = min(max(proxy.size.height * 0.04, 20), 40)
 
             Group {
@@ -31,7 +30,7 @@ struct GrammarCheckerView: View {
                     .frame(maxWidth: 1200, maxHeight: .infinity, alignment: .topLeading)
                     .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
                     .padding(.horizontal, dynamicHorizontalPadding)
-                    .padding(.top, dynamicTopPadding)
+                    .padding(.top, AppTheme.brandLabelTopInset)
                     .padding(.bottom, dynamicBottomPadding)
                 } else {
                     VStack(alignment: .leading, spacing: 0) {
@@ -57,7 +56,7 @@ struct GrammarCheckerView: View {
                     .frame(maxWidth: 1200, maxHeight: .infinity, alignment: .topLeading)
                     .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
                     .padding(.horizontal, dynamicHorizontalPadding)
-                    .padding(.top, dynamicTopPadding)
+                    .padding(.top, AppTheme.brandLabelTopInset)
                     .padding(.bottom, dynamicBottomPadding)
                 }
             }

+ 1 - 2
gramora/Views/HistoryView.swift

@@ -7,7 +7,6 @@ struct HistoryView: View {
     var body: some View {
         GeometryReader { proxy in
             let dynamicHorizontalPadding = min(max(proxy.size.width * 0.045, AppTheme.contentPadding), 52)
-            let dynamicTopPadding = min(max(proxy.size.height * 0.015, 8), 16)
             let dynamicBottomPadding = min(max(proxy.size.height * 0.04, 20), 40)
 
             ScrollView(.vertical, showsIndicators: false) {
@@ -28,7 +27,7 @@ struct HistoryView: View {
                 .frame(maxWidth: 900, alignment: .topLeading)
                 .frame(maxWidth: .infinity, alignment: .topLeading)
                 .padding(.horizontal, dynamicHorizontalPadding)
-                .padding(.top, dynamicTopPadding)
+                .padding(.top, AppTheme.brandLabelTopInset)
                 .padding(.bottom, dynamicBottomPadding)
             }
         }

+ 2 - 3
gramora/Views/JournalFinderView.swift

@@ -16,7 +16,6 @@ struct JournalFinderView: View {
     var body: some View {
         GeometryReader { proxy in
             let dynamicHorizontalPadding = min(max(proxy.size.width * 0.045, AppTheme.contentPadding), 52)
-            let dynamicTopPadding = min(max(proxy.size.height * 0.015, 8), 16)
             let dynamicBottomPadding = min(max(proxy.size.height * 0.04, 20), 40)
 
             Group {
@@ -24,14 +23,14 @@ struct JournalFinderView: View {
                     resultsLayout(
                         result: result,
                         horizontalPadding: dynamicHorizontalPadding,
-                        topPadding: dynamicTopPadding,
+                        topPadding: AppTheme.brandLabelTopInset,
                         bottomPadding: dynamicBottomPadding
                     )
                 } else {
                     searchLayout(
                         abstractMinHeight: min(max(proxy.size.height * 0.28, 180), 320),
                         horizontalPadding: dynamicHorizontalPadding,
-                        topPadding: dynamicTopPadding,
+                        topPadding: AppTheme.brandLabelTopInset,
                         bottomPadding: dynamicBottomPadding
                     )
                 }

+ 1 - 2
gramora/Views/LanguageTranslatorView.swift

@@ -14,7 +14,6 @@ struct LanguageTranslatorView: View {
     var body: some View {
         GeometryReader { proxy in
             let dynamicHorizontalPadding = min(max(proxy.size.width * 0.045, AppTheme.contentPadding), 52)
-            let dynamicTopPadding = min(max(proxy.size.height * 0.015, 8), 16)
             let dynamicBottomPadding = min(max(proxy.size.height * 0.04, 20), 40)
 
             VStack(alignment: .leading, spacing: 0) {
@@ -27,7 +26,7 @@ struct LanguageTranslatorView: View {
             .frame(maxWidth: 1200, maxHeight: .infinity, alignment: .topLeading)
             .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
             .padding(.horizontal, dynamicHorizontalPadding)
-            .padding(.top, dynamicTopPadding)
+            .padding(.top, AppTheme.brandLabelTopInset)
             .padding(.bottom, dynamicBottomPadding)
         }
         .onChange(of: viewModel.sourceText) { newValue in

+ 1 - 0
gramora/Views/MainView.swift

@@ -32,6 +32,7 @@ struct MainView: View {
                             .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
                     }
                     .frame(maxWidth: .infinity, maxHeight: .infinity)
+                    .ignoresSafeArea(edges: .top)
                 }
                 .background {
                     AppTheme.background

+ 2 - 3
gramora/Views/ParaphrasingView.swift

@@ -6,7 +6,6 @@ struct ParaphrasingView: View {
     var body: some View {
         GeometryReader { proxy in
             let dynamicHorizontalPadding = min(max(proxy.size.width * 0.045, AppTheme.contentPadding), 52)
-            let dynamicTopPadding = min(max(proxy.size.height * 0.015, 8), 16)
             let dynamicBottomPadding = min(max(proxy.size.height * 0.04, 20), 40)
 
             Group {
@@ -34,7 +33,7 @@ struct ParaphrasingView: View {
                     .frame(maxWidth: 1200, maxHeight: .infinity, alignment: .topLeading)
                     .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
                     .padding(.horizontal, dynamicHorizontalPadding)
-                    .padding(.top, dynamicTopPadding)
+                    .padding(.top, AppTheme.brandLabelTopInset)
                     .padding(.bottom, dynamicBottomPadding)
                 } else {
                     ScrollView(.vertical, showsIndicators: false) {
@@ -63,7 +62,7 @@ struct ParaphrasingView: View {
                         .frame(maxWidth: 1200, alignment: .topLeading)
                         .frame(maxWidth: .infinity, alignment: .topLeading)
                         .padding(.horizontal, dynamicHorizontalPadding)
-                        .padding(.top, dynamicTopPadding)
+                        .padding(.top, AppTheme.brandLabelTopInset)
                         .padding(.bottom, dynamicBottomPadding)
                     }
                 }

+ 2 - 3
gramora/Views/PunctuationCheckerView.swift

@@ -6,7 +6,6 @@ struct PunctuationCheckerView: View {
     var body: some View {
         GeometryReader { proxy in
             let dynamicHorizontalPadding = min(max(proxy.size.width * 0.045, AppTheme.contentPadding), 52)
-            let dynamicTopPadding = min(max(proxy.size.height * 0.015, 8), 16)
             let dynamicBottomPadding = min(max(proxy.size.height * 0.04, 20), 40)
 
             Group {
@@ -34,7 +33,7 @@ struct PunctuationCheckerView: View {
                     .frame(maxWidth: 1200, maxHeight: .infinity, alignment: .topLeading)
                     .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
                     .padding(.horizontal, dynamicHorizontalPadding)
-                    .padding(.top, dynamicTopPadding)
+                    .padding(.top, AppTheme.brandLabelTopInset)
                     .padding(.bottom, dynamicBottomPadding)
                 } else {
                     ScrollView(.vertical, showsIndicators: false) {
@@ -63,7 +62,7 @@ struct PunctuationCheckerView: View {
                         .frame(maxWidth: 1200, alignment: .topLeading)
                         .frame(maxWidth: .infinity, alignment: .topLeading)
                         .padding(.horizontal, dynamicHorizontalPadding)
-                        .padding(.top, dynamicTopPadding)
+                        .padding(.top, AppTheme.brandLabelTopInset)
                         .padding(.bottom, dynamicBottomPadding)
                     }
                 }

+ 4 - 3
gramora/Views/SidebarView.swift

@@ -10,8 +10,8 @@ struct SidebarView: View {
         VStack(alignment: .leading, spacing: 0) {
             logoHeader
                 .padding(.horizontal, 20)
-                .padding(.top, 24)
-                .padding(.bottom, 20)
+                .padding(.top, AppTheme.sidebarLogoTopInset)
+                .padding(.bottom, AppTheme.sidebarLogoBottomSpacing)
 
             VStack(spacing: 2) {
                 ForEach(NavigationDestination.allCases) { destination in
@@ -37,6 +37,7 @@ struct SidebarView: View {
         }
         .frame(width: AppTheme.sidebarWidth, alignment: .top)
         .frame(maxHeight: .infinity)
+        .ignoresSafeArea(edges: .top)
         .background {
             AppTheme.sidebarBackground
                 .ignoresSafeArea(edges: .top)
@@ -48,7 +49,7 @@ struct SidebarView: View {
             ZStack {
                 Circle()
                     .fill(AppTheme.teal)
-                    .frame(width: 36, height: 36)
+                    .frame(width: AppTheme.logoMarkSize, height: AppTheme.logoMarkSize)
 
                 Text("G")
                     .font(.system(size: 18, weight: .bold))

+ 2 - 3
gramora/Views/SpellCheckerView.swift

@@ -16,7 +16,6 @@ struct SpellCheckerView: View {
     var body: some View {
         GeometryReader { proxy in
             let dynamicHorizontalPadding = min(max(proxy.size.width * 0.045, AppTheme.contentPadding), 52)
-            let dynamicTopPadding = min(max(proxy.size.height * 0.015, 8), 16)
             let dynamicBottomPadding = min(max(proxy.size.height * 0.04, 20), 40)
 
             Group {
@@ -31,7 +30,7 @@ struct SpellCheckerView: View {
                     .frame(maxWidth: 1200, maxHeight: .infinity, alignment: .topLeading)
                     .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
                     .padding(.horizontal, dynamicHorizontalPadding)
-                    .padding(.top, dynamicTopPadding)
+                    .padding(.top, AppTheme.brandLabelTopInset)
                     .padding(.bottom, dynamicBottomPadding)
                 } else {
                     VStack(alignment: .leading, spacing: 0) {
@@ -57,7 +56,7 @@ struct SpellCheckerView: View {
                     .frame(maxWidth: 1200, maxHeight: .infinity, alignment: .topLeading)
                     .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
                     .padding(.horizontal, dynamicHorizontalPadding)
-                    .padding(.top, dynamicTopPadding)
+                    .padding(.top, AppTheme.brandLabelTopInset)
                     .padding(.bottom, dynamicBottomPadding)
                 }
             }

+ 2 - 3
gramora/Views/SummarizerView.swift

@@ -6,7 +6,6 @@ struct SummarizerView: View {
     var body: some View {
         GeometryReader { proxy in
             let dynamicHorizontalPadding = min(max(proxy.size.width * 0.045, AppTheme.contentPadding), 52)
-            let dynamicTopPadding = min(max(proxy.size.height * 0.015, 8), 16)
             let dynamicBottomPadding = min(max(proxy.size.height * 0.04, 20), 40)
 
             Group {
@@ -34,7 +33,7 @@ struct SummarizerView: View {
                     .frame(maxWidth: 1200, maxHeight: .infinity, alignment: .topLeading)
                     .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
                     .padding(.horizontal, dynamicHorizontalPadding)
-                    .padding(.top, dynamicTopPadding)
+                    .padding(.top, AppTheme.brandLabelTopInset)
                     .padding(.bottom, dynamicBottomPadding)
                 } else {
                     ScrollView(.vertical, showsIndicators: false) {
@@ -63,7 +62,7 @@ struct SummarizerView: View {
                         .frame(maxWidth: 1200, alignment: .topLeading)
                         .frame(maxWidth: .infinity, alignment: .topLeading)
                         .padding(.horizontal, dynamicHorizontalPadding)
-                        .padding(.top, dynamicTopPadding)
+                        .padding(.top, AppTheme.brandLabelTopInset)
                         .padding(.bottom, dynamicBottomPadding)
                     }
                 }

+ 1 - 2
gramora/Views/WordsCountView.swift

@@ -6,7 +6,6 @@ struct WordsCountView: View {
     var body: some View {
         GeometryReader { proxy in
             let dynamicHorizontalPadding = min(max(proxy.size.width * 0.045, AppTheme.contentPadding), 52)
-            let dynamicTopPadding = min(max(proxy.size.height * 0.015, 8), 16)
             let dynamicBottomPadding = min(max(proxy.size.height * 0.04, 20), 40)
 
             VStack(alignment: .leading, spacing: 0) {
@@ -19,7 +18,7 @@ struct WordsCountView: View {
             .frame(maxWidth: 1200, maxHeight: .infinity, alignment: .topLeading)
             .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
             .padding(.horizontal, dynamicHorizontalPadding)
-            .padding(.top, dynamicTopPadding)
+            .padding(.top, AppTheme.brandLabelTopInset)
             .padding(.bottom, dynamicBottomPadding)
         }
         .onChange(of: viewModel.text) { newValue in