Skip to content

Commit b849f92

Browse files
authored
Move connect-agent above sources and preload plugin chunks (#494)
- Sources page: render McpInstallCard above the connected sources list so agent-pairing is the primary affordance on an empty workspace. - Add optional `preload` to SourcePlugin and call it from ExecutorPluginsProvider on mount, so navigating into a plugin's add/edit page no longer trips Suspense waiting on the lazy chunk. - Drop the visible "Loading…" Suspense fallback in sources-add — the preload now covers warm sessions, and a flash of nothing is better than a layout-shifting label on cold ones.
1 parent c627c48 commit b849f92

7 files changed

Lines changed: 71 additions & 21 deletions

File tree

‎packages/core/sdk/src/client.ts‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
createContext,
1414
createElement,
1515
useContext,
16+
useEffect,
1617
useMemo,
1718
type ComponentType,
1819
type ReactNode,
@@ -130,6 +131,10 @@ export interface SourcePlugin {
130131
readonly sourceId: string;
131132
}>;
132133
readonly presets?: readonly SourcePreset[];
134+
/** Trigger early download of the plugin's lazy component chunks (add/edit/etc.).
135+
* Call from the host on intent (hover/focus) so the chunks land before the
136+
* user navigates into the add page. Idempotent. */
137+
readonly preload?: () => void;
133138
}
134139

135140
// ---------------------------------------------------------------------------
@@ -269,6 +274,11 @@ export function ExecutorPluginsProvider(
269274
}),
270275
[plugins],
271276
);
277+
// Kick off lazy chunk downloads for every source plugin once the host
278+
// mounts, so navigating into an add/edit page doesn't suspend.
279+
useEffect(() => {
280+
for (const sp of value.sourcePlugins) sp.preload?.();
281+
}, [value.sourcePlugins]);
272282
return createElement(ExecutorPluginsContext.Provider, { value }, children);
273283
}
274284

‎packages/plugins/google-discovery/src/react/source-plugin.ts‎

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,23 @@ import { lazy } from "react";
22
import type { SourcePlugin } from "@executor-js/sdk/client";
33
import { googleDiscoveryPresets } from "../sdk/presets";
44

5+
const importAdd = () => import("./AddGoogleDiscoverySource");
6+
const importEdit = () => import("./EditGoogleDiscoverySource");
7+
const importSummary = () => import("./GoogleDiscoverySourceSummary");
8+
const importSignIn = () => import("./GoogleDiscoverySignInButton");
9+
510
export const googleDiscoverySourcePlugin: SourcePlugin = {
611
key: "googleDiscovery",
712
label: "Google Discovery",
8-
add: lazy(() => import("./AddGoogleDiscoverySource")),
9-
edit: lazy(() => import("./EditGoogleDiscoverySource")),
10-
summary: lazy(() => import("./GoogleDiscoverySourceSummary")),
11-
signIn: lazy(() => import("./GoogleDiscoverySignInButton")),
13+
add: lazy(importAdd),
14+
edit: lazy(importEdit),
15+
summary: lazy(importSummary),
16+
signIn: lazy(importSignIn),
1217
presets: googleDiscoveryPresets,
18+
preload: () => {
19+
void importAdd();
20+
void importEdit();
21+
void importSummary();
22+
void importSignIn();
23+
},
1324
};

‎packages/plugins/graphql/src/react/source-plugin.ts‎

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,23 @@ import { lazy } from "react";
22
import type { SourcePlugin } from "@executor-js/sdk/client";
33
import { graphqlPresets } from "../sdk/presets";
44

5+
const importAdd = () => import("./AddGraphqlSource");
6+
const importEdit = () => import("./EditGraphqlSource");
7+
const importSummary = () => import("./GraphqlSourceSummary");
8+
const importSignIn = () => import("./GraphqlSignInButton");
9+
510
export const graphqlSourcePlugin: SourcePlugin = {
611
key: "graphql",
712
label: "GraphQL",
8-
add: lazy(() => import("./AddGraphqlSource")),
9-
edit: lazy(() => import("./EditGraphqlSource")),
10-
summary: lazy(() => import("./GraphqlSourceSummary")),
11-
signIn: lazy(() => import("./GraphqlSignInButton")),
13+
add: lazy(importAdd),
14+
edit: lazy(importEdit),
15+
summary: lazy(importSummary),
16+
signIn: lazy(importSignIn),
1217
presets: graphqlPresets,
18+
preload: () => {
19+
void importAdd();
20+
void importEdit();
21+
void importSummary();
22+
void importSignIn();
23+
},
1324
};

‎packages/plugins/mcp/src/react/source-plugin.tsx‎

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,13 @@ import { lazy, type ComponentProps, type ComponentType } from "react";
22
import type { SourcePlugin } from "@executor-js/sdk/client";
33
import { mcpPresets } from "../sdk/presets";
44

5-
const LazyAddMcpSource = lazy(() => import("./AddMcpSource"));
6-
const LazyEditMcpSource = lazy(() => import("./EditMcpSource"));
7-
const LazyMcpSignInButton = lazy(() => import("./McpSignInButton"));
5+
const importAdd = () => import("./AddMcpSource");
6+
const importEdit = () => import("./EditMcpSource");
7+
const importSignIn = () => import("./McpSignInButton");
8+
9+
const LazyAddMcpSource = lazy(importAdd);
10+
const LazyEditMcpSource = lazy(importEdit);
11+
const LazyMcpSignInButton = lazy(importSignIn);
812

913
type AddProps = ComponentProps<SourcePlugin["add"]>;
1014

@@ -41,6 +45,11 @@ export const createMcpSourcePlugin = (
4145
edit: LazyEditMcpSource,
4246
signIn: LazyMcpSignInButton,
4347
presets,
48+
preload: () => {
49+
void importAdd();
50+
void importEdit();
51+
void importSignIn();
52+
},
4453
};
4554
};
4655

‎packages/plugins/openapi/src/react/source-plugin.ts‎

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,20 @@ import { lazy } from "react";
22
import type { SourcePlugin } from "@executor-js/sdk/client";
33
import { openApiPresets } from "../sdk/presets";
44

5+
const importAdd = () => import("./AddOpenApiSource");
6+
const importEdit = () => import("./EditOpenApiSource");
7+
const importSummary = () => import("./OpenApiSourceSummary");
8+
59
export const openApiSourcePlugin: SourcePlugin = {
610
key: "openapi",
711
label: "OpenAPI",
8-
add: lazy(() => import("./AddOpenApiSource")),
9-
edit: lazy(() => import("./EditOpenApiSource")),
10-
summary: lazy(() => import("./OpenApiSourceSummary")),
12+
add: lazy(importAdd),
13+
edit: lazy(importEdit),
14+
summary: lazy(importSummary),
1115
presets: openApiPresets,
16+
preload: () => {
17+
void importAdd();
18+
void importEdit();
19+
void importSummary();
20+
},
1221
};

‎packages/react/src/pages/sources-add.tsx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ export function SourcesAddPage(props: {
4646
return (
4747
<div className="relative min-h-0 flex-1 overflow-y-auto">
4848
<div className="mx-auto flex min-h-full max-w-4xl flex-col px-6 py-10 lg:px-10 lg:py-14">
49-
<Suspense fallback={<p className="text-sm text-muted-foreground">Loading…</p>}>
49+
<Suspense fallback={null}>
5050
<AddComponent
5151
initialUrl={url}
5252
initialPreset={preset}

‎packages/react/src/pages/sources.tsx‎

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,12 @@ export function SourcesPage() {
8181
</Button>
8282
</div>
8383

84+
<div className="mb-8">
85+
<McpInstallCard />
86+
</div>
87+
88+
<div className="mb-8 border-t border-border/50" />
89+
8490
{AsyncResult.match(sources, {
8591
onInitial: () => <SourcesGridSkeleton />,
8692
onFailure: () => <p className="text-sm text-destructive">Failed to load sources</p>,
@@ -106,12 +112,6 @@ export function SourcesPage() {
106112
);
107113
},
108114
})}
109-
110-
<div className="mb-8 border-t border-border/50" />
111-
112-
<div className="mb-8">
113-
<McpInstallCard />
114-
</div>
115115
</div>
116116

117117
<ConnectDialog open={connectOpen} onOpenChange={setConnectOpen} />

0 commit comments

Comments
 (0)