Skip to content

Commit 9fbccc0

Browse files
chore: improve iframe loading time (#18134)
- Pre-load all the embed app iframes - Avoid reloading the iframe when the selected app changes
1 parent 00502dc commit 9fbccc0

File tree

2 files changed

+56
-22
lines changed

2 files changed

+56
-22
lines changed

site/src/pages/TaskPage/TaskPage.stories.tsx

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import type { Meta, StoryObj } from "@storybook/react";
2-
import { spyOn } from "@storybook/test";
2+
import { expect, spyOn, within } from "@storybook/test";
33
import {
44
MockFailedWorkspace,
55
MockStartingWorkspace,
@@ -115,9 +115,8 @@ export const Active: Story = {
115115
...MockWorkspaceApp,
116116
id: "claude-code",
117117
display_name: "Claude Code",
118+
slug: "claude-code",
118119
icon: "/icon/claude.svg",
119-
url: `${window.location.protocol}/iframe.html?viewMode=story&id=pages-terminal--ready&args=&globals=`,
120-
external: true,
121120
statuses: [
122121
MockWorkspaceAppStatus,
123122
{
@@ -131,11 +130,13 @@ export const Active: Story = {
131130
{
132131
...MockWorkspaceApp,
133132
id: "vscode",
133+
slug: "vscode",
134134
display_name: "VS Code Web",
135135
icon: "/icon/code.svg",
136136
},
137137
{
138138
...MockWorkspaceApp,
139+
slug: "zed",
139140
id: "zed",
140141
display_name: "Zed",
141142
icon: "/icon/zed.svg",
@@ -153,4 +154,15 @@ export const Active: Story = {
153154
},
154155
});
155156
},
157+
play: async ({ canvasElement }) => {
158+
const canvas = within(canvasElement);
159+
160+
const vscodeIframe = await canvas.findByTitle("VS Code Web");
161+
const zedIframe = await canvas.findByTitle("Zed");
162+
const claudeIframe = await canvas.findByTitle("Claude Code");
163+
164+
expect(vscodeIframe).not.toBeVisible();
165+
expect(zedIframe).not.toBeVisible();
166+
expect(claudeIframe).toBeVisible();
167+
},
156168
};

site/src/pages/TaskPage/TaskPage.tsx

Lines changed: 41 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,13 @@ import {
1919
TooltipProvider,
2020
TooltipTrigger,
2121
} from "components/Tooltip/Tooltip";
22-
import { useProxy } from "contexts/ProxyContext";
2322
import {
2423
ArrowLeftIcon,
2524
ChevronDownIcon,
2625
LayoutGridIcon,
2726
RotateCcwIcon,
2827
} from "lucide-react";
2928
import { AppStatusIcon } from "modules/apps/AppStatusIcon";
30-
import { getAppHref } from "modules/apps/apps";
3129
import { useAppLink } from "modules/apps/useAppLink";
3230
import { AI_PROMPT_PARAMETER_NAME, type Task } from "modules/tasks/tasks";
3331
import { WorkspaceAppStatus } from "modules/workspaces/WorkspaceAppStatus/WorkspaceAppStatus";
@@ -312,17 +310,6 @@ const TaskApps: FC<TaskAppsProps> = ({ task }) => {
312310
throw new Error(`Agent for app ${activeAppId} not found in task workspace`);
313311
}
314312

315-
const { proxy } = useProxy();
316-
const [iframeSrc, setIframeSrc] = useState(() => {
317-
const src = getAppHref(activeApp, {
318-
agent,
319-
workspace: task.workspace,
320-
path: proxy.preferredPathAppURL,
321-
host: proxy.preferredWildcardHostname,
322-
});
323-
return src;
324-
});
325-
326313
const embeddedApps = apps.filter((app) => !app.external);
327314
const externalApps = apps.filter((app) => app.external);
328315

@@ -344,7 +331,6 @@ const TaskApps: FC<TaskAppsProps> = ({ task }) => {
344331

345332
e.preventDefault();
346333
setActiveAppId(app.id);
347-
setIframeSrc(e.currentTarget.href);
348334
}}
349335
/>
350336
))}
@@ -387,11 +373,16 @@ const TaskApps: FC<TaskAppsProps> = ({ task }) => {
387373
</div>
388374

389375
<div className="flex-1">
390-
<iframe
391-
title={activeApp.display_name ?? activeApp.slug}
392-
className="w-full h-full border-0"
393-
src={iframeSrc}
394-
/>
376+
{embeddedApps.map((app) => {
377+
return (
378+
<TaskAppIFrame
379+
key={app.id}
380+
active={activeAppId === app.id}
381+
app={app}
382+
task={task}
383+
/>
384+
);
385+
})}
395386
</div>
396387
</main>
397388
);
@@ -443,6 +434,37 @@ const TaskAppButton: FC<TaskAppButtonProps> = ({
443434
);
444435
};
445436

437+
type TaskAppIFrameProps = {
438+
task: Task;
439+
app: WorkspaceApp;
440+
active: boolean;
441+
};
442+
443+
const TaskAppIFrame: FC<TaskAppIFrameProps> = ({ task, app, active }) => {
444+
const agent = task.workspace.latest_build.resources
445+
.flatMap((r) => r.agents)
446+
.filter((a) => !!a)
447+
.find((a) => a.apps.some((a) => a.id === app.id));
448+
449+
if (!agent) {
450+
throw new Error(`Agent for app ${app.id} not found in task workspace`);
451+
}
452+
453+
const link = useAppLink(app, {
454+
agent,
455+
workspace: task.workspace,
456+
});
457+
458+
return (
459+
<iframe
460+
src={link.href}
461+
title={link.label}
462+
loading="eager"
463+
className={cn([active ? "block" : "hidden", "w-full h-full border-0"])}
464+
/>
465+
);
466+
};
467+
446468
export const data = {
447469
fetchTask: async (workspaceOwnerUsername: string, workspaceName: string) => {
448470
const workspace = await API.getWorkspaceByOwnerAndName(

0 commit comments

Comments
 (0)