@@ -202,15 +202,23 @@ public struct MessageList<Message: MessageListItem, RowContent: View>: View {
202202 }
203203
204204 private var pinTailSpacerHeight : CGFloat {
205- guard pinning. pinnedUserMessageID != nil , scrollViewHeight > 0 else { return 0 }
206- let turnHeight = max ( activeTurnMaxMeasuredHeight, rawActiveTurnMeasuredHeight)
207- return max ( 0 , scrollViewHeight - turnHeight - 16 )
205+ guard pinning. isPinningUserMessage, scrollViewHeight > 0 else { return 0 }
206+ return max ( 0 , scrollViewHeight - activeTurnHeight - MessageListConstants. minimumPinnedTailSpacing)
208207 }
209208
210209 private var rawActiveTurnMeasuredHeight : CGFloat {
211210 max ( 0 , tailMarkerMinY - latestUserMinY)
212211 }
213212
213+ private var activeTurnHeight : CGFloat {
214+ max ( activeTurnMaxMeasuredHeight, rawActiveTurnMeasuredHeight)
215+ }
216+
217+ private var pinnedTurnFillsViewport : Bool {
218+ guard scrollViewHeight > 0 else { return false }
219+ return activeTurnHeight >= scrollViewHeight - MessageListConstants. minimumPinnedTailSpacing
220+ }
221+
214222 private var isAnchoredAtBottom : Bool {
215223 anchor. isNearBottom && isAtBottom
216224 }
@@ -223,6 +231,22 @@ public struct MessageList<Message: MessageListItem, RowContent: View>: View {
223231 messages. last { $0. isUserMessage } ? . id
224232 }
225233
234+ private var hasContentAfterPinnedUserMessage : Bool {
235+ guard let pinnedID = pinning. pinnedUserMessageID,
236+ let pinnedIndex = messages. firstIndex ( where: { $0. id == pinnedID } )
237+ else { return false }
238+
239+ let nextIndex = messages. index ( after: pinnedIndex)
240+ guard nextIndex < messages. endIndex else { return false }
241+ return messages [ nextIndex... ] . contains { !$0. isMessageListAccessory }
242+ }
243+
244+ private var shouldReleasePinnedUserMessageForFilledTurn : Bool {
245+ pinning. isPinningUserMessage
246+ && hasContentAfterPinnedUserMessage
247+ && pinnedTurnFillsViewport
248+ }
249+
226250 private var messageListChangeToken : MessageListChangeToken < Message . ID > {
227251 MessageListChangeToken (
228252 ids: messages. map ( \. id) ,
@@ -237,6 +261,11 @@ public struct MessageList<Message: MessageListItem, RowContent: View>: View {
237261 visibleMaxY: metrics. visibleMaxY
238262 )
239263 updateIsAtBottomBinding ( anchor. isNearBottom)
264+
265+ if shouldReleasePinnedUserMessageForFilledTurn, !isUserDrivenScroll {
266+ releasePinnedUserMessage ( proxy: proxy)
267+ }
268+
240269 if decision == . scrollToBottom,
241270 isAtBottom,
242271 isStreaming,
@@ -426,7 +455,7 @@ public struct MessageList<Message: MessageListItem, RowContent: View>: View {
426455 }
427456
428457 private func updateActiveTurnMaxMeasuredHeight( ) {
429- guard pinning. pinnedUserMessageID != nil else { return }
458+ guard pinning. isPinningUserMessage else { return }
430459 let measured = rawActiveTurnMeasuredHeight
431460 guard measured > activeTurnMaxMeasuredHeight + 0.5 else { return }
432461 var transaction = Transaction ( )
@@ -492,6 +521,7 @@ private nonisolated enum MessageListConstants {
492521 static let tailMarkerID = " message-list-tail-marker "
493522 static let coordinateSpaceName = " message-list-content "
494523 static let loadThreshold : CGFloat = 96
524+ static let minimumPinnedTailSpacing : CGFloat = 16
495525 static let userScrollDownDelta : CGFloat = 4
496526 static let layoutSettleDelayNanoseconds : UInt64 = 16_000_000
497527 static let streamingBottomScrollInterval : TimeInterval = 2
0 commit comments