Skip to content

perf: unmount idle Marketplace/Admin/Docs/ApiTest views#10

Merged
enowdev merged 1 commit into
enowdev:mainfrom
aguung:perf/unmount-idle-views
Jul 10, 2026
Merged

perf: unmount idle Marketplace/Admin/Docs/ApiTest views#10
enowdev merged 1 commit into
enowdev:mainfrom
aguung:perf/unmount-idle-views

Conversation

@aguung

@aguung aguung commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Classic layout (the default) kept every center view mounted permanently, toggling only a CSS hidden class — so a view's own background polling ran forever regardless of which tab was actually visible, and its state/DOM never got released across a long session.
  • Converts Marketplace, Admin, Docs, ApiTest to conditional mounting (mount only the active view), matching the pattern FocusShell already uses correctly for layoutMode: "focus".

Problem

  • web/src/os/Desktop.tsx's Classic-mode block rendered every center view unconditionally, hiding inactive ones via a hidden class rather than not rendering them.
  • Concretely: MarketplaceApp runs three separate setIntervals (including cloud-touching calls) and AdminApp runs a 15s poll — both continuously, even when the user is on the Widgets or Terminal tab and never looks at Marketplace/Admin again for the rest of the session.

Solution

  • Replace each of the four views' <div className={... ${view === "x" ? "" : "hidden"}}> wrapper with {view === "x" && (<div ...>...)} — React unmounts the view when its condition goes false, running its effect cleanup and releasing its state/DOM.
  • Widgets, CenterTerminal, AiChatApp are untouched and stay always-mounted: Widgets is the default/home view, and Terminal/AI Chat hold live session state that must survive switching away and back.
  • No behavior change to the presence/SSE heartbeat, layoutMode: "focus" (already correct), or any mobile-specific layout (none exists in this codebase today).
  • Safe by construction: every setInterval in the affected views already has a matching clearInterval in its effect cleanup, so React's unmount lifecycle stops them automatically — this required no changes to MarketplaceApp.tsx/AdminApp.tsx themselves.

Changed files

File Change
web/src/os/Desktop.tsx Four views converted from CSS-hidden to conditionally mounted.

No conflict with other open PRs

This only touches web/src/os/Desktop.tsx, which none of #6, #7, #8, or #9 touch — safe to merge in any order relative to them.

Test plan

  • tsc -b --noEmit and oxlint pass on the changed file
  • Manually traced: every setInterval in MarketplaceApp/AdminApp has a matching clearInterval in the same effect's cleanup, so unmounting reliably stops them; DocsApp/ApiTestApp have no timers to clean up
  • Visual confirmation in a running browser (tabs still render correctly; Network tab activity for Marketplace/Admin stops when switching away and resumes when switching back) — not done in this environment (no browser-automation tooling available); please check before merging

Classic layout kept every center view mounted via a CSS hidden class
instead of conditionally rendering, so each view's own polling (incl.
cloud-touching calls in Marketplace) ran forever in the background
regardless of which tab was visible, and their state/DOM never got
released, growing memory over a long session. Mount only the active
view for these four, matching the pattern FocusShell already uses
correctly. Widgets/Terminal/AI Chat stay always-mounted (home view /
live sessions that must survive switching away and back).
@enowdev enowdev merged commit 4a2b0c6 into enowdev:main Jul 10, 2026
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.

2 participants