Selaa lähdekoodia

Tighten tool page top spacing with a shared header layout.

Remove double title-bar inset stacking, use ToolPageHeader for consistent compact headers, and add a small breathing-room offset across all tool pages.

Co-authored-by: Cursor <cursoragent@cursor.com>
AhtashamShahzad1 1 kuukausi sitten
vanhempi
sitoutus
0cf3edba06

+ 3 - 1
gramora/Utilities/AppTheme.swift

@@ -28,10 +28,12 @@ enum AppTheme {
     }
 
     static let sidebarLogoBottomSpacing: CGFloat = 20
+    /// Small extra inset below the title bar on tool pages.
+    static let contentTopBreathingRoom: CGFloat = 8
 
     /// Aligns tool page titles with the sidebar "Gramora" label.
     static var brandLabelTopInset: CGFloat {
-        headerTopInset + (logoMarkSize - 22) / 2
+        headerTopInset + (logoMarkSize - 22) / 2 + contentTopBreathingRoom
     }
     static let cardCornerRadius: CGFloat = 16
     static let buttonCornerRadius: CGFloat = 14

+ 32 - 0
gramora/Views/Components/ToolPageHeader.swift

@@ -0,0 +1,32 @@
+import SwiftUI
+
+struct ToolPageHeader<Tagline: View, Trailing: View>: View {
+    let title: String
+    var titleFontSize: CGFloat = 30
+    var showsBackButton: Bool
+    var onBack: () -> Void
+    @ViewBuilder var tagline: () -> Tagline
+    @ViewBuilder var trailing: () -> Trailing
+
+    var body: some View {
+        HStack(alignment: .top, spacing: 8) {
+            if showsBackButton {
+                IconButton(iconName: "arrow.left", action: onBack)
+                    .accessibilityLabel("Go back")
+                    .padding(.top, 8)
+            }
+
+            VStack(alignment: .leading, spacing: 8) {
+                Text(title)
+                    .font(.system(size: titleFontSize, weight: .bold))
+                    .foregroundStyle(AppTheme.textPrimary)
+
+                tagline()
+            }
+
+            Spacer(minLength: 0)
+
+            trailing()
+        }
+    }
+}

+ 27 - 30
gramora/Views/DictionaryView.swift

@@ -50,38 +50,35 @@ struct DictionaryView: View {
     }
 
     private var header: some View {
-        HStack(alignment: .top) {
-            VStack(alignment: .leading, spacing: 8) {
-                Text("Dictionary")
-                    .font(.system(size: 30, weight: .bold))
-                    .foregroundStyle(AppTheme.textPrimary)
-
-                HStack(spacing: 0) {
-                    Text("Find ")
-                        .foregroundStyle(AppTheme.textSecondary)
-                    Text("meaning")
-                        .fontWeight(.semibold)
-                        .foregroundStyle(AppTheme.teal)
-                    Text(". Learn ")
-                        .foregroundStyle(AppTheme.textSecondary)
-                    Text("words")
-                        .fontWeight(.semibold)
-                        .foregroundStyle(AppTheme.teal)
-                    Text(". Expand your ")
-                        .foregroundStyle(AppTheme.textSecondary)
-                    Text("vocabulary")
-                        .fontWeight(.semibold)
-                        .foregroundStyle(AppTheme.teal)
-                    Text(".")
-                        .foregroundStyle(AppTheme.textSecondary)
-                }
-                .font(.system(size: 15))
+        ToolPageHeader(
+            title: "Dictionary",
+            showsBackButton: false,
+            onBack: {}
+        ) {
+            HStack(spacing: 0) {
+                Text("Find ")
+                    .foregroundStyle(AppTheme.textSecondary)
+                Text("meaning")
+                    .fontWeight(.semibold)
+                    .foregroundStyle(AppTheme.teal)
+                Text(". Learn ")
+                    .foregroundStyle(AppTheme.textSecondary)
+                Text("words")
+                    .fontWeight(.semibold)
+                    .foregroundStyle(AppTheme.teal)
+                Text(". Expand your ")
+                    .foregroundStyle(AppTheme.textSecondary)
+                Text("vocabulary")
+                    .fontWeight(.semibold)
+                    .foregroundStyle(AppTheme.teal)
+                Text(".")
+                    .foregroundStyle(AppTheme.textSecondary)
             }
-
-            Spacer()
-
+            .font(.system(size: 15))
+        } trailing: {
             IconButton(icon: { SettingsGearIcon() }, action: onSettingsTapped)
-            .accessibilityLabel("Settings")
+                .accessibilityLabel("Settings")
+                .padding(.top, 8)
         }
     }
 

+ 23 - 25
gramora/Views/EmailWriterView.swift

@@ -67,33 +67,31 @@ struct EmailWriterView: View {
     }
 
     private var header: some View {
-        HStack(alignment: .top) {
-            VStack(alignment: .leading, spacing: 6) {
-                Text("AI Email Writer")
-                    .font(.system(size: 26, weight: .bold))
-                    .foregroundStyle(AppTheme.textPrimary)
-
-                HStack(spacing: 0) {
-                    Text("Write ")
-                        .foregroundStyle(AppTheme.textSecondary)
-                    Text("better emails")
-                        .fontWeight(.semibold)
-                        .foregroundStyle(AppTheme.teal)
-                    Text(". Save ")
-                        .foregroundStyle(AppTheme.textSecondary)
-                    Text("time")
-                        .fontWeight(.semibold)
-                        .foregroundStyle(AppTheme.teal)
-                    Text(".")
-                        .foregroundStyle(AppTheme.textSecondary)
-                }
-                .font(.system(size: 14))
+        ToolPageHeader(
+            title: "AI Email Writer",
+            titleFontSize: 26,
+            showsBackButton: false,
+            onBack: {}
+        ) {
+            HStack(spacing: 0) {
+                Text("Write ")
+                    .foregroundStyle(AppTheme.textSecondary)
+                Text("better emails")
+                    .fontWeight(.semibold)
+                    .foregroundStyle(AppTheme.teal)
+                Text(". Save ")
+                    .foregroundStyle(AppTheme.textSecondary)
+                Text("time")
+                    .fontWeight(.semibold)
+                    .foregroundStyle(AppTheme.teal)
+                Text(".")
+                    .foregroundStyle(AppTheme.textSecondary)
             }
-
-            Spacer()
-
+            .font(.system(size: 14))
+        } trailing: {
             IconButton(icon: { SettingsGearIcon() }, action: onSettingsTapped)
-            .accessibilityLabel("Settings")
+                .accessibilityLabel("Settings")
+                .padding(.top, 8)
         }
     }
 

+ 23 - 35
gramora/Views/GrammarCheckerView.swift

@@ -28,7 +28,7 @@ struct GrammarCheckerView: View {
                 } else if viewModel.hasResults {
                     VStack(alignment: .leading, spacing: 0) {
                         header
-                            .padding(.bottom, 20)
+                            .padding(.bottom, 12)
 
                         resultsLayout
                             .frame(maxWidth: .infinity, maxHeight: .infinity)
@@ -109,42 +109,30 @@ struct GrammarCheckerView: View {
     }
 
     private var header: some View {
-        VStack(alignment: .leading, spacing: 16) {
-            if viewModel.hasResults {
-                IconButton(iconName: "arrow.left") {
-                    viewModel.resetResults()
-                }
-                .accessibilityLabel("Go back")
+        ToolPageHeader(
+            title: "Grammar Checker",
+            showsBackButton: viewModel.hasResults,
+            onBack: { viewModel.resetResults() }
+        ) {
+            HStack(spacing: 0) {
+                Text("Write ")
+                    .foregroundStyle(AppTheme.textSecondary)
+                Text("better")
+                    .fontWeight(.semibold)
+                    .foregroundStyle(AppTheme.teal)
+                Text(". Sound ")
+                    .foregroundStyle(AppTheme.textSecondary)
+                Text("smarter")
+                    .fontWeight(.semibold)
+                    .foregroundStyle(AppTheme.teal)
+                Text(".")
+                    .foregroundStyle(AppTheme.textSecondary)
             }
-
-            HStack(alignment: .top) {
-                VStack(alignment: .leading, spacing: 8) {
-                    Text("Grammar Checker")
-                        .font(.system(size: 30, weight: .bold))
-                        .foregroundStyle(AppTheme.textPrimary)
-
-                    HStack(spacing: 0) {
-                        Text("Write ")
-                            .foregroundStyle(AppTheme.textSecondary)
-                        Text("better")
-                            .fontWeight(.semibold)
-                            .foregroundStyle(AppTheme.teal)
-                        Text(". Sound ")
-                            .foregroundStyle(AppTheme.textSecondary)
-                        Text("smarter")
-                            .fontWeight(.semibold)
-                            .foregroundStyle(AppTheme.teal)
-                        Text(".")
-                            .foregroundStyle(AppTheme.textSecondary)
-                    }
-                    .font(.system(size: 15))
-                }
-
-                Spacer()
-
-                IconButton(icon: { SettingsGearIcon() }, action: onSettingsTapped)
+            .font(.system(size: 15))
+        } trailing: {
+            IconButton(icon: { SettingsGearIcon() }, action: onSettingsTapped)
                 .accessibilityLabel("Settings")
-            }
+                .padding(.top, 8)
         }
     }
 

+ 10 - 13
gramora/Views/HistoryView.swift

@@ -65,24 +65,21 @@ struct HistoryView: View {
     }
 
     private var header: some View {
-        HStack(alignment: .top) {
-            VStack(alignment: .leading, spacing: 8) {
-                Text("History")
-                    .font(.system(size: 30, weight: .bold))
-                    .foregroundStyle(AppTheme.textPrimary)
-
-                Text("Review, reopen, or remove your past tool results.")
-                    .font(.system(size: 15))
-                    .foregroundStyle(AppTheme.teal)
-            }
-
-            Spacer()
-
+        ToolPageHeader(
+            title: "History",
+            showsBackButton: false,
+            onBack: {}
+        ) {
+            Text("Review, reopen, or remove your past tool results.")
+                .font(.system(size: 15))
+                .foregroundStyle(AppTheme.teal)
+        } trailing: {
             if !viewModel.isEmpty {
                 ClearButton {
                     viewModel.isShowingClearConfirmation = true
                 }
                 .accessibilityLabel("Clear all history")
+                .padding(.top, 8)
             }
         }
     }

+ 35 - 48
gramora/Views/JournalFinderView.swift

@@ -147,7 +147,7 @@ struct JournalFinderView: View {
     ) -> some View {
         VStack(alignment: .leading, spacing: 0) {
             resultsHeader(result: result)
-                .padding(.bottom, 24)
+                .padding(.bottom, 12)
 
             ScrollView(.vertical, showsIndicators: false) {
                 LazyVStack(spacing: 16) {
@@ -169,59 +169,46 @@ struct JournalFinderView: View {
     // MARK: - Header
 
     private var header: some View {
-        HStack(alignment: .top) {
-            VStack(alignment: .leading, spacing: 8) {
-                Text("Journal Finder")
-                    .font(.system(size: 30, weight: .bold))
-                    .foregroundStyle(AppTheme.textPrimary)
-
-                HStack(spacing: 0) {
-                    Text("Find the ")
-                        .foregroundStyle(AppTheme.textSecondary)
-                    Text("right journal")
-                        .fontWeight(.semibold)
-                        .foregroundStyle(AppTheme.teal)
-                    Text(" for your ")
-                        .foregroundStyle(AppTheme.textSecondary)
-                    Text("research")
-                        .fontWeight(.semibold)
-                        .foregroundStyle(AppTheme.teal)
-                    Text(".")
-                        .foregroundStyle(AppTheme.textSecondary)
-                }
-                .font(.system(size: 15))
+        ToolPageHeader(
+            title: "Journal Finder",
+            showsBackButton: false,
+            onBack: {}
+        ) {
+            HStack(spacing: 0) {
+                Text("Find the ")
+                    .foregroundStyle(AppTheme.textSecondary)
+                Text("right journal")
+                    .fontWeight(.semibold)
+                    .foregroundStyle(AppTheme.teal)
+                Text(" for your ")
+                    .foregroundStyle(AppTheme.textSecondary)
+                Text("research")
+                    .fontWeight(.semibold)
+                    .foregroundStyle(AppTheme.teal)
+                Text(".")
+                    .foregroundStyle(AppTheme.textSecondary)
             }
-
-            Spacer()
-
+            .font(.system(size: 15))
+        } trailing: {
             IconButton(icon: { SettingsGearIcon() }, action: onSettingsTapped)
-            .accessibilityLabel("Settings")
+                .accessibilityLabel("Settings")
+                .padding(.top, 8)
         }
     }
 
     private func resultsHeader(result: JournalFinderResult) -> some View {
-        VStack(alignment: .leading, spacing: 16) {
-            IconButton(iconName: "arrow.left") {
-                viewModel.resetResults()
-            }
-            .accessibilityLabel("Go back")
-
-            HStack(alignment: .top) {
-                VStack(alignment: .leading, spacing: 8) {
-                    Text("Recommended Journals")
-                        .font(.system(size: 30, weight: .bold))
-                        .foregroundStyle(AppTheme.textPrimary)
-
-                    Text(result.summary)
-                        .font(.system(size: 15))
-                        .foregroundStyle(AppTheme.textSecondary)
-                        .fixedSize(horizontal: false, vertical: true)
-                }
-
-                Spacer()
-
-                resultCountBadge(count: result.recommendations.count)
-            }
+        ToolPageHeader(
+            title: "Recommended Journals",
+            showsBackButton: true,
+            onBack: { viewModel.resetResults() }
+        ) {
+            Text(result.summary)
+                .font(.system(size: 15))
+                .foregroundStyle(AppTheme.textSecondary)
+                .fixedSize(horizontal: false, vertical: true)
+        } trailing: {
+            resultCountBadge(count: result.recommendations.count)
+                .padding(.top, 8)
         }
     }
 

+ 22 - 25
gramora/Views/LanguageTranslatorView.swift

@@ -56,33 +56,30 @@ struct LanguageTranslatorView: View {
     }
 
     private var header: some View {
-        HStack(alignment: .top) {
-            VStack(alignment: .leading, spacing: 8) {
-                Text("Language Translator")
-                    .font(.system(size: 30, weight: .bold))
-                    .foregroundStyle(AppTheme.textPrimary)
-
-                HStack(spacing: 0) {
-                    Text("Speak ")
-                        .foregroundStyle(AppTheme.textSecondary)
-                    Text("any language")
-                        .fontWeight(.semibold)
-                        .foregroundStyle(AppTheme.teal)
-                    Text(". Reach ")
-                        .foregroundStyle(AppTheme.textSecondary)
-                    Text("everyone")
-                        .fontWeight(.semibold)
-                        .foregroundStyle(AppTheme.teal)
-                    Text(".")
-                        .foregroundStyle(AppTheme.textSecondary)
-                }
-                .font(.system(size: 15))
+        ToolPageHeader(
+            title: "Language Translator",
+            showsBackButton: false,
+            onBack: {}
+        ) {
+            HStack(spacing: 0) {
+                Text("Speak ")
+                    .foregroundStyle(AppTheme.textSecondary)
+                Text("any language")
+                    .fontWeight(.semibold)
+                    .foregroundStyle(AppTheme.teal)
+                Text(". Reach ")
+                    .foregroundStyle(AppTheme.textSecondary)
+                Text("everyone")
+                    .fontWeight(.semibold)
+                    .foregroundStyle(AppTheme.teal)
+                Text(".")
+                    .foregroundStyle(AppTheme.textSecondary)
             }
-
-            Spacer()
-
+            .font(.system(size: 15))
+        } trailing: {
             IconButton(icon: { SettingsGearIcon() }, action: onSettingsTapped)
-            .accessibilityLabel("Settings")
+                .accessibilityLabel("Settings")
+                .padding(.top, 8)
         }
     }
 

+ 1 - 0
gramora/Views/MainView.swift

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

+ 23 - 35
gramora/Views/ParaphrasingView.swift

@@ -28,7 +28,7 @@ struct ParaphrasingView: View {
                 } else if viewModel.hasResults {
                     VStack(alignment: .leading, spacing: 0) {
                         header
-                            .padding(.bottom, 20)
+                            .padding(.bottom, 12)
 
                         resultsLayout
                             .frame(maxWidth: .infinity, maxHeight: .infinity)
@@ -109,42 +109,30 @@ struct ParaphrasingView: View {
     }
 
     private var header: some View {
-        VStack(alignment: .leading, spacing: 16) {
-            if viewModel.hasResults {
-                IconButton(iconName: "arrow.left") {
-                    viewModel.resetResults()
-                }
-                .accessibilityLabel("Go back")
+        ToolPageHeader(
+            title: "Paraphrasing",
+            showsBackButton: viewModel.hasResults,
+            onBack: { viewModel.resetResults() }
+        ) {
+            HStack(spacing: 0) {
+                Text("Say it ")
+                    .foregroundStyle(AppTheme.textSecondary)
+                Text("differently")
+                    .fontWeight(.semibold)
+                    .foregroundStyle(AppTheme.teal)
+                Text(". Keep it ")
+                    .foregroundStyle(AppTheme.textSecondary)
+                Text("yours")
+                    .fontWeight(.semibold)
+                    .foregroundStyle(AppTheme.teal)
+                Text(".")
+                    .foregroundStyle(AppTheme.textSecondary)
             }
-
-            HStack(alignment: .top) {
-                VStack(alignment: .leading, spacing: 8) {
-                    Text("Paraphrasing")
-                        .font(.system(size: 30, weight: .bold))
-                        .foregroundStyle(AppTheme.textPrimary)
-
-                    HStack(spacing: 0) {
-                        Text("Say it ")
-                            .foregroundStyle(AppTheme.textSecondary)
-                        Text("differently")
-                            .fontWeight(.semibold)
-                            .foregroundStyle(AppTheme.teal)
-                        Text(". Keep it ")
-                            .foregroundStyle(AppTheme.textSecondary)
-                        Text("yours")
-                            .fontWeight(.semibold)
-                            .foregroundStyle(AppTheme.teal)
-                        Text(".")
-                            .foregroundStyle(AppTheme.textSecondary)
-                    }
-                    .font(.system(size: 15))
-                }
-
-                Spacer()
-
-                IconButton(icon: { SettingsGearIcon() }, action: onSettingsTapped)
+            .font(.system(size: 15))
+        } trailing: {
+            IconButton(icon: { SettingsGearIcon() }, action: onSettingsTapped)
                 .accessibilityLabel("Settings")
-            }
+                .padding(.top, 8)
         }
     }
 

+ 23 - 35
gramora/Views/PunctuationCheckerView.swift

@@ -28,7 +28,7 @@ struct PunctuationCheckerView: View {
                 } else if viewModel.hasResults {
                     VStack(alignment: .leading, spacing: 0) {
                         header
-                            .padding(.bottom, 20)
+                            .padding(.bottom, 12)
 
                         resultsLayout
                             .frame(maxWidth: .infinity, maxHeight: .infinity)
@@ -109,42 +109,30 @@ struct PunctuationCheckerView: View {
     }
 
     private var header: some View {
-        VStack(alignment: .leading, spacing: 16) {
-            if viewModel.hasResults {
-                IconButton(iconName: "arrow.left") {
-                    viewModel.resetResults()
-                }
-                .accessibilityLabel("Go back")
+        ToolPageHeader(
+            title: "Punctuation Checker",
+            showsBackButton: viewModel.hasResults,
+            onBack: { viewModel.resetResults() }
+        ) {
+            HStack(spacing: 0) {
+                Text("Perfect your ")
+                    .foregroundStyle(AppTheme.textSecondary)
+                Text("punctuation")
+                    .fontWeight(.semibold)
+                    .foregroundStyle(AppTheme.teal)
+                Text(". Write with ")
+                    .foregroundStyle(AppTheme.textSecondary)
+                Text("clarity")
+                    .fontWeight(.semibold)
+                    .foregroundStyle(AppTheme.teal)
+                Text(".")
+                    .foregroundStyle(AppTheme.textSecondary)
             }
-
-            HStack(alignment: .top) {
-                VStack(alignment: .leading, spacing: 8) {
-                    Text("Punctuation Checker")
-                        .font(.system(size: 30, weight: .bold))
-                        .foregroundStyle(AppTheme.textPrimary)
-
-                    HStack(spacing: 0) {
-                        Text("Perfect your ")
-                            .foregroundStyle(AppTheme.textSecondary)
-                        Text("punctuation")
-                            .fontWeight(.semibold)
-                            .foregroundStyle(AppTheme.teal)
-                        Text(". Write with ")
-                            .foregroundStyle(AppTheme.textSecondary)
-                        Text("clarity")
-                            .fontWeight(.semibold)
-                            .foregroundStyle(AppTheme.teal)
-                        Text(".")
-                            .foregroundStyle(AppTheme.textSecondary)
-                    }
-                    .font(.system(size: 15))
-                }
-
-                Spacer()
-
-                IconButton(icon: { SettingsGearIcon() }, action: onSettingsTapped)
+            .font(.system(size: 15))
+        } trailing: {
+            IconButton(icon: { SettingsGearIcon() }, action: onSettingsTapped)
                 .accessibilityLabel("Settings")
-            }
+                .padding(.top, 8)
         }
     }
 

+ 10 - 13
gramora/Views/SettingsView.swift

@@ -56,19 +56,16 @@ struct SettingsView: View {
     }
 
     private var header: some View {
-        VStack(alignment: .leading, spacing: 16) {
-            IconButton(iconName: "arrow.left", action: onClose)
-                .accessibilityLabel("Go back")
-
-            VStack(alignment: .leading, spacing: 8) {
-                Text("Settings")
-                    .font(.system(size: 30, weight: .bold))
-                    .foregroundStyle(AppTheme.textPrimary)
-
-                Text("Manage your account, data, and app preferences.")
-                    .font(.system(size: 15))
-                    .foregroundStyle(AppTheme.teal)
-            }
+        ToolPageHeader(
+            title: "Settings",
+            showsBackButton: true,
+            onBack: onClose
+        ) {
+            Text("Manage your account, data, and app preferences.")
+                .font(.system(size: 15))
+                .foregroundStyle(AppTheme.teal)
+        } trailing: {
+            EmptyView()
         }
     }
 

+ 23 - 35
gramora/Views/SpellCheckerView.swift

@@ -28,7 +28,7 @@ struct SpellCheckerView: View {
                 } else if viewModel.hasResults {
                     VStack(alignment: .leading, spacing: 0) {
                         header
-                            .padding(.bottom, 20)
+                            .padding(.bottom, 12)
 
                         resultsLayout
                             .frame(maxWidth: .infinity, maxHeight: .infinity)
@@ -109,42 +109,30 @@ struct SpellCheckerView: View {
     }
 
     private var header: some View {
-        VStack(alignment: .leading, spacing: 16) {
-            if viewModel.hasResults {
-                IconButton(iconName: "arrow.left") {
-                    viewModel.resetResults()
-                }
-                .accessibilityLabel("Go back")
+        ToolPageHeader(
+            title: "Spelling Checker",
+            showsBackButton: viewModel.hasResults,
+            onBack: { viewModel.resetResults() }
+        ) {
+            HStack(spacing: 0) {
+                Text("Catch ")
+                    .foregroundStyle(AppTheme.textSecondary)
+                Text("misspellings")
+                    .fontWeight(.semibold)
+                    .foregroundStyle(AppTheme.teal)
+                Text(". Write ")
+                    .foregroundStyle(AppTheme.textSecondary)
+                Text("clearly")
+                    .fontWeight(.semibold)
+                    .foregroundStyle(AppTheme.teal)
+                Text(".")
+                    .foregroundStyle(AppTheme.textSecondary)
             }
-
-            HStack(alignment: .top) {
-                VStack(alignment: .leading, spacing: 8) {
-                    Text("Spelling Checker")
-                        .font(.system(size: 30, weight: .bold))
-                        .foregroundStyle(AppTheme.textPrimary)
-
-                    HStack(spacing: 0) {
-                        Text("Catch ")
-                            .foregroundStyle(AppTheme.textSecondary)
-                        Text("misspellings")
-                            .fontWeight(.semibold)
-                            .foregroundStyle(AppTheme.teal)
-                        Text(". Write ")
-                            .foregroundStyle(AppTheme.textSecondary)
-                        Text("clearly")
-                            .fontWeight(.semibold)
-                            .foregroundStyle(AppTheme.teal)
-                        Text(".")
-                            .foregroundStyle(AppTheme.textSecondary)
-                    }
-                    .font(.system(size: 15))
-                }
-
-                Spacer()
-
-                IconButton(icon: { SettingsGearIcon() }, action: onSettingsTapped)
+            .font(.system(size: 15))
+        } trailing: {
+            IconButton(icon: { SettingsGearIcon() }, action: onSettingsTapped)
                 .accessibilityLabel("Settings")
-            }
+                .padding(.top, 8)
         }
     }
 

+ 23 - 35
gramora/Views/SummarizerView.swift

@@ -28,7 +28,7 @@ struct SummarizerView: View {
                 } else if viewModel.hasResults {
                     VStack(alignment: .leading, spacing: 0) {
                         header
-                            .padding(.bottom, 20)
+                            .padding(.bottom, 12)
 
                         resultsLayout
                             .frame(maxWidth: .infinity, maxHeight: .infinity)
@@ -109,42 +109,30 @@ struct SummarizerView: View {
     }
 
     private var header: some View {
-        VStack(alignment: .leading, spacing: 16) {
-            if viewModel.hasResults {
-                IconButton(iconName: "arrow.left") {
-                    viewModel.resetResults()
-                }
-                .accessibilityLabel("Go back")
+        ToolPageHeader(
+            title: "Summarizer",
+            showsBackButton: viewModel.hasResults,
+            onBack: { viewModel.resetResults() }
+        ) {
+            HStack(spacing: 0) {
+                Text("Read ")
+                    .foregroundStyle(AppTheme.textSecondary)
+                Text("less")
+                    .fontWeight(.semibold)
+                    .foregroundStyle(AppTheme.teal)
+                Text(". Understand ")
+                    .foregroundStyle(AppTheme.textSecondary)
+                Text("more")
+                    .fontWeight(.semibold)
+                    .foregroundStyle(AppTheme.teal)
+                Text(".")
+                    .foregroundStyle(AppTheme.textSecondary)
             }
-
-            HStack(alignment: .top) {
-                VStack(alignment: .leading, spacing: 8) {
-                    Text("Summarizer")
-                        .font(.system(size: 30, weight: .bold))
-                        .foregroundStyle(AppTheme.textPrimary)
-
-                    HStack(spacing: 0) {
-                        Text("Read ")
-                            .foregroundStyle(AppTheme.textSecondary)
-                        Text("less")
-                            .fontWeight(.semibold)
-                            .foregroundStyle(AppTheme.teal)
-                        Text(". Understand ")
-                            .foregroundStyle(AppTheme.textSecondary)
-                        Text("more")
-                            .fontWeight(.semibold)
-                            .foregroundStyle(AppTheme.teal)
-                        Text(".")
-                            .foregroundStyle(AppTheme.textSecondary)
-                    }
-                    .font(.system(size: 15))
-                }
-
-                Spacer()
-
-                IconButton(icon: { SettingsGearIcon() }, action: onSettingsTapped)
+            .font(.system(size: 15))
+        } trailing: {
+            IconButton(icon: { SettingsGearIcon() }, action: onSettingsTapped)
                 .accessibilityLabel("Settings")
-            }
+                .padding(.top, 8)
         }
     }
 

+ 22 - 25
gramora/Views/WordsCountView.swift

@@ -54,33 +54,30 @@ struct WordsCountView: View {
     }
 
     private var header: some View {
-        HStack(alignment: .top) {
-            VStack(alignment: .leading, spacing: 8) {
-                Text("Words Count")
-                    .font(.system(size: 30, weight: .bold))
-                    .foregroundStyle(AppTheme.textPrimary)
-
-                HStack(spacing: 0) {
-                    Text("Count ")
-                        .foregroundStyle(AppTheme.textSecondary)
-                    Text("every word")
-                        .fontWeight(.semibold)
-                        .foregroundStyle(AppTheme.teal)
-                    Text(". Write with ")
-                        .foregroundStyle(AppTheme.textSecondary)
-                    Text("precision")
-                        .fontWeight(.semibold)
-                        .foregroundStyle(AppTheme.teal)
-                    Text(".")
-                        .foregroundStyle(AppTheme.textSecondary)
-                }
-                .font(.system(size: 15))
+        ToolPageHeader(
+            title: "Words Count",
+            showsBackButton: false,
+            onBack: {}
+        ) {
+            HStack(spacing: 0) {
+                Text("Count ")
+                    .foregroundStyle(AppTheme.textSecondary)
+                Text("every word")
+                    .fontWeight(.semibold)
+                    .foregroundStyle(AppTheme.teal)
+                Text(". Write with ")
+                    .foregroundStyle(AppTheme.textSecondary)
+                Text("precision")
+                    .fontWeight(.semibold)
+                    .foregroundStyle(AppTheme.teal)
+                Text(".")
+                    .foregroundStyle(AppTheme.textSecondary)
             }
-
-            Spacer()
-
+            .font(.system(size: 15))
+        } trailing: {
             IconButton(icon: { SettingsGearIcon() }, action: onSettingsTapped)
-            .accessibilityLabel("Settings")
+                .accessibilityLabel("Settings")
+                .padding(.top, 8)
         }
     }