Bladeren bron

Unify tool page layouts with full-height editors and bottom action buttons.

Align Grammar Checker, Summarizer, Paraphrasing, and related tools on consistent padding and field placement, and restore Journal Finder scrolling so filters stay reachable.

Co-authored-by: Cursor <cursoragent@cursor.com>
AhtashamShahzad1 1 maand geleden
bovenliggende
commit
fcc81b6360

+ 6 - 6
gramora/Views/EmailWriterView.swift

@@ -14,22 +14,21 @@ 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.03, 16), 28)
+            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) {
                 header
-                    .padding(.bottom, 16)
+                    .padding(.bottom, 24)
 
                 contentCard
-                    .frame(maxHeight: .infinity, alignment: .topLeading)
-                    .padding(.bottom, 24)
+                    .frame(maxWidth: .infinity, maxHeight: .infinity)
 
                 if let errorMessage = viewModel.errorMessage {
                     Text(errorMessage)
                         .font(.system(size: 14))
                         .foregroundStyle(.red)
-                        .padding(.bottom, 12)
+                        .padding(.top, 12)
                 }
 
                 GradientButton(title: viewModel.isGenerating ? "Generating..." : "Generate Email") {
@@ -37,6 +36,7 @@ struct EmailWriterView: View {
                 }
                 .disabled(!viewModel.canGenerate)
                 .opacity(viewModel.canGenerate ? 1 : 0.55)
+                .padding(.top, 16)
             }
             .frame(maxWidth: 1200, maxHeight: .infinity, alignment: .topLeading)
             .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
@@ -103,7 +103,7 @@ struct EmailWriterView: View {
             outputPanel
                 .frame(maxHeight: .infinity)
         }
-        .frame(maxHeight: .infinity, alignment: .top)
+        .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
         .padding(18)
         .background(AppTheme.cardBackground)
         .clipShape(RoundedRectangle(cornerRadius: AppTheme.cardCornerRadius))

+ 26 - 26
gramora/Views/GrammarCheckerView.swift

@@ -18,7 +18,6 @@ struct GrammarCheckerView: View {
             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)
-            let dynamicEditorHeight = min(max(proxy.size.height * 0.43, 260), 560)
 
             Group {
                 if viewModel.hasResults {
@@ -35,32 +34,31 @@ struct GrammarCheckerView: View {
                     .padding(.top, dynamicTopPadding)
                     .padding(.bottom, dynamicBottomPadding)
                 } else {
-                    ScrollView(.vertical, showsIndicators: false) {
-                        VStack(alignment: .leading, spacing: 0) {
-                            header
-                                .padding(.bottom, 24)
-
-                            contentCard(editorHeight: dynamicEditorHeight)
-                                .padding(.bottom, 24)
-
-                            if let errorMessage = viewModel.errorMessage {
-                                Text(errorMessage)
-                                    .font(.system(size: 14))
-                                    .foregroundStyle(.red)
-                                    .padding(.bottom, 12)
-                            }
+                    VStack(alignment: .leading, spacing: 0) {
+                        header
+                            .padding(.bottom, 24)
 
-                            GradientButton(title: viewModel.isChecking ? "Checking..." : "Check Grammar") {
-                                viewModel.checkGrammar()
-                            }
-                            .disabled(!viewModel.canCheck)
+                        contentCard
+                            .frame(maxWidth: .infinity, maxHeight: .infinity)
+
+                        if let errorMessage = viewModel.errorMessage {
+                            Text(errorMessage)
+                                .font(.system(size: 14))
+                                .foregroundStyle(.red)
+                                .padding(.top, 12)
                         }
-                        .frame(maxWidth: 1200, alignment: .topLeading)
-                        .frame(maxWidth: .infinity, alignment: .topLeading)
-                        .padding(.horizontal, dynamicHorizontalPadding)
-                        .padding(.top, dynamicTopPadding)
-                        .padding(.bottom, dynamicBottomPadding)
+
+                        GradientButton(title: viewModel.isChecking ? "Checking..." : "Check Grammar") {
+                            viewModel.checkGrammar()
+                        }
+                        .disabled(!viewModel.canCheck)
+                        .padding(.top, 16)
                     }
+                    .frame(maxWidth: 1200, maxHeight: .infinity, alignment: .topLeading)
+                    .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
+                    .padding(.horizontal, dynamicHorizontalPadding)
+                    .padding(.top, dynamicTopPadding)
+                    .padding(.bottom, dynamicBottomPadding)
                 }
             }
         }
@@ -123,7 +121,7 @@ struct GrammarCheckerView: View {
         }
     }
 
-    private func contentCard(editorHeight: CGFloat) -> some View {
+    private var contentCard: some View {
         VStack(alignment: .leading, spacing: 16) {
             Image("YourContentLogo")
                 .resizable()
@@ -140,7 +138,7 @@ struct GrammarCheckerView: View {
                 )
                     .font(.system(size: 14))
                     .foregroundStyle(AppTheme.textPrimary)
-                    .frame(minHeight: editorHeight, idealHeight: editorHeight)
+                    .frame(maxWidth: .infinity, maxHeight: .infinity)
                     .background(Color.white)
                     .clipShape(RoundedRectangle(cornerRadius: AppTheme.inputCornerRadius))
                     .overlay(
@@ -153,6 +151,7 @@ struct GrammarCheckerView: View {
                     .foregroundStyle(AppTheme.textMuted)
                     .frame(maxWidth: .infinity, alignment: .trailing)
             }
+            .frame(maxWidth: .infinity, maxHeight: .infinity)
 
             HStack(spacing: 8) {
                 ToolbarButton(title: "Paste Text", iconName: "doc.on.clipboard") {
@@ -169,6 +168,7 @@ struct GrammarCheckerView: View {
             }
         }
         .padding(22)
+        .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
         .background(AppTheme.cardBackground)
         .clipShape(RoundedRectangle(cornerRadius: AppTheme.cardCornerRadius))
         .overlay(

+ 33 - 30
gramora/Views/JournalFinderView.swift

@@ -18,7 +18,6 @@ struct JournalFinderView: View {
             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)
-            let dynamicEditorHeight = min(max(proxy.size.height * 0.28, 180), 320)
 
             Group {
                 if viewModel.hasResults, let result = viewModel.result {
@@ -30,7 +29,7 @@ struct JournalFinderView: View {
                     )
                 } else {
                     searchLayout(
-                        editorHeight: dynamicEditorHeight,
+                        abstractMinHeight: min(max(proxy.size.height * 0.28, 180), 320),
                         horizontalPadding: dynamicHorizontalPadding,
                         topPadding: dynamicTopPadding,
                         bottomPadding: dynamicBottomPadding
@@ -68,40 +67,43 @@ struct JournalFinderView: View {
     // MARK: - Search Layout
 
     private func searchLayout(
-        editorHeight: CGFloat,
+        abstractMinHeight: CGFloat,
         horizontalPadding: CGFloat,
         topPadding: CGFloat,
         bottomPadding: CGFloat
     ) -> some View {
-        ScrollView(.vertical, showsIndicators: false) {
-            VStack(alignment: .leading, spacing: 0) {
-                header
-                    .padding(.bottom, 24)
+        VStack(alignment: .leading, spacing: 0) {
+            header
+                .padding(.bottom, 24)
 
-                inputCard(editorHeight: editorHeight)
-                    .padding(.bottom, 20)
+            ScrollView(.vertical, showsIndicators: false) {
+                VStack(alignment: .leading, spacing: 0) {
+                    inputCard(abstractMinHeight: abstractMinHeight)
+                        .padding(.bottom, 16)
 
-                filtersCard
-                    .padding(.bottom, 20)
+                    filtersCard
 
-                if let errorMessage = viewModel.errorMessage {
-                    errorBanner(errorMessage)
-                        .padding(.bottom, 16)
+                    if let errorMessage = viewModel.errorMessage {
+                        errorBanner(errorMessage)
+                            .padding(.top, 12)
+                    }
                 }
+            }
+            .frame(maxWidth: .infinity, maxHeight: .infinity)
 
-                GradientButton(
-                    title: viewModel.isSearching ? "Finding Journals..." : "Find Journals"
-                ) {
-                    viewModel.findJournals()
-                }
-                .disabled(!viewModel.canSearch)
+            GradientButton(
+                title: viewModel.isSearching ? "Finding Journals..." : "Find Journals"
+            ) {
+                viewModel.findJournals()
             }
-            .frame(maxWidth: 1200, alignment: .topLeading)
-            .frame(maxWidth: .infinity, alignment: .topLeading)
-            .padding(.horizontal, horizontalPadding)
-            .padding(.top, topPadding)
-            .padding(.bottom, bottomPadding)
+            .disabled(!viewModel.canSearch)
+            .padding(.top, 16)
         }
+        .frame(maxWidth: 1200, maxHeight: .infinity, alignment: .topLeading)
+        .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
+        .padding(.horizontal, horizontalPadding)
+        .padding(.top, topPadding)
+        .padding(.bottom, bottomPadding)
     }
 
     // MARK: - Results Layout
@@ -194,7 +196,7 @@ struct JournalFinderView: View {
 
     // MARK: - Input Card
 
-    private func inputCard(editorHeight: CGFloat) -> some View {
+    private func inputCard(abstractMinHeight: CGFloat) -> some View {
         VStack(alignment: .leading, spacing: 18) {
             Image("YourContentLogo")
                 .resizable()
@@ -205,7 +207,7 @@ struct JournalFinderView: View {
                 .accessibilityLabel("Your Content")
 
             titleField
-            abstractEditor(height: editorHeight)
+            abstractEditor(minHeight: abstractMinHeight)
             keywordsField
 
             HStack(spacing: 8) {
@@ -221,6 +223,7 @@ struct JournalFinderView: View {
             }
         }
         .padding(22)
+        .frame(maxWidth: .infinity, alignment: .topLeading)
         .background(AppTheme.cardBackground)
         .clipShape(RoundedRectangle(cornerRadius: AppTheme.cardCornerRadius))
         .overlay(
@@ -249,7 +252,7 @@ struct JournalFinderView: View {
         }
     }
 
-    private func abstractEditor(height: CGFloat) -> some View {
+    private func abstractEditor(minHeight: CGFloat) -> some View {
         VStack(alignment: .leading, spacing: 8) {
             fieldLabel("Abstract or Summary", optional: false)
 
@@ -258,9 +261,9 @@ struct JournalFinderView: View {
                     text: $viewModel.abstractText,
                     placeholder: "Paste your abstract, introduction, or manuscript summary here..."
                 )
-                    .frame(height: height)
-
+                    .frame(maxWidth: .infinity, minHeight: minHeight)
             }
+            .frame(maxWidth: .infinity, minHeight: minHeight)
             .background(Color.white)
             .clipShape(RoundedRectangle(cornerRadius: AppTheme.inputCornerRadius))
             .overlay(

+ 6 - 6
gramora/Views/LanguageTranslatorView.swift

@@ -14,17 +14,17 @@ 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.05, 24), 44)
-            let dynamicBottomPadding = min(max(proxy.size.height * 0.06, 28), 56)
+            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) {
                 header
-                    .padding(.bottom, 28)
+                    .padding(.bottom, 24)
 
                 contentCard
-                    .frame(maxWidth: 1200, maxHeight: .infinity, alignment: .topLeading)
-                    .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
+                    .frame(maxWidth: .infinity, maxHeight: .infinity)
             }
+            .frame(maxWidth: 1200, maxHeight: .infinity, alignment: .topLeading)
             .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
             .padding(.horizontal, dynamicHorizontalPadding)
             .padding(.top, dynamicTopPadding)
@@ -108,7 +108,7 @@ struct LanguageTranslatorView: View {
                 .foregroundStyle(AppTheme.textMuted)
                 .fixedSize(horizontal: false, vertical: true)
         }
-        .frame(maxHeight: .infinity, alignment: .top)
+        .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
         .padding(22)
         .background(AppTheme.cardBackground)
         .clipShape(RoundedRectangle(cornerRadius: AppTheme.cardCornerRadius))

+ 59 - 30
gramora/Views/ParaphrasingView.swift

@@ -6,40 +6,67 @@ 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.05, 24), 44)
-            let dynamicBottomPadding = min(max(proxy.size.height * 0.06, 28), 56)
-            let dynamicEditorHeight = min(max(proxy.size.height * 0.43, 260), 560)
+            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) {
-                VStack(alignment: .leading, spacing: 0) {
-                    header
-                        .padding(.bottom, 28)
-
-                    contentCard(editorHeight: dynamicEditorHeight)
-                        .padding(.bottom, 24)
-
-                    if !viewModel.paraphrasedText.isEmpty {
-                        outputCard
+            Group {
+                if viewModel.paraphrasedText.isEmpty {
+                    VStack(alignment: .leading, spacing: 0) {
+                        header
                             .padding(.bottom, 24)
-                    }
 
-                    if let errorMessage = viewModel.errorMessage {
-                        Text(errorMessage)
-                            .font(.system(size: 14))
-                            .foregroundStyle(.red)
-                            .padding(.bottom, 12)
+                        contentCard
+                            .frame(maxWidth: .infinity, maxHeight: .infinity)
+
+                        if let errorMessage = viewModel.errorMessage {
+                            Text(errorMessage)
+                                .font(.system(size: 14))
+                                .foregroundStyle(.red)
+                                .padding(.top, 12)
+                        }
+
+                        GradientButton(title: viewModel.isParaphrasing ? "Paraphrasing..." : "Paraphrase") {
+                            viewModel.paraphrase()
+                        }
+                        .disabled(!viewModel.canParaphrase)
+                        .padding(.top, 16)
                     }
-
-                    GradientButton(title: viewModel.isParaphrasing ? "Paraphrasing..." : "Paraphrase") {
-                        viewModel.paraphrase()
+                    .frame(maxWidth: 1200, maxHeight: .infinity, alignment: .topLeading)
+                    .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
+                    .padding(.horizontal, dynamicHorizontalPadding)
+                    .padding(.top, dynamicTopPadding)
+                    .padding(.bottom, dynamicBottomPadding)
+                } else {
+                    ScrollView(.vertical, showsIndicators: false) {
+                        VStack(alignment: .leading, spacing: 0) {
+                            header
+                                .padding(.bottom, 24)
+
+                            contentCard
+                                .padding(.bottom, 24)
+
+                            outputCard
+                                .padding(.bottom, 24)
+
+                            if let errorMessage = viewModel.errorMessage {
+                                Text(errorMessage)
+                                    .font(.system(size: 14))
+                                    .foregroundStyle(.red)
+                                    .padding(.bottom, 12)
+                            }
+
+                            GradientButton(title: viewModel.isParaphrasing ? "Paraphrasing..." : "Paraphrase") {
+                                viewModel.paraphrase()
+                            }
+                            .disabled(!viewModel.canParaphrase)
+                        }
+                        .frame(maxWidth: 1200, alignment: .topLeading)
+                        .frame(maxWidth: .infinity, alignment: .topLeading)
+                        .padding(.horizontal, dynamicHorizontalPadding)
+                        .padding(.top, dynamicTopPadding)
+                        .padding(.bottom, dynamicBottomPadding)
                     }
-                    .disabled(!viewModel.canParaphrase)
                 }
-                .frame(maxWidth: 1200, alignment: .topLeading)
-                .frame(maxWidth: .infinity, alignment: .topLeading)
-                .padding(.horizontal, dynamicHorizontalPadding)
-                .padding(.top, dynamicTopPadding)
-                .padding(.bottom, dynamicBottomPadding)
             }
         }
         .onChange(of: viewModel.text) { newValue in
@@ -82,7 +109,7 @@ struct ParaphrasingView: View {
         }
     }
 
-    private func contentCard(editorHeight: CGFloat) -> some View {
+    private var contentCard: some View {
         VStack(alignment: .leading, spacing: 16) {
             Image("YourContentLogo")
                 .resizable()
@@ -99,7 +126,7 @@ struct ParaphrasingView: View {
                 )
                     .font(.system(size: 14))
                     .foregroundStyle(AppTheme.textPrimary)
-                    .frame(minHeight: editorHeight, idealHeight: editorHeight)
+                    .frame(maxWidth: .infinity, maxHeight: .infinity)
                     .background(Color.white)
                     .clipShape(RoundedRectangle(cornerRadius: AppTheme.inputCornerRadius))
                     .overlay(
@@ -112,6 +139,7 @@ struct ParaphrasingView: View {
                     .foregroundStyle(AppTheme.textMuted)
                     .frame(maxWidth: .infinity, alignment: .trailing)
             }
+            .frame(maxWidth: .infinity, maxHeight: .infinity)
 
             HStack(spacing: 8) {
                 ToolbarButton(title: "Paste Text", iconName: "doc.on.clipboard") {
@@ -128,6 +156,7 @@ struct ParaphrasingView: View {
             }
         }
         .padding(22)
+        .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
         .background(AppTheme.cardBackground)
         .clipShape(RoundedRectangle(cornerRadius: AppTheme.cardCornerRadius))
         .overlay(

+ 59 - 30
gramora/Views/PunctuationCheckerView.swift

@@ -6,40 +6,67 @@ 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.05, 24), 44)
-            let dynamicBottomPadding = min(max(proxy.size.height * 0.06, 28), 56)
-            let dynamicEditorHeight = min(max(proxy.size.height * 0.43, 260), 560)
+            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) {
-                VStack(alignment: .leading, spacing: 0) {
-                    header
-                        .padding(.bottom, 28)
-
-                    contentCard(editorHeight: dynamicEditorHeight)
-                        .padding(.bottom, 24)
-
-                    if !viewModel.correctedText.isEmpty {
-                        outputCard
+            Group {
+                if viewModel.correctedText.isEmpty {
+                    VStack(alignment: .leading, spacing: 0) {
+                        header
                             .padding(.bottom, 24)
-                    }
 
-                    if let errorMessage = viewModel.errorMessage {
-                        Text(errorMessage)
-                            .font(.system(size: 14))
-                            .foregroundStyle(.red)
-                            .padding(.bottom, 12)
+                        contentCard
+                            .frame(maxWidth: .infinity, maxHeight: .infinity)
+
+                        if let errorMessage = viewModel.errorMessage {
+                            Text(errorMessage)
+                                .font(.system(size: 14))
+                                .foregroundStyle(.red)
+                                .padding(.top, 12)
+                        }
+
+                        GradientButton(title: viewModel.isChecking ? "Checking..." : "Check Punctuation") {
+                            viewModel.checkPunctuation()
+                        }
+                        .disabled(!viewModel.canCheck)
+                        .padding(.top, 16)
                     }
-
-                    GradientButton(title: viewModel.isChecking ? "Checking..." : "Check Punctuation") {
-                        viewModel.checkPunctuation()
+                    .frame(maxWidth: 1200, maxHeight: .infinity, alignment: .topLeading)
+                    .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
+                    .padding(.horizontal, dynamicHorizontalPadding)
+                    .padding(.top, dynamicTopPadding)
+                    .padding(.bottom, dynamicBottomPadding)
+                } else {
+                    ScrollView(.vertical, showsIndicators: false) {
+                        VStack(alignment: .leading, spacing: 0) {
+                            header
+                                .padding(.bottom, 24)
+
+                            contentCard
+                                .padding(.bottom, 24)
+
+                            outputCard
+                                .padding(.bottom, 24)
+
+                            if let errorMessage = viewModel.errorMessage {
+                                Text(errorMessage)
+                                    .font(.system(size: 14))
+                                    .foregroundStyle(.red)
+                                    .padding(.bottom, 12)
+                            }
+
+                            GradientButton(title: viewModel.isChecking ? "Checking..." : "Check Punctuation") {
+                                viewModel.checkPunctuation()
+                            }
+                            .disabled(!viewModel.canCheck)
+                        }
+                        .frame(maxWidth: 1200, alignment: .topLeading)
+                        .frame(maxWidth: .infinity, alignment: .topLeading)
+                        .padding(.horizontal, dynamicHorizontalPadding)
+                        .padding(.top, dynamicTopPadding)
+                        .padding(.bottom, dynamicBottomPadding)
                     }
-                    .disabled(!viewModel.canCheck)
                 }
-                .frame(maxWidth: 1200, alignment: .topLeading)
-                .frame(maxWidth: .infinity, alignment: .topLeading)
-                .padding(.horizontal, dynamicHorizontalPadding)
-                .padding(.top, dynamicTopPadding)
-                .padding(.bottom, dynamicBottomPadding)
             }
         }
         .onChange(of: viewModel.text) { newValue in
@@ -82,7 +109,7 @@ struct PunctuationCheckerView: View {
         }
     }
 
-    private func contentCard(editorHeight: CGFloat) -> some View {
+    private var contentCard: some View {
         VStack(alignment: .leading, spacing: 16) {
             Image("YourContentLogo")
                 .resizable()
@@ -99,7 +126,7 @@ struct PunctuationCheckerView: View {
                 )
                     .font(.system(size: 14))
                     .foregroundStyle(AppTheme.textPrimary)
-                    .frame(minHeight: editorHeight, idealHeight: editorHeight)
+                    .frame(maxWidth: .infinity, maxHeight: .infinity)
                     .background(Color.white)
                     .clipShape(RoundedRectangle(cornerRadius: AppTheme.inputCornerRadius))
                     .overlay(
@@ -112,6 +139,7 @@ struct PunctuationCheckerView: View {
                     .foregroundStyle(AppTheme.textMuted)
                     .frame(maxWidth: .infinity, alignment: .trailing)
             }
+            .frame(maxWidth: .infinity, maxHeight: .infinity)
 
             HStack(spacing: 8) {
                 ToolbarButton(title: "Paste Text", iconName: "doc.on.clipboard") {
@@ -128,6 +156,7 @@ struct PunctuationCheckerView: View {
             }
         }
         .padding(22)
+        .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
         .background(AppTheme.cardBackground)
         .clipShape(RoundedRectangle(cornerRadius: AppTheme.cardCornerRadius))
         .overlay(

+ 26 - 26
gramora/Views/SpellCheckerView.swift

@@ -18,7 +18,6 @@ struct SpellCheckerView: View {
             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)
-            let dynamicEditorHeight = min(max(proxy.size.height * 0.43, 260), 560)
 
             Group {
                 if viewModel.hasResults {
@@ -35,32 +34,31 @@ struct SpellCheckerView: View {
                     .padding(.top, dynamicTopPadding)
                     .padding(.bottom, dynamicBottomPadding)
                 } else {
-                    ScrollView(.vertical, showsIndicators: false) {
-                        VStack(alignment: .leading, spacing: 0) {
-                            header
-                                .padding(.bottom, 24)
-
-                            contentCard(editorHeight: dynamicEditorHeight)
-                                .padding(.bottom, 24)
-
-                            if let errorMessage = viewModel.errorMessage {
-                                Text(errorMessage)
-                                    .font(.system(size: 14))
-                                    .foregroundStyle(.red)
-                                    .padding(.bottom, 12)
-                            }
+                    VStack(alignment: .leading, spacing: 0) {
+                        header
+                            .padding(.bottom, 24)
 
-                            GradientButton(title: viewModel.isChecking ? "Checking..." : "Check Spelling") {
-                                viewModel.checkSpelling()
-                            }
-                            .disabled(!viewModel.canCheck)
+                        contentCard
+                            .frame(maxWidth: .infinity, maxHeight: .infinity)
+
+                        if let errorMessage = viewModel.errorMessage {
+                            Text(errorMessage)
+                                .font(.system(size: 14))
+                                .foregroundStyle(.red)
+                                .padding(.top, 12)
                         }
-                        .frame(maxWidth: 1200, alignment: .topLeading)
-                        .frame(maxWidth: .infinity, alignment: .topLeading)
-                        .padding(.horizontal, dynamicHorizontalPadding)
-                        .padding(.top, dynamicTopPadding)
-                        .padding(.bottom, dynamicBottomPadding)
+
+                        GradientButton(title: viewModel.isChecking ? "Checking..." : "Check Spelling") {
+                            viewModel.checkSpelling()
+                        }
+                        .disabled(!viewModel.canCheck)
+                        .padding(.top, 16)
                     }
+                    .frame(maxWidth: 1200, maxHeight: .infinity, alignment: .topLeading)
+                    .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
+                    .padding(.horizontal, dynamicHorizontalPadding)
+                    .padding(.top, dynamicTopPadding)
+                    .padding(.bottom, dynamicBottomPadding)
                 }
             }
         }
@@ -123,7 +121,7 @@ struct SpellCheckerView: View {
         }
     }
 
-    private func contentCard(editorHeight: CGFloat) -> some View {
+    private var contentCard: some View {
         VStack(alignment: .leading, spacing: 16) {
             Image("YourContentLogo")
                 .resizable()
@@ -140,7 +138,7 @@ struct SpellCheckerView: View {
                 )
                     .font(.system(size: 14))
                     .foregroundStyle(AppTheme.textPrimary)
-                    .frame(minHeight: editorHeight, idealHeight: editorHeight)
+                    .frame(maxWidth: .infinity, maxHeight: .infinity)
                     .background(Color.white)
                     .clipShape(RoundedRectangle(cornerRadius: AppTheme.inputCornerRadius))
                     .overlay(
@@ -153,6 +151,7 @@ struct SpellCheckerView: View {
                     .foregroundStyle(AppTheme.textMuted)
                     .frame(maxWidth: .infinity, alignment: .trailing)
             }
+            .frame(maxWidth: .infinity, maxHeight: .infinity)
 
             HStack(spacing: 8) {
                 ToolbarButton(title: "Paste Text", iconName: "doc.on.clipboard") {
@@ -169,6 +168,7 @@ struct SpellCheckerView: View {
             }
         }
         .padding(22)
+        .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
         .background(AppTheme.cardBackground)
         .clipShape(RoundedRectangle(cornerRadius: AppTheme.cardCornerRadius))
         .overlay(

+ 59 - 30
gramora/Views/SummarizerView.swift

@@ -6,40 +6,67 @@ 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.05, 24), 44)
-            let dynamicBottomPadding = min(max(proxy.size.height * 0.06, 28), 56)
-            let dynamicEditorHeight = min(max(proxy.size.height * 0.43, 260), 560)
+            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) {
-                VStack(alignment: .leading, spacing: 0) {
-                    header
-                        .padding(.bottom, 28)
-
-                    contentCard(editorHeight: dynamicEditorHeight)
-                        .padding(.bottom, 24)
-
-                    if !viewModel.summary.isEmpty {
-                        outputCard
+            Group {
+                if viewModel.summary.isEmpty {
+                    VStack(alignment: .leading, spacing: 0) {
+                        header
                             .padding(.bottom, 24)
-                    }
 
-                    if let errorMessage = viewModel.errorMessage {
-                        Text(errorMessage)
-                            .font(.system(size: 14))
-                            .foregroundStyle(.red)
-                            .padding(.bottom, 12)
+                        contentCard
+                            .frame(maxWidth: .infinity, maxHeight: .infinity)
+
+                        if let errorMessage = viewModel.errorMessage {
+                            Text(errorMessage)
+                                .font(.system(size: 14))
+                                .foregroundStyle(.red)
+                                .padding(.top, 12)
+                        }
+
+                        GradientButton(title: viewModel.isSummarizing ? "Summarizing..." : "Summarize") {
+                            viewModel.summarize()
+                        }
+                        .disabled(!viewModel.canSummarize)
+                        .padding(.top, 16)
                     }
-
-                    GradientButton(title: viewModel.isSummarizing ? "Summarizing..." : "Summarize") {
-                        viewModel.summarize()
+                    .frame(maxWidth: 1200, maxHeight: .infinity, alignment: .topLeading)
+                    .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
+                    .padding(.horizontal, dynamicHorizontalPadding)
+                    .padding(.top, dynamicTopPadding)
+                    .padding(.bottom, dynamicBottomPadding)
+                } else {
+                    ScrollView(.vertical, showsIndicators: false) {
+                        VStack(alignment: .leading, spacing: 0) {
+                            header
+                                .padding(.bottom, 24)
+
+                            contentCard
+                                .padding(.bottom, 24)
+
+                            outputCard
+                                .padding(.bottom, 24)
+
+                            if let errorMessage = viewModel.errorMessage {
+                                Text(errorMessage)
+                                    .font(.system(size: 14))
+                                    .foregroundStyle(.red)
+                                    .padding(.bottom, 12)
+                            }
+
+                            GradientButton(title: viewModel.isSummarizing ? "Summarizing..." : "Summarize") {
+                                viewModel.summarize()
+                            }
+                            .disabled(!viewModel.canSummarize)
+                        }
+                        .frame(maxWidth: 1200, alignment: .topLeading)
+                        .frame(maxWidth: .infinity, alignment: .topLeading)
+                        .padding(.horizontal, dynamicHorizontalPadding)
+                        .padding(.top, dynamicTopPadding)
+                        .padding(.bottom, dynamicBottomPadding)
                     }
-                    .disabled(!viewModel.canSummarize)
                 }
-                .frame(maxWidth: 1200, alignment: .topLeading)
-                .frame(maxWidth: .infinity, alignment: .topLeading)
-                .padding(.horizontal, dynamicHorizontalPadding)
-                .padding(.top, dynamicTopPadding)
-                .padding(.bottom, dynamicBottomPadding)
             }
         }
         .onChange(of: viewModel.text) { newValue in
@@ -82,7 +109,7 @@ struct SummarizerView: View {
         }
     }
 
-    private func contentCard(editorHeight: CGFloat) -> some View {
+    private var contentCard: some View {
         VStack(alignment: .leading, spacing: 16) {
             Image("YourContentLogo")
                 .resizable()
@@ -99,7 +126,7 @@ struct SummarizerView: View {
                 )
                     .font(.system(size: 14))
                     .foregroundStyle(AppTheme.textPrimary)
-                    .frame(minHeight: editorHeight, idealHeight: editorHeight)
+                    .frame(maxWidth: .infinity, maxHeight: .infinity)
                     .background(Color.white)
                     .clipShape(RoundedRectangle(cornerRadius: AppTheme.inputCornerRadius))
                     .overlay(
@@ -112,6 +139,7 @@ struct SummarizerView: View {
                     .foregroundStyle(AppTheme.textMuted)
                     .frame(maxWidth: .infinity, alignment: .trailing)
             }
+            .frame(maxWidth: .infinity, maxHeight: .infinity)
 
             HStack(spacing: 8) {
                 ToolbarButton(title: "Paste Text", iconName: "doc.on.clipboard") {
@@ -128,6 +156,7 @@ struct SummarizerView: View {
             }
         }
         .padding(22)
+        .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
         .background(AppTheme.cardBackground)
         .clipShape(RoundedRectangle(cornerRadius: AppTheme.cardCornerRadius))
         .overlay(

+ 6 - 6
gramora/Views/WordsCountView.swift

@@ -6,17 +6,17 @@ 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.05, 24), 44)
-            let dynamicBottomPadding = min(max(proxy.size.height * 0.06, 28), 56)
+            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) {
                 header
-                    .padding(.bottom, 28)
+                    .padding(.bottom, 24)
 
                 contentCard
-                    .frame(maxWidth: 1200, maxHeight: .infinity, alignment: .topLeading)
-                    .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
+                    .frame(maxWidth: .infinity, maxHeight: .infinity)
             }
+            .frame(maxWidth: 1200, maxHeight: .infinity, alignment: .topLeading)
             .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
             .padding(.horizontal, dynamicHorizontalPadding)
             .padding(.top, dynamicTopPadding)
@@ -115,7 +115,7 @@ struct WordsCountView: View {
                 }
             }
         }
-        .frame(maxHeight: .infinity, alignment: .top)
+        .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
         .padding(22)
         .background(AppTheme.cardBackground)
         .clipShape(RoundedRectangle(cornerRadius: AppTheme.cardCornerRadius))