Copilot changes to improve initial load time.

This commit is contained in:
Jade Welton
2026-02-27 15:29:25 -08:00
parent 204444b878
commit 1689cbf75a
4 changed files with 143 additions and 57 deletions
+57 -32
View File
@@ -2,26 +2,48 @@ import { Spinner, SpinnerSize, TooltipHost } from "@fluentui/react";
import { CollectionTabKind } from "Contracts/ViewModels"; import { CollectionTabKind } from "Contracts/ViewModels";
import Explorer from "Explorer/Explorer"; import Explorer from "Explorer/Explorer";
import { useCommandBar } from "Explorer/Menus/CommandBar/CommandBarComponentAdapter"; import { useCommandBar } from "Explorer/Menus/CommandBar/CommandBarComponentAdapter";
import { QueryCopilotTab } from "Explorer/QueryCopilot/QueryCopilotTab";
import { FabricHomeScreen } from "Explorer/SplashScreen/FabricHome";
import { SplashScreen } from "Explorer/SplashScreen/SplashScreen"; import { SplashScreen } from "Explorer/SplashScreen/SplashScreen";
import { ConnectTab } from "Explorer/Tabs/ConnectTab"; import { ConnectTab } from "Explorer/Tabs/ConnectTab";
import { PostgresConnectTab } from "Explorer/Tabs/PostgresConnectTab";
import { QuickstartTab } from "Explorer/Tabs/QuickstartTab"; import { QuickstartTab } from "Explorer/Tabs/QuickstartTab";
import { VcoreMongoConnectTab } from "Explorer/Tabs/VCoreMongoConnectTab";
import { VcoreMongoQuickstartTab } from "Explorer/Tabs/VCoreMongoQuickstartTab";
import { KeyboardAction, KeyboardActionGroup, useKeyboardActionGroup } from "KeyboardShortcuts"; import { KeyboardAction, KeyboardActionGroup, useKeyboardActionGroup } from "KeyboardShortcuts";
import { isFabricNative } from "Platform/Fabric/FabricUtil"; import { isFabricNative } from "Platform/Fabric/FabricUtil";
import { userContext } from "UserContext"; import { userContext } from "UserContext";
import { useTeachingBubble } from "hooks/useTeachingBubble"; import { useTeachingBubble } from "hooks/useTeachingBubble";
import ko from "knockout"; import ko from "knockout";
import React, { MutableRefObject, useEffect, useRef, useState } from "react"; import React, { MutableRefObject, Suspense, useEffect, useRef, useState } from "react";
import errorQuery from "../../../images/error_no_outline.svg"; import errorQuery from "../../../images/error_no_outline.svg";
import warningIconSvg from "../../../images/warning.svg"; import warningIconSvg from "../../../images/warning.svg";
import { useObservable } from "../../hooks/useObservable"; import { useObservable } from "../../hooks/useObservable";
import { ReactTabKind, useTabs } from "../../hooks/useTabs"; import { ReactTabKind, useTabs } from "../../hooks/useTabs";
import TabsBase from "./TabsBase"; import TabsBase from "./TabsBase";
// Lazy-loaded tab components (code-split by API type)
const QueryCopilotTab = React.lazy(() =>
import(/* webpackChunkName: "QueryCopilotTab" */ "Explorer/QueryCopilot/QueryCopilotTab").then((m) => ({
default: m.QueryCopilotTab,
})),
);
const FabricHomeScreen = React.lazy(() =>
import(/* webpackChunkName: "FabricHomeScreen" */ "Explorer/SplashScreen/FabricHome").then((m) => ({
default: m.FabricHomeScreen,
})),
);
const PostgresConnectTab = React.lazy(() =>
import(/* webpackChunkName: "PostgresConnectTab" */ "Explorer/Tabs/PostgresConnectTab").then((m) => ({
default: m.PostgresConnectTab,
})),
);
const VcoreMongoConnectTab = React.lazy(() =>
import(/* webpackChunkName: "VcoreMongoConnectTab" */ "Explorer/Tabs/VCoreMongoConnectTab").then((m) => ({
default: m.VcoreMongoConnectTab,
})),
);
const VcoreMongoQuickstartTab = React.lazy(() =>
import(/* webpackChunkName: "VcoreMongoQuickstartTab" */ "Explorer/Tabs/VCoreMongoQuickstartTab").then((m) => ({
default: m.VcoreMongoQuickstartTab,
})),
);
type Tab = TabsBase | (TabsBase & { render: () => JSX.Element }); type Tab = TabsBase | (TabsBase & { render: () => JSX.Element });
interface TabsProps { interface TabsProps {
@@ -302,30 +324,33 @@ const isQueryErrorThrown = (tab?: Tab, tabKind?: ReactTabKind): boolean => {
}; };
const getReactTabContent = (activeReactTab: ReactTabKind, explorer: Explorer): JSX.Element => { const getReactTabContent = (activeReactTab: ReactTabKind, explorer: Explorer): JSX.Element => {
switch (activeReactTab) { const content = (() => {
case ReactTabKind.Connect: switch (activeReactTab) {
return userContext.apiType === "VCoreMongo" ? ( case ReactTabKind.Connect:
<VcoreMongoConnectTab /> return userContext.apiType === "VCoreMongo" ? (
) : userContext.apiType === "Postgres" ? ( <VcoreMongoConnectTab />
<PostgresConnectTab /> ) : userContext.apiType === "Postgres" ? (
) : ( <PostgresConnectTab />
<ConnectTab /> ) : (
); <ConnectTab />
case ReactTabKind.Home: );
if (isFabricNative()) { case ReactTabKind.Home:
return <FabricHomeScreen explorer={explorer} />; if (isFabricNative()) {
} else { return <FabricHomeScreen explorer={explorer} />;
return <SplashScreen explorer={explorer} />; } else {
} return <SplashScreen explorer={explorer} />;
case ReactTabKind.Quickstart: }
return userContext.apiType === "VCoreMongo" ? ( case ReactTabKind.Quickstart:
<VcoreMongoQuickstartTab explorer={explorer} /> return userContext.apiType === "VCoreMongo" ? (
) : ( <VcoreMongoQuickstartTab explorer={explorer} />
<QuickstartTab explorer={explorer} /> ) : (
); <QuickstartTab explorer={explorer} />
case ReactTabKind.QueryCopilot: );
return <QueryCopilotTab explorer={explorer} />; case ReactTabKind.QueryCopilot:
default: return <QueryCopilotTab explorer={explorer} />;
throw new Error(`Unsupported tab kind ${ReactTabKind[activeReactTab]}`); default:
} throw new Error(`Unsupported tab kind ${ReactTabKind[activeReactTab]}`);
}
})();
return <Suspense fallback={<Spinner size={SpinnerSize.large} />}>{content}</Suspense>;
}; };
+7 -5
View File
@@ -34,7 +34,7 @@ import Explorer from "../Explorer";
import { useCommandBar } from "../Menus/CommandBar/CommandBarComponentAdapter"; import { useCommandBar } from "../Menus/CommandBar/CommandBarComponentAdapter";
import { CassandraAPIDataClient, CassandraTableKey, CassandraTableKeys } from "../Tables/TableDataClient"; import { CassandraAPIDataClient, CassandraTableKey, CassandraTableKeys } from "../Tables/TableDataClient";
import ConflictsTab from "../Tabs/ConflictsTab"; import ConflictsTab from "../Tabs/ConflictsTab";
import GraphTab from "../Tabs/GraphTab"; import type GraphTab from "../Tabs/GraphTab";
import { NewMongoQueryTab } from "../Tabs/MongoQueryTab/MongoQueryTab"; import { NewMongoQueryTab } from "../Tabs/MongoQueryTab/MongoQueryTab";
import { NewMongoShellTab } from "../Tabs/MongoShellTab/MongoShellTab"; import { NewMongoShellTab } from "../Tabs/MongoShellTab/MongoShellTab";
import { NewQueryTab } from "../Tabs/QueryTab/QueryTab"; import { NewQueryTab } from "../Tabs/QueryTab/QueryTab";
@@ -450,7 +450,7 @@ export default class Collection implements ViewModels.Collection {
} }
} }
public onGraphDocumentsClick() { public async onGraphDocumentsClick() {
useSelectedNode.getState().setSelectedNode(this); useSelectedNode.getState().setSelectedNode(this);
this.selectedSubnodeKind(ViewModels.CollectionTabKind.Graph); this.selectedSubnodeKind(ViewModels.CollectionTabKind.Graph);
TelemetryProcessor.trace(Action.SelectItem, ActionModifiers.Mark, { TelemetryProcessor.trace(Action.SelectItem, ActionModifiers.Mark, {
@@ -483,7 +483,8 @@ export default class Collection implements ViewModels.Collection {
tabTitle: title, tabTitle: title,
}); });
graphTab = new GraphTab({ const { default: GraphTabModule } = await import(/* webpackChunkName: "GraphTab" */ "../Tabs/GraphTab");
graphTab = new GraphTabModule({
account: userContext.databaseAccount, account: userContext.databaseAccount,
tabKind: ViewModels.CollectionTabKind.Graph, tabKind: ViewModels.CollectionTabKind.Graph,
node: this, node: this,
@@ -727,7 +728,7 @@ export default class Collection implements ViewModels.Collection {
useTabs.getState().activateNewTab(newMongoQueryTab); useTabs.getState().activateNewTab(newMongoQueryTab);
} }
public onNewGraphClick() { public async onNewGraphClick() {
const id: number = useTabs.getState().getTabs(ViewModels.CollectionTabKind.Graph).length + 1; const id: number = useTabs.getState().getTabs(ViewModels.CollectionTabKind.Graph).length + 1;
const title: string = "Graph Query " + id; const title: string = "Graph Query " + id;
@@ -739,7 +740,8 @@ export default class Collection implements ViewModels.Collection {
tabTitle: title, tabTitle: title,
}); });
const graphTab: GraphTab = new GraphTab({ const { default: GraphTabModule } = await import(/* webpackChunkName: "GraphTab" */ "../Tabs/GraphTab");
const graphTab: GraphTab = new GraphTabModule({
account: userContext.databaseAccount, account: userContext.databaseAccount,
tabKind: ViewModels.CollectionTabKind.Graph, tabKind: ViewModels.CollectionTabKind.Graph,
node: this, node: this,
+54 -19
View File
@@ -5,27 +5,14 @@ import "./ReactDevTools";
import { initializeIcons, loadTheme, useTheme } from "@fluentui/react"; import { initializeIcons, loadTheme, useTheme } from "@fluentui/react";
import { FluentProvider, makeStyles, webDarkTheme, webLightTheme } from "@fluentui/react-components"; import { FluentProvider, makeStyles, webDarkTheme, webLightTheme } from "@fluentui/react-components";
import { Platform } from "ConfigContext"; import { Platform } from "ConfigContext";
import ContainerCopyPanel from "Explorer/ContainerCopy/ContainerCopyPanel";
import Explorer from "Explorer/Explorer"; import Explorer from "Explorer/Explorer";
import { QuickstartCarousel } from "Explorer/Quickstart/QuickstartCarousel";
import { MongoQuickstartTutorial } from "Explorer/Quickstart/Tutorials/MongoQuickstartTutorial";
import { SQLQuickstartTutorial } from "Explorer/Quickstart/Tutorials/SQLQuickstartTutorial";
import { userContext } from "UserContext"; import { userContext } from "UserContext";
import "allotment/dist/style.css"; import "allotment/dist/style.css";
import "bootstrap/dist/css/bootstrap.css";
import { useCarousel } from "hooks/useCarousel"; import { useCarousel } from "hooks/useCarousel";
import React from "react"; import React from "react";
import ReactDOM from "react-dom"; import ReactDOM from "react-dom";
import "../externals/jquery-ui.min.css";
import "../externals/jquery-ui.min.js";
import "../externals/jquery-ui.structure.min.css";
import "../externals/jquery-ui.theme.min.css";
import "../externals/jquery.dataTables.min.css";
import "../externals/jquery.typeahead.min.css";
import "../externals/jquery.typeahead.min.js";
// Image Dependencies // Image Dependencies
import { SidePanel } from "Explorer/Panes/PanelContainerComponent"; import { SidePanel } from "Explorer/Panes/PanelContainerComponent";
import { QueryCopilotCarousel } from "Explorer/QueryCopilot/CopilotCarousel";
import { SidebarContainer } from "Explorer/Sidebar"; import { SidebarContainer } from "Explorer/Sidebar";
import { KeyboardShortcutRoot } from "KeyboardShortcuts"; import { KeyboardShortcutRoot } from "KeyboardShortcuts";
import "allotment/dist/style.css"; import "allotment/dist/style.css";
@@ -72,6 +59,50 @@ import { useKnockoutExplorer } from "./hooks/useKnockoutExplorer";
import { useThemeStore } from "./hooks/useTheme"; import { useThemeStore } from "./hooks/useTheme";
import "./less/DarkModeMenus.less"; import "./less/DarkModeMenus.less";
import "./less/ThemeSystem.less"; import "./less/ThemeSystem.less";
// Lazy-loaded components (code-split into separate chunks)
const ContainerCopyPanel = React.lazy(
() => import(/* webpackChunkName: "ContainerCopyPanel" */ "Explorer/ContainerCopy/ContainerCopyPanel"),
);
const QuickstartCarousel = React.lazy(() =>
import(/* webpackChunkName: "QuickstartCarousel" */ "Explorer/Quickstart/QuickstartCarousel").then((m) => ({
default: m.QuickstartCarousel,
})),
);
const SQLQuickstartTutorial = React.lazy(() =>
import(/* webpackChunkName: "SQLQuickstartTutorial" */ "Explorer/Quickstart/Tutorials/SQLQuickstartTutorial").then(
(m) => ({ default: m.SQLQuickstartTutorial }),
),
);
const MongoQuickstartTutorial = React.lazy(() =>
import(
/* webpackChunkName: "MongoQuickstartTutorial" */ "Explorer/Quickstart/Tutorials/MongoQuickstartTutorial"
).then((m) => ({ default: m.MongoQuickstartTutorial })),
);
const QueryCopilotCarousel = React.lazy(() =>
import(/* webpackChunkName: "QueryCopilotCarousel" */ "Explorer/QueryCopilot/CopilotCarousel").then((m) => ({
default: m.QueryCopilotCarousel,
})),
);
// Defer loading legacy jQuery/Bootstrap CSS and JS — they are needed by Tables features, not on initial render
const loadLegacyDependencies = () => {
// @ts-expect-error — side-effect-only imports handled by webpack, not real TS/ES modules
import(/* webpackChunkName: "legacy-styles" */ "bootstrap/dist/css/bootstrap.css");
// @ts-expect-error — webpack handles CSS imports
import(/* webpackChunkName: "legacy-styles" */ "../externals/jquery-ui.min.css");
// @ts-expect-error — webpack handles JS side-effect imports
import(/* webpackChunkName: "legacy-scripts" */ "../externals/jquery-ui.min.js");
// @ts-expect-error — webpack handles CSS imports
import(/* webpackChunkName: "legacy-styles" */ "../externals/jquery-ui.structure.min.css");
// @ts-expect-error — webpack handles CSS imports
import(/* webpackChunkName: "legacy-styles" */ "../externals/jquery-ui.theme.min.css");
// @ts-expect-error — webpack handles CSS imports
import(/* webpackChunkName: "legacy-styles" */ "../externals/jquery.dataTables.min.css");
// @ts-expect-error — webpack handles CSS imports
import(/* webpackChunkName: "legacy-styles" */ "../externals/jquery.typeahead.min.css");
import(/* webpackChunkName: "legacy-scripts" */ "../externals/jquery.typeahead.min.js");
};
// Initialize icons before React is loaded // Initialize icons before React is loaded
initializeIcons(undefined, { disableWarnings: true }); initializeIcons(undefined, { disableWarnings: true });
@@ -98,6 +129,8 @@ const App = (): JSX.Element => {
import("../less/documentDBFabric.less"); import("../less/documentDBFabric.less");
} }
StyleConstants.updateStyles(); StyleConstants.updateStyles();
// Load legacy jQuery/Bootstrap dependencies after initial render
loadLegacyDependencies();
}, [config?.platform]); }, [config?.platform]);
const explorer = useKnockoutExplorer(config?.platform); const explorer = useKnockoutExplorer(config?.platform);
@@ -131,11 +164,11 @@ const App = (): JSX.Element => {
<KeyboardShortcutRoot> <KeyboardShortcutRoot>
<div className="flexContainer" aria-hidden="false"> <div className="flexContainer" aria-hidden="false">
{userContext.features.enableContainerCopy && userContext.apiType === "SQL" ? ( {userContext.features.enableContainerCopy && userContext.apiType === "SQL" ? (
<> <React.Suspense fallback={null}>
<ContainerCopyPanel explorer={explorer} /> <ContainerCopyPanel explorer={explorer} />
<SidePanel /> <SidePanel />
<Dialog /> <Dialog />
</> </React.Suspense>
) : ( ) : (
<DivExplorer explorer={explorer} /> <DivExplorer explorer={explorer} />
)} )}
@@ -191,10 +224,12 @@ const DivExplorer: React.FC<{ explorer: Explorer }> = ({ explorer }) => {
</div> </div>
<SidePanel /> <SidePanel />
<Dialog /> <Dialog />
{<QuickstartCarousel isOpen={isCarouselOpen} />} <React.Suspense fallback={null}>
{<SQLQuickstartTutorial />} {<QuickstartCarousel isOpen={isCarouselOpen} />}
{<MongoQuickstartTutorial />} {<SQLQuickstartTutorial />}
{<QueryCopilotCarousel isOpen={isCopilotCarouselOpen} explorer={explorer} />} {<MongoQuickstartTutorial />}
{<QueryCopilotCarousel isOpen={isCopilotCarouselOpen} explorer={explorer} />}
</React.Suspense>
</div> </div>
); );
}; };
+25 -1
View File
@@ -123,7 +123,7 @@ module.exports = function (_env = {}, argv = {}) {
new HtmlWebpackPlugin({ new HtmlWebpackPlugin({
filename: "explorer.html", filename: "explorer.html",
template: "src/explorer.html", template: "src/explorer.html",
chunks: ["main"], chunks: ["main", "vendor", "vendor-fluentui", "vendor-nteract"],
}), }),
new HtmlWebpackPlugin({ new HtmlWebpackPlugin({
filename: "terminal.html", filename: "terminal.html",
@@ -262,6 +262,30 @@ module.exports = function (_env = {}, argv = {}) {
}, },
}), }),
], ],
splitChunks: {
chunks: "all",
maxInitialRequests: 10,
cacheGroups: {
fluentui: {
test: /[\\/]node_modules[\\/]@fluentui[\\/]/,
name: "vendor-fluentui",
chunks: "all",
priority: 30,
},
nteract: {
test: /[\\/]node_modules[\\/]@nteract[\\/]/,
name: "vendor-nteract",
chunks: "all",
priority: 30,
},
vendor: {
test: /[\\/]node_modules[\\/]/,
name: "vendor",
chunks: "all",
priority: 10,
},
},
},
}, },
watch: false, watch: false,
// Hack since it is hard to disable watch entirely with webpack dev server https://github.com/webpack/webpack-dev-server/issues/1251#issuecomment-654240734 // Hack since it is hard to disable watch entirely with webpack dev server https://github.com/webpack/webpack-dev-server/issues/1251#issuecomment-654240734