|
|
@@ -0,0 +1,134 @@
|
|
|
+import SwiftUI
|
|
|
+
|
|
|
+struct ParaphrasingView: View {
|
|
|
+ @StateObject private var viewModel = ParaphrasingViewModel()
|
|
|
+
|
|
|
+ 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)
|
|
|
+
|
|
|
+ ScrollView(.vertical, showsIndicators: false) {
|
|
|
+ VStack(alignment: .leading, spacing: 0) {
|
|
|
+ header
|
|
|
+ .padding(.bottom, 28)
|
|
|
+
|
|
|
+ contentCard(editorHeight: dynamicEditorHeight)
|
|
|
+ .padding(.bottom, 24)
|
|
|
+
|
|
|
+ GradientButton(title: "Paraphrase") {
|
|
|
+ viewModel.paraphrase()
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .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
|
|
|
+ if newValue.count > ParaphrasingViewModel.maxCharacters {
|
|
|
+ viewModel.text = String(newValue.prefix(ParaphrasingViewModel.maxCharacters))
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private var header: some View {
|
|
|
+ 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 {
|
|
|
+ SettingsGearIcon()
|
|
|
+ } action: {}
|
|
|
+ .accessibilityLabel("Settings")
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private func contentCard(editorHeight: CGFloat) -> some View {
|
|
|
+ VStack(alignment: .leading, spacing: 16) {
|
|
|
+ Image("YourContentLogo")
|
|
|
+ .resizable()
|
|
|
+ .aspectRatio(contentMode: .fit)
|
|
|
+ .frame(height: 52, alignment: .topLeading)
|
|
|
+ .frame(height: 46, alignment: .topLeading)
|
|
|
+ .clipped()
|
|
|
+ .accessibilityLabel("Your Content")
|
|
|
+
|
|
|
+ ZStack(alignment: .bottomTrailing) {
|
|
|
+ ThinCaretTextEditor(text: $viewModel.text)
|
|
|
+ .font(.system(size: 14))
|
|
|
+ .foregroundStyle(AppTheme.textPrimary)
|
|
|
+ .frame(minHeight: editorHeight, idealHeight: editorHeight)
|
|
|
+ .background(Color.white)
|
|
|
+ .clipShape(RoundedRectangle(cornerRadius: AppTheme.inputCornerRadius))
|
|
|
+ .overlay(
|
|
|
+ RoundedRectangle(cornerRadius: AppTheme.inputCornerRadius)
|
|
|
+ .stroke(AppTheme.inputBorder, lineWidth: 1)
|
|
|
+ )
|
|
|
+ .overlay(alignment: .topLeading) {
|
|
|
+ if viewModel.text.isEmpty {
|
|
|
+ Text("Type or paste your text here and press the paraphrase button...")
|
|
|
+ .font(.system(size: 14))
|
|
|
+ .foregroundStyle(AppTheme.textMuted)
|
|
|
+ .padding(.leading, AppTheme.textEditorHorizontalInset)
|
|
|
+ .padding(.top, AppTheme.textEditorVerticalInset)
|
|
|
+ .allowsHitTesting(false)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ Text(viewModel.characterCountLabel)
|
|
|
+ .font(.system(size: 11))
|
|
|
+ .foregroundStyle(AppTheme.textMuted)
|
|
|
+ .padding(.trailing, 16)
|
|
|
+ .padding(.bottom, 12)
|
|
|
+ }
|
|
|
+
|
|
|
+ HStack(spacing: 8) {
|
|
|
+ ToolbarButton(title: "Paste Text", iconName: "doc.on.clipboard") {
|
|
|
+ viewModel.pasteText()
|
|
|
+ }
|
|
|
+
|
|
|
+ ToolbarButton(title: "Upload File", iconName: "square.and.arrow.up") {}
|
|
|
+
|
|
|
+ Spacer()
|
|
|
+
|
|
|
+ ClearButton {
|
|
|
+ viewModel.clearText()
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .padding(22)
|
|
|
+ .background(AppTheme.cardBackground)
|
|
|
+ .clipShape(RoundedRectangle(cornerRadius: AppTheme.cardCornerRadius))
|
|
|
+ .overlay(
|
|
|
+ RoundedRectangle(cornerRadius: AppTheme.cardCornerRadius)
|
|
|
+ .stroke(AppTheme.border, lineWidth: 1)
|
|
|
+ )
|
|
|
+ .shadow(color: AppTheme.cardShadow, radius: 16, y: 6)
|
|
|
+ }
|
|
|
+}
|