Skip to content

fix(ui): prevent LazyVStack layout hang with 700+ cards#70

Open
sergioestebance wants to merge 1 commit into
mainfrom
fix/list-board-layout-hang
Open

fix(ui): prevent LazyVStack layout hang with 700+ cards#70
sergioestebance wants to merge 1 commit into
mainfrom
fix/list-board-layout-hang

Conversation

@sergioestebance
Copy link
Copy Markdown
Contributor

Summary

  • Fix 100% CPU hang caused by LazyVStack(pinnedViews: [.sectionHeaders]) in the sidebar ListBoardView. The outer container needed to measure each section to position pinned headers, which forced materializing all ~700 card views on every reconciliation cycle (3s), permanently blocking the main thread.
  • Remove inner LazyVStack in section bodies, replacing with VStack so the outer lazy container can size sections via direct children.
  • Move ChannelShareController drain loops from Task.detached to DispatchQueue.global to avoid blocking the cooperative thread pool with synchronous FileHandle.availableData reads.

Evidence

Thread samples (~/.kanban-code/logs/main-thread-samples/) showed 100% of samples in LazySubviewPlacements.placeSubviewsForEachList.applyNodes with deeply nested per-item modifier chains. Watchdog log confirmed continuous HANG: main thread blocked for >500ms every 10 seconds for 2+ hours.

Test plan

  • swift test — all 755 tests pass
  • Launch app with 700+ cards, verify sidebar list scrolls smoothly
  • Create a new card and launch it — confirm no hang
  • Verify section headers still render correctly (they scroll with content now instead of pinning)

The sidebar ListBoardView used LazyVStack with pinnedViews:
[.sectionHeaders], but each section wrapped its cards in an inner
LazyVStack. The outer container needed to measure each section to
position pinned headers, which required materializing the inner
LazyVStack — forcing all ~700 card views to be laid out on every
reconciliation cycle. Thread samples confirmed 100% main thread time
spent in LazySubviewPlacements.placeSubviews → ForEachList.applyNodes.

Two changes:
- Remove pinnedViews from the outer LazyVStack so sections are lazily
  evaluated without full measurement
- Replace the inner LazyVStack with VStack so the outer container can
  size sections via their direct children

Also move ChannelShareController drain loops from Task.detached to
DispatchQueue.global to avoid blocking the cooperative thread pool
with synchronous FileHandle.availableData reads.
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.

1 participant