Repository navigation
CmuxTabsKit: build only nearby grid cards; dormant hidden grid - #26818
azooz2003-bit wants to merge 6 commits into
Conversation
…en grid dormant The tab grid built and laid out every card (full-page content under a GeometryReader and scale, a shadow and a UIKit interaction view) in a plain ZStack, on screen or not, and it stays mounted at opacity 0 while a page shows, so every change on the page re-ran it. - GridBand: the grid builds the cards within the visible rect plus a row of margin, snapped to whole rows (the band changes once per row scrolled, not per frame). Cards a gesture or the zoom holds (swiped, lifted for the menu, dragged, the zooming tab) stay built. Absolute layout, swipe to close, reorder, menus, Select Tabs and the zoom are unchanged. - DormantGate: while the grid is hidden its parent's updates are held back (equatable gate); it wakes with current values when it starts to show. - The active tab's card content is not built while its page is shown or zooming (the page draws it); a streaming chat's card re-rendered every token under the page. Public API unchanged. Tests: GridBandTests (bounded band for 80 tabs, one change per row, next row prebuilt). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit 40e42e7)
…nged The first cull rebuilt the grid's body each time its band moved a row, so every built card re-ran and new cards were built in scroll frames (rig: 30 tabs scrolled worse than with every card built). Residency replaces it: - CardResidency: one observed slot per card, so building or releasing a card re-runs that card only; the scroll offset comes in from the scroll geometry callback and never re-runs the grid's body. Cards on screen are built at once while the grid shows (a zoom or a fling never shows a blank card); cards within 3 screens are built ahead one per frame while the grid rests; cards past 4 screens are released. A hidden grid builds only its on-screen cards. ResidentCard makes its card in its own body, so an unbuilt card costs the grid's body nothing (a built value per card was made for all 80 cards on every zoom frame). - TabsDataSource.overviewIdleChanged(_:) (additive, default no-op): the grid is settled, not scrolling and has nothing left to build, the time for a source to draw costly cards. README documents it and residency. - TabsScreen's overview is its own property (the body no longer type-checks in time with it inline). Tests: CardResidencyPlanTests (80 tabs keep a bounded set with every on-screen card built, scrolling back keeps built cards, on-screen first then nearest); swift test on mini-cmuxs-mac-mini-3: 62 tests passed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit b81d289)
…ames overviewIdleChanged used the scroll phase, which does not report an offset set from code (the list rig's scroll, a programmatic reveal), so a source drew costly cards in scroll frames (rig, 30 tabs with a streaming chat: 135-214 ms/s of hitches, main CPU 750-920 ms/s). CardResidency now counts frames without an offset change (FrameClock, 6 frames) and reports busy while moving or building ahead; the grid is idle only when settled, not scrolling and not busy. Rig after: 0 ms/s hitches, CPU 160 ms/s. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit e518f67)
…rst frame Review findings: TabOverview set CardResidency.onBusyChanged to a closure that captured the view (and through it the source), held by the residency it owned; and the callback was set after the first update, so a grid shown from its first frame never reported idle. CardResidency is now @observable with an observed busy flag that the grid reads in its idle expression (onChange, initial: true); the callback is gone. Tests: swift test on mini-cmuxs-mac-mini-3, 62 passed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit 134307d)
…grid The idle state (settled, visible, not scrolling, not moving, nothing to build) was an observed property the grid's body read, so a scroll that started or stopped and each build-ahead step re-ran the grid's body. It is now computed in CardResidency (plain class) and sent to the source through a sink that holds it weakly; a sink installed after the first update hears the current state. The scroll phase goes to the residency directly. Tests: swift test on mini-cmuxs-mac-mini-3, 62 passed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit 81df30c)
Review round 3 (low): cards outside the residency band were absent, so a
VoiceOver swipe through the grid stopped at the last built card. An unbuilt
card is now a bare accessibility element in its slot (no pixels, no
touches), labelled as its built card ("<title>, tab" or "<title>,
Pinned") with Open as its activation; scrolling to it builds the card.
Not covered by a unit test (a view's accessibility tree); swift test on the
kit still passes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit c80182f)
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the
|
|
Found 14 test failures on Blacksmith runners: Failures
...and 4 more test failures. View all on Blacksmith: run 40664679414, run 40664679197
|
|
cmux status (one comment, edited in place; each line is updated by the job that owns it) Probably yours: CI failed on
Matched log linesNot re-run automatically: Written by
Agents: |
![Fix with [code]smith](https://pr-comments-assets.blacksmith.sh/codesmith/fix-with-codesmith-light-3x.png)
Summary
CmuxTabsKit's tab grid builds only the cards near the screen and does no body work while it is hidden. These are the kit-only commits of the cmux-next iOS lists rebuild (manaflow-ai/cmux PR against
feat-cmux-nextfromfeat-cmux-next-ios-smooth-lists), proposed here soPackages/iOS/CmuxTabsKitstays identical on both branches.CardResidency,CardResidencyPlan): cards on screen are built at once, cards within 3 screens are built one per frame while the grid rests, cards past 4 screens are released. Scrolling never re-runs the grid's body.DormantGate): while a page shows, the grid stays mounted but holds its updates, and the active card's content is not built under its page.overviewIdleChanged(_:): new source hook with a default no-op (additive; nothing public removed or changed). README updated.Measurements (60 Hz iPhone 17 Pro simulator, iOS 26.5, list rig on aws-m4pro-6, before = feat-cmux-next at 4e28d9c, after = the app branch head, 2 runs each)
The after numbers include the app-side host changes (stored tabs, per-tab observation, agent cards as pictures), which stay in the app branch.
Testing
swift testinPackages/iOS/CmuxTabsKiton aws-m4pro-5: 62 tests in 13 suites passed (newCardResidencyPlanTests).CmuxTabsKitDemobuilds for the iOS simulator (aws-m4pro-6, Xcode 26.6).WorkspaceTabsSourceListTests,WorkspaceTabsSourceUpdateTestson an iOS simulator, 16 passed.Observed / Not run
Changelog
none
🤖 Generated with Claude Code
Need help on this PR? Tag
@codesmith-botwith what you need.Summary by cubic
The tab grid now builds only cards near the screen and stays dormant while hidden, so scrolling and page updates never re-run the grid's body.
DormantGate), and the active card's content is not built under its page.overviewIdleChanged(_:)toTabsDataSourceas an additive default no-op; nothing public was removed or changed.Written for commit 19ec7fc. Summary will update on new commits.