Skip to content

perf(desktop): keep the shell mounted under settings, cheaper mounts - #91362

Merged
trunk-io[bot] merged 13 commits into
perf/desktop-render-churnfrom
perf/desktop-shell-remount
Aug 30, 2026
Merged

perf(desktop): keep the shell mounted under settings, cheaper mounts#91362
trunk-io[bot] merged 13 commits into
perf/desktop-render-churnfrom
perf/desktop-shell-remount

Conversation

@charlesvien

@charlesvien charlesvien commented Aug 30, 2026

Copy link
Copy Markdown
Member

Problem

Stacked on #91360. Still slow after it:

  • "Back to app" rebuilt the whole shell because /settings sat outside the _shell layout. Packaged build: 516 to 607ms frozen per click.
  • Switching settings categories re-rendered the entire sidebar behind the overlay: rows subscribed to the raw pathname and mounted their handoff and rename dialogs while closed.
  • Opening a session paid for things nobody sees yet: a Tooltip and Button per footer action, a QueryObserver per workspaces hook, a mermaid re-render per diagram, two forced layouts.

Changes

  • Settings is a full-window overlay (portal) over a mounted, inert shell. Leaving it costs one tab switch. Cmd+W inside Settings now closes the Settings tab; before it did nothing.
  • Settings keeps the connectivity and announcement banners. They render inside the overlay, so going offline there still shows the offline state and its Retry.
  • Opening Settings no longer overwrites the tab's saved Spaces destination. The tab strip is mounted on that route now, so it skips it rather than recording it.
  • A turn's copy button and feedback thumbs stay reachable from the keyboard. Tabbing into a row reveals its footer.
  • The thumbs mount on row hover or focus, or when the turn is rated. They were invisible until hover anyway.
  • Sidebar rows select "is this row active" from router state instead of the pathname; sidebar, rail, tab strip and right panel are memoized; per-row dialogs mount on first open.
  • One shared QueryObserver for the workspaces map (55 call sites).
  • Mermaid diagrams cached by code and theme.
  • Overflow measurement and tab scroll-into-view read layout after paint (mechanical).

Not changed: the composer's editable flip while connecting; removing it would let people type before the agent connects.

Dev build, same state before and after (6 tabs open). The packaged sidebar is 4x larger and was not re-measured.

Before After
Three Settings round trips, blocking (worst frame) 2,840ms (695ms) 2,651ms (569ms)
Nine settings category switches, blocking (worst frame) 3,106ms (318ms) 439ms (102ms)
Reopening a session with a mermaid diagram, worst frame 866ms (1,418ms packaged) 326ms
Feedback thumbs mounted on a 28-row thread before hover every row 0
Workspaces hooks in the "Back to app" profile 130ms absent

How did you test this code?

  • CDP traces and long-animation-frame observers before and after.
  • Checked live: overlay covers the viewport, sidebar stays mounted and inert under it, portals get z-index 101, "Back to app" restores the view, footer buttons reveal on hover, mermaid reopens with no loading state.
  • useAppView.test.ts locks the route-to-view mapping. It catches a settings case written in route-id form, which the generated tree never produces as a fullPath and which silently made Settings report itself as the new-task view.
  • Router mocks in ChannelsList.test.tsx, ChannelsSidebar.test.tsx, ShellLayout.test.tsx and useReviewInRightPanel.test.ts now honor select.
  • Not checked in a running build: the packaged build, a Radix dialog opened from Settings, the settings banners, and the footer's keyboard path.

@charlesvien charlesvien self-assigned this Aug 30, 2026
@github-actions github-actions Bot added the feature/desktop Feature Tag: Desktop label Aug 30, 2026
@github-actions

github-actions Bot commented Aug 30, 2026

Copy link
Copy Markdown
Contributor

React Doctor found 2 issues in 2 files · 2 warnings.

2 warnings

packages/ui/src/features/browser-tabs/BrowserTabStrip.tsx

packages/ui/src/features/sessions/components/chat-thread/ChatThread.tsx

Reviewed by React Doctor for commit 69637b6.

@charlesvien
charlesvien marked this pull request as ready for review August 30, 2026 10:32
@github-actions

Copy link
Copy Markdown
Contributor

🤖 CI report

Trunk lane — non-backend lane

This PR is assigned to the non-backend lane. It does not run backend Python tests and may merge in parallel with PRs in other lanes.

@pr-assigner-resolver-posthog
pr-assigner-resolver-posthog Bot requested a review from a team August 30, 2026 10:32
@greptile-apps

greptile-apps Bot commented Aug 30, 2026

Copy link
Copy Markdown
Contributor
Prompt To Fix All With AI
### Issue 1
products/desktop/packages/ui/src/router/routes/_shell/settings/$category.tsx:11
**Settings route state stays false**

When a tour is active and the user opens settings, the new `/_shell/settings/$category` route ID does not satisfy `useIsSettingsOpen()`'s unchanged `startsWith("/settings")` check. `TourOverlay` therefore remains active over the full-window settings overlay, obscuring settings controls or advancing the tour against hidden shell content.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Reviews (1): Last reviewed commit: "Cut mount work in chat thread and tab pa..." | Re-trigger Greptile

Settings routes moved under the pathless `_shell` layout, so their route IDs
now read `/_shell/settings/…`. `useIsSettingsOpen` still matched on
`startsWith("/settings")` and went permanently false, which left the tour
spotlight drawn over the settings overlay.

Share one `isSettingsRouteId` predicate across the three call sites instead of
carrying a copy of the substring in each.

Generated-By: PostHog Desktop
Task-Id: d3e0d459-5a9e-4627-866b-3a6fac9677b5
Generated-By: PostHog Desktop
Task-Id: d3e0d459-5a9e-4627-866b-3a6fac9677b5
@charlesvien charlesvien changed the title perf(desktop): keep shell mounted under settings, trim mount work perf(desktop): keep the shell mounted under settings, cheaper mounts Aug 30, 2026
@charlesvien charlesvien added reviewhog ($$$) Reviews pull requests before humans do stamphog Request AI approval (no full review) labels Aug 30, 2026
@posthog

posthog Bot commented Aug 30, 2026

Copy link
Copy Markdown
Contributor

🦔 PostHog Review reviewed this pull request

Found 1 must fix, 6 should fix, 0 consider.

Published 7 findings (view the review).

Resolved comments: 4 fixed, 3 left for you

@greptile-apps

greptile-apps Bot commented Aug 30, 2026

Copy link
Copy Markdown
Contributor

Reviews (2): Last reviewed commit: "Cut sidebar work on settings navigation" | Re-trigger Greptile

stamphog[bot]
stamphog Bot previously approved these changes Aug 30, 2026

@stamphog stamphog Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Desktop-app-only performance refactor (mounting/memoization, no auth/billing/data-model/CI/dependency surface); the one substantive bug a bot reviewer flagged (settings route-ID matching breaking tour-overlay state) is already fixed consistently across the diff, and the author is a strong-familiarity member of the owning team.

  • Author wrote 82% of the modified lines and has 15 merged PRs in these paths (familiarity STRONG).
  • 👍 on the PR from greptile-apps[bot].
Gate mechanics and policy version
Gate Result
prerequisites all clear
deny-list no deny categories matched
size 450L, 20F substantive, 557L/24F incl. docs/generated/snapshots — within ceiling
tier T1-agent / T1d-complex (557L, 24F, single-area, perf)
stamphog 2.0.0b4 .stamphog/policy.yml @ 232ba24 · reviewed head 232ba24

@posthog

posthog Bot commented Aug 30, 2026

Copy link
Copy Markdown
Contributor

PostHog Review alpha 🦔 If you find any issues helpful - please reply "valid", "invalid", etc., for evaluation purposes 🙏

@posthog posthog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PostHog Review

Found 1 must fix, 6 should fix.

Comment thread products/desktop/packages/ui/src/features/workspace/useWorkspace.ts
Comment thread products/desktop/packages/ui/src/features/canvas/components/ShellLayout.tsx Outdated
Comment thread products/desktop/packages/ui/src/router/routes/_shell/settings/$category.tsx Outdated
Comment thread products/desktop/packages/ui/src/router/useAppView.ts Outdated
Comment thread products/desktop/packages/ui/src/router/routes/__root.tsx
Comment thread products/desktop/packages/ui/src/router/routes/__root.tsx
@posthog posthog Bot removed the reviewhog ($$$) Reviews pull requests before humans do label Aug 30, 2026
The bare QueryObserver for the workspaces map never tracks accessed
fields the way useQuery does, so it notified every subscriber on any
result change (fetchStatus flips, dataUpdatedAt bumps on refetch). All
hooks in this file read only `data` and `isFetched`, so set
notifyOnChangeProps to those two fields.

Generated-By: PostHog Desktop
Task-Id: 55c57757-4318-4446-8726-aa6d926d6a61
@stamphog
stamphog Bot dismissed their stale review August 30, 2026 12:07

A new stamphog review started for this PR — the fresh verdict replaces this approval.

@stamphog

stamphog Bot commented Aug 30, 2026

Copy link
Copy Markdown

A bot reviewer flagged a confirmed must-fix regression (the settings view type becomes unreachable after the route restructuring) plus six should-fix regressions (global banners disappearing, saved navigation state getting overwritten, a modal-dialog/settings-overlay conflict, and lost keyboard accessibility on turn actions) - all 7 threads are still unresolved per the review's own tracker.

  • Author wrote 82% of the modified lines and has 15 merged PRs in these paths (familiarity STRONG).
  • 👍 on the PR from greptile-apps[bot].
  • posthog[bot] must-fix on useAppView.ts: settings routes no longer match any case in deriveFromMatches because fullPath excludes the pathless _shell segment, making the settings app view type unreachable - unresolved.
  • posthog[bot] should-fix on __root.tsx: deleting the dedicated settings render branch drops ConnectivityBanner and AnnouncementBanner while Settings is open - unresolved.
  • posthog[bot] should-fix on __root.tsx: BrowserTabStrip's reconciliation effect now runs during Settings navigation and unconditionally overwrites lastByPane['spaces'], corrupting the saved Back to app destination - unresolved.
Gate mechanics and policy version
Gate Result
prerequisites all clear
deny-list no deny categories matched
size 455L, 20F substantive, 562L/24F incl. docs/generated/snapshots — within ceiling
tier T1-agent / T1d-complex (562L, 24F, single-area, perf)
stamphog 2.0.0b4 .stamphog/policy.yml @ 7b6e1c1 · reviewed head 7b6e1c1

@stamphog stamphog Bot removed the stamphog Request AI approval (no full review) label Aug 30, 2026
ShellLayout and useActiveSession read the whole route param set via
useParams({ strict: false }). The nearest match carries the full param
chain, so switching a settings category changed `category` and
re-rendered the shell layout and every useActiveSession consumer (the
right panel included), even though none read `category`.

Select each param on its own instead. The router uses strict reference
equality on the selector result (no default structural sharing), so
selecting single primitives skips the re-render when only an unrelated
param changes; an object selector would return a new reference each time
and defeat that.

Update the useParams mocks in the two affected tests to honor `select`,
matching the real hook and the mocks already updated elsewhere in this
change.

Generated-By: PostHog Desktop
Task-Id: 55c57757-4318-4446-8726-aa6d926d6a61
posthog Bot and others added 4 commits August 30, 2026 12:23
deriveFromMatches switches on a match's fullPath, but the settings cases
used the route-id form (/_shell/settings/$category, /_shell/settings/).
The pathless _shell layout lives only in a route's id, never its
fullPath — the generated tree assigns these routes fullPath
/settings/$category and /settings/ — so those cases never matched.
Settings fell through to the task-input view, making the `settings`
AppView unreachable and mislabeling the settings route for every
useAppView / getAppViewSnapshot consumer (SpaceSwitcher's isOnNewTask,
the New-task sidebar highlight, and the task-input prefill merge).

Use the fullPath form, matching the neighboring /spaces/context case.
Add a regression test for the previously uncovered mapping.

Generated-By: PostHog Desktop
Task-Id: 55c57757-4318-4446-8726-aa6d926d6a61
The tab strip now stays mounted under the Settings overlay, so its
settled-navigation effect runs on settings navigations. Settings is an
unclaimed route, which railPaneForPath classifies as the spaces pane, so
the effect wrote the settings href into the active tab's
viewState.lastByPane.spaces. After leaving Settings, a Spaces rail click
restored that href and reopened Settings, losing the tab's real last
spaces location.

Preserve the existing lastByPane map when the route is settings. The
guard reads routeAppView === "settings", which now resolves correctly for
the settings route. This restores the pre-mount behavior, where the strip
never ran on settings at all.

Generated-By: PostHog Desktop
Task-Id: 55c57757-4318-4446-8726-aa6d926d6a61
Two things the shell staying mounted under settings took away.

The connectivity and announcement banners now sit under the settings overlay
and inside the inert subtree, so losing connectivity in settings hid both the
offline state and its Retry. They render inside the overlay while it is up, and
the shell drops its copies for that span so an announcement is not reported as
seen twice.

The turn footer mounted its copy and rating buttons on pointer hover only, and
keyboard message navigation never marks an agent turn, so those controls left
the DOM for anyone not using a pointer. The copy button stays mounted, a row
reveals its footer on focus as well as hover, and the thumbs mount from there.

Also reformats a ShellLayout line the formatter was failing on.

Generated-By: PostHog Desktop
Task-Id: fec4ff4f-19c1-4dfe-a58a-567e777c7365
Generated-By: PostHog Desktop
Task-Id: fec4ff4f-19c1-4dfe-a58a-567e777c7365
@trunk-io

trunk-io Bot commented Aug 30, 2026

Copy link
Copy Markdown

😎 Stack merged successfully - details.

@trunk-io
trunk-io Bot merged commit 9e8c383 into master Aug 30, 2026
192 checks passed
@trunk-io
trunk-io Bot deleted the perf/desktop-shell-remount branch August 30, 2026 15:38
@deployment-status-posthog

deployment-status-posthog Bot commented Aug 30, 2026

Copy link
Copy Markdown

Deploy status

Environment Status Deployed At Workflow
dev ✅ Deployed 2026-08-30 15:58 UTC Run
prod-us ✅ Deployed 2026-08-30 16:11 UTC Run
prod-eu ✅ Deployed 2026-08-30 16:12 UTC Run

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature/desktop Feature Tag: Desktop

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants