Skip to content

Re-measure timeline row when a collapsed event group expands#152

Merged
subpop merged 2 commits into
subpop:mainfrom
rexbron:fix-collapsed-events-row-height
Jun 26, 2026
Merged

Re-measure timeline row when a collapsed event group expands#152
subpop merged 2 commits into
subpop:mainfrom
rexbron:fix-collapsed-events-row-height

Conversation

@rexbron

@rexbron rexbron commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

Fixes #150 — expanding a collapsed list of call/system events would clip the content and push the timeline off screen.

Cause

The table-backed timeline renderer caches row heights keyed by (messageID, width). Expanding a collapsed group only flips the shared @Observable ExpandedGroupsState — it doesn't change the rows data, so updateRows never diffs that row, the height cache keeps the collapsed value, and heightOfRow returns it. NSTableView then clips the taller expanded content to the old height, and the un-accounted-for growth throws off scroll position.

Fix

  • ExpandedGroupsState.toggle now fires an onToggle(groupID) callback after mutating.
  • The table controller's new remeasureRow(forMessageID:) invalidates that row's cached height and calls noteHeightOfRows. heightOfRow's measurement host rebuilds the row reading the updated expansion state, so it returns the full expanded (or collapsed) height.
  • Scroll position is preserved across the height change, so expanding a row above the viewport no longer shifts the visible content off screen.
  • The representable wires expandedGroups.onToggle to the controller.

The LazyVStack renderer auto-sizes and was never affected — this is specific to the table renderer's height cache.

Testing

  • Expand/collapse a collapsed group of call/membership events → the row grows/shrinks to fit instead of clipping, and the timeline stays put.

Expanding a collapsed system-event group clipped its content at a fixed
height. The table renderer caches row heights keyed by (messageID,
width), and toggling expansion only flips the shared ExpandedGroupsState
— it doesn't change the `rows` data, so `updateRows` never invalidates
the cached height and `heightOfRow` keeps returning the collapsed value.
NSTableView then clips the taller expanded content to that height.

ExpandedGroupsState.toggle now fires an onToggle(groupID) callback; the
table controller's new remeasureRow(forMessageID:) invalidates that
row's cached height and notes its new height. heightOfRow's measurement
host rebuilds the row reading the updated expansion state, so it returns
the full expanded (or collapsed) height. Scroll position is preserved so
expanding a row above the viewport doesn't shift the visible content.

Fixes subpop#150.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@rexbron rexbron marked this pull request as ready for review June 26, 2026 16:57
Comment thread Relay/Views/Timeline/TimelineTableView.swift Outdated
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@subpop subpop merged commit b956fde into subpop:main Jun 26, 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.

Expanding collapsed list of call events scrolls off screen.

2 participants