Skip to content

Anchor the reaction picker above the message bubble#153

Merged
subpop merged 2 commits into
subpop:mainfrom
rexbron:fix-reaction-picker-position
Jun 28, 2026
Merged

Anchor the reaction picker above the message bubble#153
subpop merged 2 commits into
subpop:mainfrom
rexbron:fix-reaction-picker-position

Conversation

@rexbron

@rexbron rexbron commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

Fixes #147 — the reaction picker rendered fixed near the center, off in a corner, or overlapping/under the compose bar instead of attached to its message.

Cause

The default timeline renderer is NSTableView-backed, with each row in its own NSHostingView. The picker was positioned from a bubble frame captured in the SwiftUI "timeline" coordinate space, but a named coordinate space can't resolve across that per-cell hosting boundary — so the frame was meaningless and the picker landed wherever.

Fix

  • Capture the bubble frame in global (window) coordinates. This is consistent across both the table and LazyVStack renderers and survives the NSHostingView boundary. ReactionPickerOverlay converts it into its own local space by subtracting its global origin.
  • Deterministic vertical anchor. The capsule's bottom edge is pinned a small gap above the bubble's top using padding within the overlay bounds — no capsule-size measurement, which previously lagged a frame and overlapped the message. The capsule's trailing edge aligns to the bubble's trailing edge.
  • Single source of the bubble frame. MessageView streams its precise bubble frame so the picker tracks the message across layout changes (e.g. window resize), and reports the same frame up to the row for the context-menu present. This removes a second, whole-row geometry stream that previously fought it and slid the picker onto the avatar gutter when the window narrowed.

Testing

  • Open the picker (smiley button, long-press, or "Add Reaction") on incoming and outgoing messages, one-line and multi-line → it sits just above the bubble, trailing-aligned, clear of the compose bar.
  • Resize the window (width and height) with the picker open → it stays anchored to the bubble.

The reaction picker rendered detached from its message — centered, off in
a corner, or overlapping the bubble — in the default NSTableView timeline
renderer. The bubble frame was captured in the SwiftUI "timeline"
coordinate space, which can't resolve inside the table's per-row
NSHostingView cells, so the frame was meaningless across that boundary.

Capture the bubble frame in global (window) coordinates, which is
consistent across both the table and LazyVStack renderers and survives the
NSHostingView boundary. ReactionPickerOverlay converts it to its own local
space by subtracting its global origin, then pins the capsule's bottom
edge a small gap above the bubble's top via deterministic padding (no
capsule-size measurement to lag a frame). The capsule's trailing edge
aligns to the bubble's trailing edge.

MessageView is the single source of the bubble frame: it streams updates
so the picker tracks the bubble across layout changes (e.g. window resize)
and reports the same frame up to the row for the context-menu present —
avoiding a second, whole-row stream that previously fought it and slid the
picker onto the avatar gutter during width changes.

Fixes subpop#147.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@rexbron rexbron marked this pull request as ready for review June 27, 2026 13:27
For short incoming messages, the reaction picker capsule was anchored
to the bubble's trailing edge, causing it to extend leftward behind
the room list sidebar. Now incoming messages anchor the capsule to the
bubble's leading edge so it extends rightward into the visible timeline
area. Outgoing messages retain trailing-edge alignment.

Assisted-By: OpenCode (claude-opus-4-6)
@subpop subpop merged commit aa2848c into subpop:main Jun 28, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Reaction pop up sometime renders under compose bar and far away from the message.

2 participants