feat: add chat message list package#52
Merged
Merged
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
There was a problem hiding this comment.
Pull request overview
This PR introduces a new reusable SwiftUI MessageList package and migrates both mobile and macOS chat transcripts to use it, aiming to centralize scroll anchoring, pinning behavior, and pagination triggers across clients.
Changes:
- Added a new
MessageListSwift Package product (with unit/UI-adjacent tests and preview/simulator harnesses). - Replaced bespoke transcript
ScrollViewlogic in mobile and macOS withChatTranscriptList(a thin adapter overMessageList). - Updated streaming text flush behavior in
AppStateto group text deltas (token-based) and force flushes at tool/result boundaries and stream lifecycle edges.
Reviewed changes
Copilot reviewed 21 out of 21 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| RxCodeMobile/Views/MobileChatView.swift | Switches mobile transcript rendering to ChatTranscriptList and adapts scroll-to-bottom behavior. |
| RxCode/App/AppState+Stream.swift | Implements token-grouped text delta flushing and forces flushes before tool events / stream detaches. |
| RxCode/App/AppState+Project.swift | Forces text flush when switching projects while streaming. |
| RxCode/App/AppState+Messaging.swift | Forces text flush when finalizing a stream session. |
| RxCode/App/AppState+CrossProject.swift | Forces text flush before applying tool-use cross-project handling. |
| RxCode/App/AppState.swift | Updates documentation for the stream text buffer behavior. |
| Packages/Tests/MessageListTests/MessageListScrollAnchorTests.swift | Adds unit tests for scroll anchor decisions. |
| Packages/Tests/MessageListTests/MessageListPreviewTests.swift | Adds ViewInspector tests for the preview harness behavior (DEBUG-only). |
| Packages/Tests/MessageListTests/MessageListPinningControllerTests.swift | Adds unit tests for pinning controller actions. |
| Packages/Sources/RxCodeChatKit/MessageListView.swift | Migrates macOS message list to ChatTranscriptList and new scroll request mechanism. |
| Packages/Sources/RxCodeChatKit/MessageBubble.swift | Adds streaming fade-in behavior for assistant markdown content. |
| Packages/Sources/RxCodeChatKit/ChatTranscriptList.swift | Introduces ChatKit adapter types (ChatTranscriptListItem/ChatTranscriptList). |
| Packages/Sources/MessageList/MessageListScrollAnchor.swift | Adds anchor state machine for “near bottom” + content-growth decisions. |
| Packages/Sources/MessageList/MessageListPreview.swift | Adds a DEBUG preview harness for manual interaction testing. |
| Packages/Sources/MessageList/MessageListPinningController.swift | Adds controller for pin/re-pin/release decisions around user-message pinning. |
| Packages/Sources/MessageList/MessageList.swift | Adds the reusable list implementation (scroll tracking, pinning, paging triggers). |
| Packages/Package.swift | Registers MessageList as a product/target and adds a new test target. |
| Packages/Examples/MessageListSimulatorApp/README.md | Documents opening/building the simulator host app for manual testing. |
| Packages/Examples/MessageListSimulatorApp/MessageListSimulatorApp/MessageListSimulatorApp.swift | Adds a small iOS host app for manual list testing. |
| Packages/Examples/MessageListSimulatorApp/MessageListSimulatorApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved | Adds resolved package pins for the example app workspace. |
| Packages/Examples/MessageListSimulatorApp/MessageListSimulatorApp.xcodeproj/project.pbxproj | Adds the Xcode project for the example simulator app. |
Comments suppressed due to low confidence (1)
RxCodeMobile/Views/MobileChatView.swift:1016
- After moving to
ChatTranscriptList,distanceFromBottomis no longer updated anywhere, but the scroll-to-bottom button log still reports it. This makes the log data misleading; either compute/report distance using the new list’s metrics or remove the field from the log message.
Button {
mobileChatLogger.info(
"[ScrollButton] tap session=\(sessionID, privacy: .public) messages=\(messages.count, privacy: .public) distance=\(Double(distanceFromBottom), privacy: .public) padding=\(Double(scrollToBottomButtonBottomPadding), privacy: .public)"
)
autoScrollEnabled = true
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| return .releasePinAndScrollToBottom | ||
| } | ||
|
|
||
| mutating func releasePin() { |
Comment on lines
+125
to
+132
| scrollToBottom(proxy: proxy, animated: false) | ||
| } | ||
| } | ||
| .onChange(of: shouldScrollToBottom) { _, shouldScroll in | ||
| guard shouldScroll else { return } | ||
| guard !pinning.isPinningUserMessage else { return } | ||
| anchor.resetToBottom() | ||
| scrollToBottom(proxy: proxy, animated: true) |
Comment on lines
+37
to
+42
|
|
||
| if contentGrew { | ||
| return previouslyNearBottom ? .scrollToBottom : .none | ||
| } | ||
|
|
||
| isNearBottom = nowNearBottom |
Comment on lines
+108
to
+121
| MessageList( | ||
| messages: items, | ||
| isStreaming: isStreaming, | ||
| shouldScrollToBottom: shouldScrollToBottom, | ||
| isAtBottom: $isAtBottom, | ||
| hasMorePrevious: hasMorePrevious, | ||
| loadMorePrevious: loadMorePrevious, | ||
| onLoadError: onLoadError | ||
| ) { item in | ||
| rowContent(for: item) | ||
| .padding(rowPadding) | ||
| } | ||
| .accessibilityElement(children: .contain) | ||
| .accessibilityIdentifier(accessibilityIdentifier ?? "") |
Comment on lines
+76
to
+85
| ForEach(messages) { message in | ||
| let messageID = message.id | ||
| rowContent(message) | ||
| .onGeometryChange(for: CGFloat.self) { geometry in | ||
| geometry.frame(in: .named(MessageListConstants.coordinateSpaceName)).minY | ||
| } action: { value in | ||
| guard messageID == pinning.pinnedUserMessageID else { return } | ||
| updateLatestUserMinY(value) | ||
| } | ||
| .id(messageID) |
| handleLastMessageChange() | ||
| } | ||
| .onChange(of: chatBridge.messages.last?.content) { _, _ in | ||
| guard isSessionReady else { return } |
Contributor
Author
|
🎉 This PR is included in version 1.11.0 🎉 The release is available on GitHub release Your semantic-release bot 📦🚀 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.