perf(chrome): preload panel chunks on hover/focus#9650
Open
mscolnick wants to merge 1 commit into
Open
Conversation
Wrap all sidebar/dev-panel lazy imports with reactLazyWithPreload and prefetch the corresponding chunk when the user hovers or focuses the icon/tab. On mount, idle-preload the persisted selected panel(s) if their parent was last left open. Adds a theme-matched skeleton for the terminal panel so first open isn't a blank flash.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Contributor
There was a problem hiding this comment.
No issues found across 3 files
Architecture diagram
sequenceDiagram
participant User
participant Sidebar as Sidebar Item
participant AppChrome as AppChrome
participant LazyPanels as PANEL_PRELOADERS
participant Chunk as Panel Chunk (JS)
participant TerminalSkeleton as TerminalSkeleton
participant IdleCallback as requestIdleCallback
Note over User,IdleCallback: NEW: Proactive chunk loading on hover/focus and idle
alt User hovers or focuses sidebar icon
User->>Sidebar: onMouseEnter / onFocus
Sidebar->>LazyPanels: PANEL_PRELOADERS[panelType]()
LazyPanels->>LazyPanels: Call matching .preload()
LazyPanels->>Chunk: Fetch panel chunk
Note over Chunk: Chunk downloaded but not rendered yet
Chunk-->>LazyPanels: Cached in browser
end
alt User clicks panel icon (after preload)
User->>Sidebar: onClick
Sidebar->>AppChrome: Select panel
AppChrome->>LazyPanels: LazyPanel.Component (renders)
Note over AppChrome,Chunk: Chunk already loaded — instant render
LazyPanels->>Chunk: Mount component from cached chunk
Chunk-->>AppChrome: Panel rendered
end
opt On mount — idle preload of previously open panels
AppChrome->>IdleCallback: useEffect(): requestIdleCallback / setTimeout
alt requestIdleCallback available
IdleCallback->>IdleCallback: Schedule with timeout 2000ms
else fallback
IdleCallback->>IdleCallback: setTimeout(300ms)
end
IdleCallback->>LazyPanels: Preload selectedPanel (if sidebar open)
IdleCallback->>LazyPanels: Preload selectedDeveloperPanelTab (if dev panel open)
LazyPanels->>Chunk: Fetch chunk(s) for persisted panels
Chunk-->>LazyPanels: Cached
end
opt Terminal panel first open
User->>AppChrome: Select terminal panel
AppChrome->>TerminalSkeleton: Suspense fallback
Note over TerminalSkeleton: Renders themed placeholder (dark/light)
AppChrome->>LazyPanels: LazyTerminal.Component
alt Chunk still loading
TerminalSkeleton-->>User: Show skeleton animation
LazyPanels->>Chunk: Fetch (if not preloaded)
Chunk-->>LazyPanels: Chunk ready
LazyPanels->>AppChrome: Render actual terminal
TerminalSkeleton-->>AppChrome: Unmount skeleton
else Chunk already preloaded
LazyPanels->>AppChrome: Render terminal immediately
Note over TerminalSkeleton: Skeleton never shown
end
end
Contributor
There was a problem hiding this comment.
Pull request overview
This PR improves perceived performance of the editor chrome by centralizing sidebar/developer-panel lazy imports behind a preloading wrapper, prefetching the relevant chunks on user intent (hover/focus) and on initial idle time for previously-open panels, and adding a themed terminal loading skeleton to avoid a blank flash.
Changes:
- Introduces
reactLazyWithPreload-backed lazy panel registry plus aPANEL_PRELOADERSmap for warming chunks. - Hooks preloading into sidebar icons and developer-panel tabs, and adds an on-mount idle preload for persisted open panel(s).
- Adds a terminal-specific Suspense fallback skeleton to smooth the first terminal open.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
| frontend/src/components/editor/chrome/wrapper/sidebar.tsx | Adds intent-based preloading hooks to sidebar items via PANEL_PRELOADERS. |
| frontend/src/components/editor/chrome/wrapper/lazy-panels.ts | New centralized lazy-panel registry using reactLazyWithPreload and preloader map. |
| frontend/src/components/editor/chrome/wrapper/app-chrome.tsx | Switches chrome panels to new lazy registry, adds idle-preload-on-mount, and terminal skeleton fallbacks. |
Comment on lines
+234
to
+238
| <div | ||
| className={itemClassName} | ||
| onMouseEnter={onPreloadHint} | ||
| onFocus={onPreloadHint} | ||
| > |
| : "hover:bg-muted/50", | ||
| )} | ||
| onMouseEnter={PANEL_PRELOADERS[panel.type]} | ||
| onFocus={PANEL_PRELOADERS[panel.type]} |
Comment on lines
+59
to
+80
| // Preloader registry: hovering an icon/tab calls into this map to warm the | ||
| // corresponding chunk. Two panel types (chat and agents) share the "ai" slot, | ||
| // so we preload both. | ||
| export const PANEL_PRELOADERS: Record<PanelType, () => void> = { | ||
| files: LazyFileExplorerPanel.preload, | ||
| variables: LazySessionPanel.preload, | ||
| dependencies: LazyDependencyGraphPanel.preload, | ||
| packages: LazyPackagesPanel.preload, | ||
| outline: LazyOutlinePanel.preload, | ||
| documentation: LazyDocumentationPanel.preload, | ||
| snippets: LazySnippetsPanel.preload, | ||
| ai: () => { | ||
| LazyChatPanel.preload(); | ||
| LazyAgentPanel.preload(); | ||
| }, | ||
| errors: LazyErrorsPanel.preload, | ||
| scratchpad: LazyScratchpadPanel.preload, | ||
| tracing: LazyTracingPanel.preload, | ||
| secrets: LazySecretsPanel.preload, | ||
| logs: LazyLogsPanel.preload, | ||
| terminal: LazyTerminal.preload, | ||
| cache: LazyCachePanel.preload, |
Comment on lines
+115
to
+127
| const schedule = | ||
| typeof window !== "undefined" && | ||
| typeof window.requestIdleCallback === "function" | ||
| ? (cb: () => void) => window.requestIdleCallback(cb, { timeout: 2000 }) | ||
| : (cb: () => void) => setTimeout(cb, 300); | ||
| schedule(() => { | ||
| if (isSidebarOpen && selectedPanel) { | ||
| PANEL_PRELOADERS[selectedPanel]?.(); | ||
| } | ||
| if (isDeveloperPanelOpen && selectedDeveloperPanelTab) { | ||
| PANEL_PRELOADERS[selectedDeveloperPanelTab]?.(); | ||
| } | ||
| }); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Wrap all sidebar/dev-panel lazy imports with reactLazyWithPreload and
prefetch the corresponding chunk when the user hovers or focuses the
icon/tab. On mount, idle-preload the persisted selected panel(s) if
their parent was last left open. Adds a theme-matched skeleton for the
terminal panel so first open isn't a blank flash.