fix(app): calm loading state for Home sessions by kitlangton · Pull Request #57679 · anomalyco/opencode · GitHub
Skip to content

fix(app): calm loading state for Home sessions - #57679

Merged
kitlangton merged 2 commits into
betafrom
home-loading
Oct 21, 2026
Merged

kitlangton merged 2 commits into
betafrom
home-loading

Conversation

@kitlangton

@kitlangton kitlangton commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

Home showed a "Loading" heading over four dark slabs while its session list loaded, sometimes for several seconds.

Why it was slow

Home's list is one request per server: GET /api/session?limit=5000&order=desc&parentID=null. On a real database with about 1,900 sessions the server answers it in 2–30 ms. The wait happened in the client:

  • The list was dropped 5 minutes after leaving Home. The query used TanStack's default gcTime, so going back to Home after working in a session for a while started again from an empty list and showed the loading state.
  • The refetch waited at the back of the request queue. The app sends at most 4 API requests at a time, first come first served. The session you just left queues its location reads (provider, model, config, agent, permission, form and others), and each one takes seconds while the server loads that location, so the list request waited behind all of them. The desktop log around the capture shows server thrashing detected (a request waiting more than 2 s for a slot) every 10 s.
  • Switching projects never fetches. Home filters the list it already has, so rows change on the next frame before and after this PR.

Changes

  • gcTime: Infinity on the Home session query (the index is already bounded per directory). Going back to Home shows the last list right away and refreshes it in the background.
  • In the request queue, GET /api/session takes the next free slot ahead of queued requests. Other requests keep their order, and the 4-slot limit is unchanged.
  • New loading state:
    • no "Loading" text in place of the heading
    • nothing for the first 250 ms, so fast loads paint the list straight away
    • after that, six placeholder rows with the real row's height, padding, avatar and title positions. Title widths vary, and a short meta bar appears only when rows would show a project name. They use bg-layer-02 on the base surface, with a slow highlight that sweeps in reading direction (reversed in RTL) and is turned off under reduced motion.
    • the rows fade out in 150 ms while the list fades in over 200 ms (opacity only). The list renders in the same place, so its first row lands where the first placeholder row was.

Measurements

Mock server, production build, headless Chromium. Times are measured in the page.

Case Before After
First open, list answers in 1.5 s "Loading" slabs at 180 ms, then rows at 1.7 s blank for 250 ms, then placeholder rows, then the crossfade to rows at 1.7 s
First open, list answers in 100 ms "Loading" slabs flash before the rows rows paint directly
Back to Home after 6 min, list answers in 1.5 s Loading for 1.45 s rows at 227 ms (from cache)
Back to Home after 6 min, other reads take 800 ms, 8 tabs list request sent 4.1 s after the click; still loading at 2.5 s rows at 212 ms; the refresh is sent first
Home from a session after restart, other reads take 800 ms (no cache) 5.2 s from click to rows 0.8 s
Switch project next frame, no request next frame, no request

Proof

Before is on the left and After on the right. Each clip uses a delayed session-list response in the mock app.

First open, list answers in 1.5 s

open-1500.mp4

First open, list answers in 100 ms

open-100.mp4

Back to Home after 6 minutes, list answers in 1.5 s

return-1500.mp4

Home from a session after restart, server busy (other reads take 800 ms)

start-busy.mp4

Switching projects. The list is already loaded, so there is nothing to wait for and no dimmed previous list to keep.

switch.mp4

Crossfade, frame by frame (animations paused when the list arrives, then stepped 8 ms at a time; about 8× slower)

crossfade-8x-slow.mp4
crossfade-strip

The headless screencast captures about 5 frames per second, so the real-time clips step. The frame-by-frame clip shows the actual transition.

bun run lint:changed origin/beta still reports 5 findings, all on lines this PR does not touch: request-queue.ts:43, request-queue.test.ts:12,157,175 and view.tsx:46.

@kitlangton
kitlangton merged commit 5798a40 into beta Oct 21, 2026
9 of 15 checks passed
@kitlangton
kitlangton deleted the home-loading branch October 21, 2026 16:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant