mac: translucent material band behind the composer row (iMessage-style) (#377)
In dark mode the feed scrolled fully-legible text behind the composer strip. Now the input row (and its transient busy caption) rides ONE .bar material band, so what passes beneath shows as blur — the iMessage treatment. Only the row: the typeahead/banners/staged chips above stay transparent floaters, so the band can never grow and curtain the chat (the failure that got the original full-inset slab removed). iOS needs nothing — its composer already wears floating-glass capsules.
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 file changed,
+23
−15
ios/LChatMac/NativeFeedView.swift+23 −15
diff --git a/ios/LChatMac/NativeFeedView.swift b/ios/LChatMac/NativeFeedView.swift| index 20999d5..6a3e945 100644 |
| --- a/ios/LChatMac/NativeFeedView.swift |
| +++ b/ios/LChatMac/NativeFeedView.swift |
| @@ -310,20 +310,11 @@ struct NativeFeedView: View { |
| } |
| } |
| .safeAreaInset(edge: .bottom) { |
| - VStack(spacing: 0) { |
| - composer |
| - // Transient feedback only (downloads, upload errors) — no |
| - // permanent status line. |
| - if let busyText { |
| - Text(busyText) |
| - .font(.caption2) |
| - .foregroundStyle(.secondary) |
| - .frame(maxWidth: .infinity) |
| - .padding(4) |
| - } |
| - } |
| - // No material slab: staged previews/banners float over the feed — |
| - // the .bar background grew with them and curtained the chat below. |
| + // The material band lives on the input row INSIDE `composer`, never |
| + // on this whole inset: staged previews/banners float transparent |
| + // above it, so the band can't grow and curtain the chat (the |
| + // failure that got the original full-inset .bar slab removed). |
| + composer |
| } |
| // Track the feed's window frame so the swipe monitor only claims |
| // scroll events that happen over it. |
| @@ -583,7 +574,24 @@ struct NativeFeedView: View { |
| if editingID != nil { |
| composerBanner("Editing message", detail: nil) { cancelEdit() } |
| } |
| - composerRow |
| + VStack(spacing: 0) { |
| + composerRow |
| + // Transient feedback only (downloads, upload errors) — no |
| + // permanent status line. |
| + if let busyText { |
| + Text(busyText) |
| + .font(.caption2) |
| + .foregroundStyle(.secondary) |
| + .frame(maxWidth: .infinity) |
| + .padding(4) |
| + } |
| + } |
| + .frame(maxWidth: .infinity) |
| + // iMessage-style: the input row rides ONE translucent band, so the |
| + // feed shows through it as material blur instead of fully-legible |
| + // text (worst in dark mode). Only the row — the typeahead/banners |
| + // above stay transparent floaters. |
| + .background(.bar) |
| } |
| .onChange(of: draft) { _, text in |
| // Caret-at-end is the common case while typing; caret tracking for |