perf: load plugin, debugger and project popups when first shown

The IntelliJ theme import, the debugger's popovers and dialogs and the title
bar's project popup now load when first opened. `lazyWhen` keeps a view
mounted once shown, so its own open/closed handling is unchanged.
Цей коміт міститься в:
l0ng-ai committed 2026-11-06 08:21:15 +08:00
1 джерело 2e12f51cac
коміт 75718a6d21
6 files changed
+50 -29

No files matched your search

+10 -3
Переглянути файл
@@ -3,7 +3,7 @@
* backend), DAP sessions driven by `IdeEvent::Debug`, the Debug tool window,
* execution point + inline values, Evaluate / View Breakpoints dialogs.
*/
import { lazyComponent } from "@/ui";
import { lazyComponent, lazyWhen } from "@/ui";
import { onIdeEvent } from "@/platform/ipc";
import { useProject } from "@/platform/project";
import { selectionValueTooltip, valueTooltip } from "./editor/valueTooltip";
@@ -35,9 +35,13 @@ import {
stopSession,
useDebug,
} from "./state";
import { DebuggerOverlays } from "./ui/Overlays";
import { useOverlays } from "./ui/popoverStore";
import { REMOTE_TYPES } from "./remote";
/** Whether a breakpoint popover, debugger dialog or chooser is open. */
const useAnyOverlay = () =>
useOverlays((s) => !!(s.popover || s.evaluate || s.breakpoints || s.chooser || s.attach || s.smartStep));
export function activate(): () => void {
useDebugRunSessions.setState({
Console: lazyComponent(() => import("./ui/DebugConsole").then((m) => m.DebugServiceConsole)),
@@ -62,7 +66,10 @@ export function activate(): () => void {
component: lazyComponent(() => import("./ui/DebugToolWindow").then((m) => m.DebugToolWindow)),
headerContent: lazyComponent(() => import("./ui/DebugToolWindow").then((m) => m.DebugTabs)),
}),
registerOverlay({ id: "debugger", component: DebuggerOverlays }),
registerOverlay({
id: "debugger",
component: lazyWhen(useAnyOverlay, () => import("./ui/Overlays").then((m) => m.DebuggerOverlays)),
}),
registerMenuGroup({
id: "debugger.evaluate",
menus: ["editor"],
+8 -4
Переглянути файл
@@ -15,7 +15,7 @@
* `line-markers` providers (`lineMarkerGutter.ts`).
*/
import { registerEditorContribution } from "@/editor/api";
import { lazyComponent } from "@/ui";
import { lazyComponent, lazyWhen } from "@/ui";
import { executeAction, registerActions } from "@/platform/actions";
import { useProject } from "@/platform/project";
import { editorService, registerOverlay, registerSettingsPage } from "@/workbench/contributions";
@@ -31,8 +31,7 @@ import { trackIconTheme } from "./iconThemes";
import { pluginLineMarkerGutter } from "./lineMarkerGutter";
import { defineBuiltinUiPoints } from "./points";
import { startViewUi } from "./uiPoints";
import { importIntellijTheme, showInstalledIntellijThemes } from "./intellijImport";
import { IntellijThemesDialogHost } from "./IntellijThemesDialog";
import { showInstalledIntellijThemes, useIntellijThemesDialog } from "./intellijThemesDialogStore";
import { installFromDisk, refreshPlugins, reloadPlugin, SETTINGS_PAGE_ID, updateAllPlugins, usePlugins } from "./store";
function registerPluginsPage(): () => void {
@@ -47,6 +46,11 @@ function registerPluginsPage(): () => void {
}
/** Settings › Plugins for the Settings window: the page plus the plugin theme. */
const useIntellijThemesOpen = () => useIntellijThemesDialog((s) => s.open);
const IntellijThemesDialogHost = lazyWhen(useIntellijThemesOpen, () =>
import("./IntellijThemesDialog").then((m) => m.IntellijThemesDialogHost),
);
export function activateSettingsPages(): () => void {
const disposers = [
registerPluginsPage(),
@@ -108,7 +112,7 @@ export function activate(): () => void {
// Installing needs a project's window.
when: "projectOpen",
run: async ({ arg }) => {
await importIntellijTheme(typeof arg === "string" ? arg : undefined);
await (await import("./intellijImport")).importIntellijTheme(typeof arg === "string" ? arg : undefined);
},
},
{
+5 -17
Переглянути файл
@@ -5,7 +5,6 @@
* here (`intellijTheme.ts`), installed through the usual install page. Its
* themes join the others; which one to use is chosen in Settings › Appearance.
*/
import { create } from "zustand";
import { notifications } from "@/workbench/contributions";
import { pluginsBackend } from "./backend";
import { intellijThemePackage } from "./intellijTheme";
@@ -35,19 +34,8 @@ export async function importIntellijTheme(path?: string): Promise<string | null>
return installFromDisk(dir);
}
interface DialogState {
open: boolean;
/** Called with the plugin id after an import from the dialog. */
onImported: ((id: string) => void) | null;
}
export const useIntellijThemesDialog = create<DialogState>(() => ({ open: false, onImported: null }));
/** Shows the themes of the JetBrains IDEs on this machine to pick one to import. */
export function showInstalledIntellijThemes(onImported?: (id: string) => void): void {
useIntellijThemesDialog.setState({ open: true, onImported: onImported ?? null });
}
export function closeInstalledIntellijThemes(): void {
useIntellijThemesDialog.setState({ open: false, onImported: null });
}
export {
closeInstalledIntellijThemes,
showInstalledIntellijThemes,
useIntellijThemesDialog,
} from "./intellijThemesDialogStore";
+18
Переглянути файл
@@ -0,0 +1,18 @@
import { create } from "zustand";
interface DialogState {
open: boolean;
/** Called with the plugin id after an import from the dialog. */
onImported: ((id: string) => void) | null;
}
export const useIntellijThemesDialog = create<DialogState>(() => ({ open: false, onImported: null }));
/** Shows the themes of the JetBrains IDEs on this machine to pick one to import. */
export function showInstalledIntellijThemes(onImported?: (id: string) => void): void {
useIntellijThemesDialog.setState({ open: true, onImported: onImported ?? null });
}
export function closeInstalledIntellijThemes(): void {
useIntellijThemesDialog.setState({ open: false, onImported: null });
}
+2 -2
Переглянути файл
@@ -1,7 +1,7 @@
/** Welcome screen, recent projects (File › Open Recent) and reopening the last project on startup. */
import { registerActions } from "@/platform/actions";
import { lazyComponent } from "@/ui";
import { provideProjectPopup, provideWelcomeView, registerOverlay } from "@/workbench/contributions";
import { ProjectPopup } from "./ProjectPopup";
import { RecentProjectsPopup, showRecentProjectsPopup } from "./RecentProjectsPopup";
import { reopenLastProject, trackSession } from "./session";
import { WelcomeView } from "./WelcomeView";
@@ -10,7 +10,7 @@ export function activate(): () => void {
const disposers = [
trackSession(),
provideWelcomeView(WelcomeView),
provideProjectPopup(ProjectPopup),
provideProjectPopup(lazyComponent(() => import("./ProjectPopup").then((m) => m.ProjectPopup))),
registerOverlay({ id: "welcome.recentProjects", component: RecentProjectsPopup }),
registerActions([
{
+7 -3
Переглянути файл
@@ -40,16 +40,20 @@ export function lazyComponent<P extends object>(load: () => Promise<ComponentTyp
}
/**
* A lazy component mounted only while `useShown()` holds (a dialog's `open`), so an
* always-registered overlay doesn't load its code at start-up.
* A lazy component first mounted when `useShown()` holds (a dialog opens), so an
* always-registered overlay doesn't load its code at start-up. It stays mounted
* afterwards: the component still decides what to render when closed.
*/
export function lazyWhen<P extends object>(
useShown: () => boolean,
load: () => Promise<ComponentType<P>>,
): ComponentType<P> {
const Lazy = lazyComponent(load);
let shown = false;
return function LazyWhen(props: P) {
return useShown() ? <Lazy {...props} /> : null;
const now = useShown();
shown ||= now;
return shown ? <Lazy {...props} /> : null;
};
}