Skip to content

perf(chrome): preload panel chunks on hover/focus#9650

Open
mscolnick wants to merge 1 commit into
mainfrom
ms/feature/terminal-loading-state
Open

perf(chrome): preload panel chunks on hover/focus#9650
mscolnick wants to merge 1 commit into
mainfrom
ms/feature/terminal-loading-state

Conversation

@mscolnick
Copy link
Copy Markdown
Contributor

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.

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.
Copilot AI review requested due to automatic review settings May 21, 2026 17:08
@vercel
Copy link
Copy Markdown

vercel Bot commented May 21, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
marimo-docs Ready Ready Preview, Comment May 21, 2026 5:14pm

Request Review

Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

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

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
Loading

Re-trigger cubic

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

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 a PANEL_PRELOADERS map 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]?.();
}
});
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