mac,ios: ↑ edits your last message; deletes implode (#305)
* mac,ios: ↑ edits your last message; deletes implode Up-arrow in an empty composer starts editing your most recent message (Mac; iOS with a hardware keyboard) — the desktop convention. Deleted messages now implode — a quick shrink-and-fade where the bubble stood: rows carry a scale+opacity removal transition, and the delete fold is the one mutation that publishes inside an animation. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * mac,ios: preview-less chat rows centre their title beside the avatar A chat with no recent message dropped a ghost two-line preview block under its title, leaving the title floating at the top of the row. The preview block only renders when there's a preview now (the unread pip rides alone in that case), so the title sits vertically centred. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> ---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2 files changed,
+26
−13
ios/LChat/Views/ThreadRow.swift+17 −8ios/LChatMac/NativeChatView.swift+9 −5
diff --git a/ios/LChat/Views/ThreadRow.swift b/ios/LChat/Views/ThreadRow.swift| index b46b3b6..8db7f32 100644 |
| --- a/ios/LChat/Views/ThreadRow.swift |
| +++ b/ios/LChat/Views/ThreadRow.swift |
| @@ -29,14 +29,23 @@ struct ThreadRow: View { |
| } |
| } |
| } |
| - HStack(spacing: 6) { |
| - // Up to two lines of preview, height reserved so every row is the |
| - // same height (à la Mail/Messages) — mirrors the web list's 2-line clamp. |
| - Text(thread.preview.isEmpty ? " " : thread.preview) |
| - .font(.subheadline).foregroundStyle(.secondary) |
| - .lineLimit(2, reservesSpace: true) |
| - Spacer() |
| - if thread.unread { |
| + // No recent message → no ghost preview block, so the title |
| + // centres beside the avatar (the unread pip rides alone). |
| + if !thread.preview.isEmpty { |
| + HStack(spacing: 6) { |
| + // Up to two lines, height reserved so chatty rows stay |
| + // uniform (à la Mail/Messages) — the web's 2-line clamp. |
| + Text(thread.preview) |
| + .font(.subheadline).foregroundStyle(.secondary) |
| + .lineLimit(2, reservesSpace: true) |
| + Spacer() |
| + if thread.unread { |
| + Circle().fill(Color.accentColor).frame(width: 9, height: 9) |
| + } |
| + } |
| + } else if thread.unread { |
| + HStack { |
| + Spacer() |
| Circle().fill(Color.accentColor).frame(width: 9, height: 9) |
| } |
| } |
diff --git a/ios/LChatMac/NativeChatView.swift b/ios/LChatMac/NativeChatView.swift| index cf3b9d3..ac3e27c 100644 |
| --- a/ios/LChatMac/NativeChatView.swift |
| +++ b/ios/LChatMac/NativeChatView.swift |
| @@ -790,11 +790,15 @@ struct NativeChatView: View { |
| .foregroundStyle(.tertiary) |
| } |
| } |
| - Text(t.preview) |
| - .font(.system(size: 12, weight: t.unread ? .medium : .regular)) |
| - .foregroundStyle(t.unread ? .primary : .secondary) |
| - .lineLimit(2, reservesSpace: true) |
| - .frame(maxWidth: .infinity, alignment: .leading) |
| + // No recent message → no ghost preview block, so the |
| + // title centres beside the avatar instead of floating top. |
| + if !t.preview.isEmpty { |
| + Text(t.preview) |
| + .font(.system(size: 12, weight: t.unread ? .medium : .regular)) |
| + .foregroundStyle(t.unread ? .primary : .secondary) |
| + .lineLimit(2, reservesSpace: true) |
| + .frame(maxWidth: .infinity, alignment: .leading) |
| + } |
| } |
| // Outside the text column so the dot centres on the ROW. |
| if t.unread { |