mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2026-06-12 15:37:27 +01:00
Copilot changes to improve initial load time.
This commit is contained in:
+57
-32
@@ -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>;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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
@@ -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
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user