From 16c3abe9099ca9029f803ca182faec0966d610ec Mon Sep 17 00:00:00 2001 From: bootoshi <127834715+kingbootoshi@users.noreply.github.com> Date: Sat, 22 Aug 2026 19:23:12 -0400 Subject: [PATCH] Float the composer suggestion popup above the input The slash, file, and skill suggestion lists were bottom-anchored onto the composer container, covering the text field the user is typing into. Anchor the popup's bottom edge to the composer's top edge so suggestions float over the transcript and the draft stays visible. Co-Authored-By: Claude Fable 5 --- ...ConversationComposerPopupOverlayView.swift | 76 +++++++++---------- .../Litter/Views/ConversationView.swift | 3 +- 2 files changed, 37 insertions(+), 42 deletions(-) diff --git a/apps/ios/Sources/Litter/Views/ConversationComposerPopupOverlayView.swift b/apps/ios/Sources/Litter/Views/ConversationComposerPopupOverlayView.swift index 4f7007e37..b8201e99b 100644 --- a/apps/ios/Sources/Litter/Views/ConversationComposerPopupOverlayView.swift +++ b/apps/ios/Sources/Litter/Views/ConversationComposerPopupOverlayView.swift @@ -28,30 +28,17 @@ struct ConversationComposerPopupOverlayView: View { suggestionPopup { let indexedSuggestions = Array(suggestions.enumerated()) ForEach(indexedSuggestions, id: \.offset) { item in - let index = item.offset - let command = item.element VStack(spacing: 0) { - Button { - onApplySlashSuggestion(command) - } label: { - HStack(spacing: 10) { - Text("/\(command.rawValue)") - .litterFont(.body) - .foregroundColor(LitterTheme.success) - Text(command.description) - .litterFont(.body) - .foregroundColor(LitterTheme.textSecondary) - .lineLimit(1) - Spacer(minLength: 0) - } - .padding(.horizontal, 12) - .padding(.vertical, 9) + nameDescriptionRow( + name: "/\(item.element.rawValue)", + description: item.element.description + ) { + onApplySlashSuggestion(item.element) } - .buttonStyle(.plain) Divider() .background(LitterTheme.border) - .opacity(index < suggestions.count - 1 ? 1 : 0) + .opacity(item.offset < suggestions.count - 1 ? 1 : 0) } } } @@ -154,30 +141,17 @@ struct ConversationComposerPopupOverlayView: View { } else { let indexedSuggestions = Array(Array(suggestions.prefix(8)).enumerated()) ForEach(indexedSuggestions, id: \.offset) { item in - let index = item.offset - let skill = item.element VStack(spacing: 0) { - Button { - onApplySkillSuggestion(skill) - } label: { - HStack(spacing: 8) { - Text("$\(skill.name)") - .litterFont(.footnote) - .foregroundColor(LitterTheme.success) - Text(skill.description) - .litterFont(.footnote) - .foregroundColor(LitterTheme.textSecondary) - .lineLimit(1) - Spacer(minLength: 0) - } - .padding(.horizontal, 12) - .padding(.vertical, 9) + nameDescriptionRow( + name: "$\(item.element.name)", + description: item.element.description + ) { + onApplySkillSuggestion(item.element) } - .buttonStyle(.plain) Divider() .background(LitterTheme.border) - .opacity(index < indexedSuggestions.count - 1 ? 1 : 0) + .opacity(item.offset < indexedSuggestions.count - 1 ? 1 : 0) } } } @@ -185,6 +159,28 @@ struct ConversationComposerPopupOverlayView: View { } } + private func nameDescriptionRow( + name: String, + description: String, + action: @escaping () -> Void + ) -> some View { + Button(action: action) { + HStack(spacing: 8) { + Text(name) + .litterFont(.footnote) + .foregroundColor(LitterTheme.success) + Text(description) + .litterFont(.footnote) + .foregroundColor(LitterTheme.textSecondary) + .lineLimit(1) + Spacer(minLength: 0) + } + .padding(.horizontal, 12) + .padding(.vertical, 9) + } + .buttonStyle(.plain) + } + @ViewBuilder private func sectionHeader(_ title: String) -> some View { Text(title) @@ -218,8 +214,6 @@ struct ConversationComposerPopupOverlayView: View { .stroke(LitterTheme.border, lineWidth: 1) ) .clipShape(RoundedRectangle(cornerRadius: 8)) - .padding(.horizontal, 12) - .padding(.bottom, 4) - .padding(.bottom, 56) + .padding(.horizontal, 10) } } diff --git a/apps/ios/Sources/Litter/Views/ConversationView.swift b/apps/ios/Sources/Litter/Views/ConversationView.swift index b7e978911..1bc318768 100644 --- a/apps/ios/Sources/Litter/Views/ConversationView.swift +++ b/apps/ios/Sources/Litter/Views/ConversationView.swift @@ -1639,7 +1639,7 @@ private struct ConversationInputBar: View { isComposerFocused: $isComposerFocused, composerSelectionRange: composerSelection.binding ) - .overlay(alignment: .bottom) { + .overlay(alignment: .top) { ConversationComposerPopupOverlayView( state: popupState, onApplySlashSuggestion: applySlashSuggestion, @@ -1647,6 +1647,7 @@ private struct ConversationInputBar: View { onApplySkillSuggestion: applySkillSuggestion, onApplyPluginSuggestion: applyPluginSuggestion ) + .alignmentGuide(.top) { $0[.bottom] } } } .dropDestination(for: URL.self) { urls, _ in