diff --git a/Rosetta.xcodeproj/project.pbxproj b/Rosetta.xcodeproj/project.pbxproj index 7192571..36e2ebd 100644 --- a/Rosetta.xcodeproj/project.pbxproj +++ b/Rosetta.xcodeproj/project.pbxproj @@ -530,7 +530,7 @@ 853F296C2F4B50420092AD05 /* Release */, ); defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; + defaultConfigurationName = Debug; }; 853F296D2F4B50420092AD05 /* Build configuration list for PBXNativeTarget "Rosetta" */ = { isa = XCConfigurationList; @@ -539,7 +539,7 @@ 853F296F2F4B50420092AD05 /* Release */, ); defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; + defaultConfigurationName = Debug; }; B5D2E60ADEB8AE2E8F7615C6 /* Build configuration list for PBXNativeTarget "RosettaNotificationService" */ = { isa = XCConfigurationList; @@ -548,7 +548,7 @@ 0140D6320A9CF4B5E933E0B1 /* Debug */, ); defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; + defaultConfigurationName = Debug; }; /* End XCConfigurationList section */ diff --git a/Rosetta.xcodeproj/xcshareddata/xcschemes/Rosetta.xcscheme b/Rosetta.xcodeproj/xcshareddata/xcschemes/Rosetta.xcscheme index d26f870..104d1d6 100644 --- a/Rosetta.xcodeproj/xcshareddata/xcschemes/Rosetta.xcscheme +++ b/Rosetta.xcodeproj/xcshareddata/xcschemes/Rosetta.xcscheme @@ -31,7 +31,7 @@ shouldAutocreateTestPlan = "YES"> Void var onUserTextInsertion: () -> Void = {} var onMultilineChange: (Bool) -> Void = { _ in } + /// Reports the ideal height (clamped to max 5 lines) on every text change. + /// Used to drive `@State` frame height in the composer layout. + var onTextHeightChange: (CGFloat) -> Void = { _ in } var font: UIFont = .systemFont(ofSize: 17, weight: .regular) var textColor: UIColor = .white var placeholderColor: UIColor = UIColor.white.withAlphaComponent(0.35) @@ -255,6 +258,22 @@ struct ChatTextInput: UIViewRepresentable { tv.setNeedsLayout() tv.layoutIfNeeded() checkMultiline(tv) + reportHeight(tv) + } + + /// Computes the ideal height (clamped to max 5 lines) and reports to SwiftUI. + /// This drives `@State textInputHeight` in the composer → forces relayout. + private func reportHeight(_ tv: UITextView) { + let lineHeight = tv.font?.lineHeight ?? 20 + let maxLines: CGFloat = 5 + let insets = tv.textContainerInset + let maxTextHeight = lineHeight * maxLines + let maxTotalHeight = maxTextHeight + insets.top + insets.bottom + let fittingSize = tv.sizeThatFits( + CGSize(width: tv.bounds.width, height: .greatestFiniteMagnitude) + ) + let height = max(36, min(fittingSize.height, maxTotalHeight)) + parent.onTextHeightChange(height) } private func checkMultiline(_ tv: UITextView) { diff --git a/Rosetta/Features/Chats/ChatDetail/ChatDetailView.swift b/Rosetta/Features/Chats/ChatDetail/ChatDetailView.swift index 4818720..d958d30 100644 --- a/Rosetta/Features/Chats/ChatDetail/ChatDetailView.swift +++ b/Rosetta/Features/Chats/ChatDetail/ChatDetailView.swift @@ -39,6 +39,7 @@ struct ChatDetailView: View { @State private var messageText = "" @State private var isMultilineInput = false + @State private var textInputHeight: CGFloat = 36 @State private var sendError: String? @State private var isViewActive = false // markReadTask removed — read receipts no longer sent from .onChange(of: messages.count) @@ -995,11 +996,17 @@ private extension ChatDetailView { isMultilineInput = multiline } }, + onTextHeightChange: { h in + withAnimation(.easeInOut(duration: 0.2)) { + textInputHeight = h + } + }, textColor: UIColor(RosettaColors.Adaptive.text), placeholderColor: UIColor(RosettaColors.Adaptive.textSecondary.opacity(0.5)) ) .padding(.leading, 6) - .frame(maxWidth: .infinity, minHeight: 36, alignment: .bottomLeading) + .frame(maxWidth: .infinity, alignment: .bottomLeading) + .frame(height: textInputHeight) HStack(alignment: .center, spacing: 0) { Button { @@ -1052,6 +1059,7 @@ private extension ChatDetailView { .padding(3) .frame(minHeight: 42, alignment: .bottom) .background { glass(shape: .rounded(isMultilineInput ? 16 : 21), strokeOpacity: 0.18) } + .clipShape(RoundedRectangle(cornerRadius: isMultilineInput ? 16 : 21, style: .continuous)) .padding(.leading, 6) Button(action: trailingAction) {