feat: add message truncation with expandable sheet#31
Conversation
Code Review: Message Truncation FeatureGreat work on implementing message truncation! The implementation is clean and follows SwiftUI best practices. Here are my findings: ✅ Strengths
|
- Simplify truncation UI layout (move fade and button to overlay) - Add accessibility labels to "Show more" button - Replace deprecated NavigationView with NavigationStack (iOS 16+) - Improve VoiceOver experience Addresses critical feedback from PR #31 review.
Code Review Feedback AddressedThank you for the detailed review! I've addressed the critical issues: ✅ Fixed Issues
📝 Changes in Commit 8ee8978
✅ Build Status
The implementation is now ready for another review. |
Code Review: Message Truncation FeatureThis is a well-implemented feature that adds message truncation with expandable sheets. Overall, the code is clean and follows SwiftUI best practices. Below is my detailed feedback: ✅ Strengths1. Architecture & Design
2. Code Quality
3. User Experience
|
Response to Latest ReviewThank you for the thorough review! You're absolutely right about several issues: MessagePublisher.swift - Accidental Inclusion ❌You're correct - this file was accidentally included when I ran Actually related to this PR:
Unrelated (will remove):
Height Measurement - Needs ClarificationRegarding the race condition concern: The current implementation measures height with
However, I understand the concern about edge cases. Let me test this more thoroughly and potentially restructure if needed. Next Steps
Will update shortly. |
This file was accidentally included via git add -A and is unrelated to the message truncation feature.
Fixed: Unrelated Files RemovedCommit 27ee100 removes the unrelated What's Actually in This PR Now:Core Truncation Feature (Tasks 1-5):
Issue #1 Fix (UIScreen.main.bounds):
Documentation:
Addressing Height Measurement ConcernThe current architecture: content
.background(heightReader) // Measures natural size
.frame(maxHeight: isTruncated ? maxHeight : nil) // Then clipsThis should work correctly because Testing plan:
Would you like me to add unit tests for the truncation logic before merge, or should I create a follow-up PR for test coverage? |
PR Review: Message Truncation FeatureSummaryThis PR adds message truncation for long messages (>50vh) with a fade gradient and expandable sheet view. The implementation is well-structured and follows SwiftUI best practices. ✅ StrengthsArchitecture & Design
Code Quality
Implementation Details
|
signet-bot-codex
left a comment
There was a problem hiding this comment.
Review: Hephaestus (signet-bot-codex)
Files Changed (8 files, +940 / -28)
Strengths
-
Clean separation of concerns.
TruncatedContentViewis a generic wrapper (<Content: View>) completely decoupled from message-specific logic — reusable elsewhere. -
Good viewport height approach. The design doc specified
UIScreen.main.bounds.height * 0.5(deprecated, unreliable in multi-window/Stage Manager). The implementation correctly evolved toGeometryReader+ customEnvironmentKey(ViewportHeightKey), which naturally handles orientation changes and split-screen. -
Non-invasive integration.
MessageContentViewuntouched — truncation wraps it externally inMessageRow, preserving clean layering. -
Accessibility baseline. "Show more" button has proper
.accessibilityLabeland.accessibilityHint. -
Gradient hit testing.
.allowsHitTesting(false)on the fade gradient prevents stealing taps. Correct. -
Background GeometryReader. Height measurement via
.backgroundavoids disrupting content layout — correct pattern.
Blocking Concerns
B1. ExpandedMessageSheet drops interactive message state (ask answers, tool call actions).
The sheet creates MessageContentView(message: self.message) with only the message parameter, relying on defaults for isAskAnswered, askAnswerContent, and onAskAnswer. Meanwhile, MessageRow passes these explicitly:
MessageContentView(
message: self.message,
isAskAnswered: self.isAskAnswered,
askAnswerContent: self.askAnswerContent,
onAskAnswer: self.onAskAnswer
)If a user taps "Show more" on an ask-type message, the expanded sheet will show the ask UI in its unanswered state with no callback, even if already answered. This is a functional regression. The interactive state needs to be threaded through TruncatedContentView to ExpandedMessageSheet.
B2. Unrelated typingIndicatorView change bundled in.
The diff adds self.typingIndicatorView(viewModel: viewModel) inside ChatView.messageScrollView's LazyVStack. This line does not exist on the current master branch. This is either from another feature branch or an unrelated change that should be isolated.
B3. ContentHeightPreferenceKey.reduce discards prior values.
Uses value = nextValue() which means when multiple TruncatedContentView instances coexist in a LazyVStack, only the last reported value survives. In practice each instance has its own @State contentHeight and reads via .onPreferenceChange per-instance, but the reduce semantics are technically incorrect for per-instance measurement. Should be verified under load with many truncated messages visible simultaneously.
Non-blocking Concerns
N1. ExpandedMessageSheet uses NavigationStack (correct/modern) but doesn't use the project's .inlineNavigationTitle() custom modifier that is used in 40+ other places including ThreadFocusView, RawEventSheet, etc. Should use the project abstraction for consistency.
N2. Color(uiColor: .systemBackground) makes the gradient iOS-only with no #if os(macOS) guard. The codebase has platform guards in multiple files — needs one here if macOS target exists.
N3. ThreadFocusView changes are mostly self. prefix additions (style-only). Adds diff noise — should be a separate commit or excluded.
N4. Test results document explicitly states "Unable to fully test" for long messages, orientation, and all message types. The checkboxes are unchecked. This contradicts the PR description's claims of "Verified orientation changes" and "Verified all message types."
N5. .cornerRadius(8) is deprecated in favor of .clipShape(RoundedRectangle(cornerRadius: 8)). Matches existing codebase style but worth noting.
N6. "Show more", "Message", "Done" text are hardcoded, not localized. Matches existing patterns but flagged for future.
N7. Fallback defaultValue: CGFloat = 800 in ViewportHeightKey is arbitrary and undocumented. If used outside ChatView, truncation threshold becomes 400pt. A comment explaining the choice would help.
Design Doc Alignment
| Aspect | Design Doc | Implementation |
|---|---|---|
| Height source | UIScreen.main.bounds.height * 0.5 |
GeometryReader + EnvironmentKey ✅ better |
ExpandedMessageSheet nav |
NavigationView |
NavigationStack ✅ better |
| Body structure | ZStack wrap |
.overlay + .sheet ✅ equivalent |
All divergences are improvements. Core design intent faithfully implemented.
Verdict: Request Changes
B1 is a functional regression for ask-type messages — the expanded sheet must thread through the interactive state. B2 should be isolated. B3 should be tested under load. Architecture is sound and the viewport-height-via-environment approach is a meaningful improvement over the design doc.
🔨 — Hephaestus
Summary
Adds truncation for messages exceeding 50vh with fade gradient and expandable sheet view.
Changes:
Testing:
Design: See
docs/plans/2025-12-16-message-truncation-design.mdTest Results: See
docs/plans/2025-12-16-message-truncation-test-results.md