From 39ede85432813ae3f4e0dea5ad8dd71ed70af783 Mon Sep 17 00:00:00 2001 From: Bruno Quaresma Date: Fri, 22 Aug 2025 19:10:54 +0000 Subject: [PATCH 1/6] Display loading section when agent is not ready --- site/src/pages/TaskPage/TaskPage.tsx | 122 +++++++++++++++++++++------ 1 file changed, 96 insertions(+), 26 deletions(-) diff --git a/site/src/pages/TaskPage/TaskPage.tsx b/site/src/pages/TaskPage/TaskPage.tsx index 4a65c6f1be993..0ca4d792eec84 100644 --- a/site/src/pages/TaskPage/TaskPage.tsx +++ b/site/src/pages/TaskPage/TaskPage.tsx @@ -1,7 +1,11 @@ import { API } from "api/api"; import { getErrorDetail, getErrorMessage } from "api/errors"; import { template as templateQueryOptions } from "api/queries/templates"; -import type { Workspace, WorkspaceStatus } from "api/typesGenerated"; +import type { + Workspace, + WorkspaceAgent, + WorkspaceStatus, +} from "api/typesGenerated"; import isChromatic from "chromatic/isChromatic"; import { Button } from "components/Button/Button"; import { Loader } from "components/Loader/Loader"; @@ -9,6 +13,8 @@ import { Margins } from "components/Margins/Margins"; import { ScrollArea } from "components/ScrollArea/ScrollArea"; import { useWorkspaceBuildLogs } from "hooks/useWorkspaceBuildLogs"; import { ArrowLeftIcon, RotateCcwIcon } from "lucide-react"; +import { AgentLogs } from "modules/resources/AgentLogs/AgentLogs"; +import { useAgentLogs } from "modules/resources/useAgentLogs"; import { AI_PROMPT_PARAMETER_NAME, type Task } from "modules/tasks/tasks"; import { WorkspaceBuildLogs } from "modules/workspaces/WorkspaceBuildLogs/WorkspaceBuildLogs"; import { type FC, type ReactNode, useEffect, useRef } from "react"; @@ -16,6 +22,7 @@ import { Helmet } from "react-helmet-async"; import { useQuery } from "react-query"; import { Panel, PanelGroup, PanelResizeHandle } from "react-resizable-panels"; import { Link as RouterLink, useParams } from "react-router"; +import type { FixedSizeList } from "react-window"; import { pageTitle } from "utils/page"; import { getActiveTransitionStats, @@ -87,6 +94,7 @@ const TaskPage = () => { } let content: ReactNode = null; + const agent = selectAgent(task); if (waitingStatuses.includes(task.workspace.latest_build.status)) { content = ; @@ -132,6 +140,8 @@ const TaskPage = () => { ); + } else if (agent && ["created", "starting"].includes(agent.lifecycle_state)) { + content = ; } else { content = ( @@ -196,34 +206,86 @@ const TaskBuildingWorkspace: FC = ({ task }) => { }, [buildLogs]); return ( -
-
-
-

- Starting your workspace -

-
- Your task will be running in a few moments -
-
+
+
+
+
+

+ Starting your workspace +

+
+ Your task will be running in a few moments +
+
-
- +
+ + + - - - +
+
+
+
+ ); +}; + +type TaskStartingAgentProps = { + agent: WorkspaceAgent; +}; + +const TaskStartingAgent: FC = ({ agent }) => { + const logs = useAgentLogs(agent, true); + const listRef = useRef(null); + + useEffect(() => { + if (listRef.current) { + listRef.current.scrollToItem(logs.length - 1, "end"); + } + }, [logs]); + + return ( +
+
+
+
+

+ Running your apps +

+
+ Your task will be running in a few moments +
+
+ +
+
+ ({ + id: l.id, + level: l.level, + output: l.output, + sourceId: l.source_id, + time: l.created_at, + }))} + sources={agent.log_sources} + height={96 * 4} + width="100%" + ref={listRef} + /> +
+
@@ -269,3 +331,11 @@ export const data = { const ellipsizeText = (text: string, maxLength = 80): string => { return text.length <= maxLength ? text : `${text.slice(0, maxLength - 3)}...`; }; + +function selectAgent(task: Task) { + const agents = task.workspace.latest_build.resources + .flatMap((r) => r.agents) + .filter((a) => !!a); + + return agents.at(0); +} From 19c7b4f35084dbeaabfd073305d54ed86378dd4f Mon Sep 17 00:00:00 2001 From: Bruno Quaresma Date: Mon, 25 Aug 2025 13:58:26 +0000 Subject: [PATCH 2/6] Add story --- site/src/pages/TaskPage/TaskPage.stories.tsx | 67 ++++++++++++++------ site/src/pages/TaskPage/TaskPage.tsx | 2 +- 2 files changed, 50 insertions(+), 19 deletions(-) diff --git a/site/src/pages/TaskPage/TaskPage.stories.tsx b/site/src/pages/TaskPage/TaskPage.stories.tsx index 6a486442ace8c..1d2ab073ab158 100644 --- a/site/src/pages/TaskPage/TaskPage.stories.tsx +++ b/site/src/pages/TaskPage/TaskPage.stories.tsx @@ -2,17 +2,17 @@ import { MockFailedWorkspace, MockStartingWorkspace, MockStoppedWorkspace, - MockTemplate, MockWorkspace, MockWorkspaceAgent, + MockWorkspaceAgentLogSource, + MockWorkspaceAgentStarting, MockWorkspaceApp, MockWorkspaceAppStatus, MockWorkspaceResource, mockApiError, } from "testHelpers/entities"; -import { withProxyProvider } from "testHelpers/storybook"; +import { withProxyProvider, withWebSocket } from "testHelpers/storybook"; import type { Meta, StoryObj } from "@storybook/react-vite"; -import { API } from "api/api"; import type { Workspace, WorkspaceApp, @@ -61,56 +61,87 @@ export const WaitingOnBuild: Story = { }, }; -export const WaitingOnBuildWithTemplate: Story = { +export const FailedBuild: Story = { beforeEach: () => { - spyOn(API, "getTemplate").mockResolvedValue(MockTemplate); spyOn(data, "fetchTask").mockResolvedValue({ prompt: "Create competitors page", - workspace: MockStartingWorkspace, + workspace: MockFailedWorkspace, }); }, }; -export const WaitingOnStatus: Story = { +export const TerminatedBuild: Story = { beforeEach: () => { spyOn(data, "fetchTask").mockResolvedValue({ prompt: "Create competitors page", - workspace: { - ...MockWorkspace, - latest_app_status: null, - }, + workspace: MockStoppedWorkspace, }); }, }; -export const FailedBuild: Story = { +export const TerminatedBuildWithStatus: Story = { beforeEach: () => { spyOn(data, "fetchTask").mockResolvedValue({ prompt: "Create competitors page", - workspace: MockFailedWorkspace, + workspace: { + ...MockStoppedWorkspace, + latest_app_status: MockWorkspaceAppStatus, + }, }); }, }; -export const TerminatedBuild: Story = { +export const WaitingOnStatus: Story = { beforeEach: () => { spyOn(data, "fetchTask").mockResolvedValue({ prompt: "Create competitors page", - workspace: MockStoppedWorkspace, + workspace: { + ...MockWorkspace, + latest_app_status: null, + }, }); }, }; -export const TerminatedBuildWithStatus: Story = { +export const WaitingStartupScripts: Story = { beforeEach: () => { spyOn(data, "fetchTask").mockResolvedValue({ prompt: "Create competitors page", workspace: { - ...MockStoppedWorkspace, - latest_app_status: MockWorkspaceAppStatus, + ...MockWorkspace, + latest_build: { + ...MockWorkspace.latest_build, + has_ai_task: true, + resources: [ + { ...MockWorkspaceResource, agents: [MockWorkspaceAgentStarting] }, + ], + }, }, }); }, + decorators: [withWebSocket], + parameters: { + webSocket: [ + { + event: "message", + data: JSON.stringify( + [ + "\x1b[91mCloning Git repository...", + "\x1b[2;37;41mStarting Docker Daemon...", + "\x1b[1;95mAdding some 🧙magic🧙...", + "Starting VS Code...", + "\r 0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0\r100 1475 0 1475 0 0 4231 0 --:--:-- --:--:-- --:--:-- 4238", + ].map((line, index) => ({ + id: index, + level: "info", + output: line, + source_id: MockWorkspaceAgentLogSource.id, + created_at: new Date().toISOString(), + })), + ), + }, + ], + }, }; export const SidebarAppHealthDisabled: Story = { diff --git a/site/src/pages/TaskPage/TaskPage.tsx b/site/src/pages/TaskPage/TaskPage.tsx index 0ca4d792eec84..a88742ddf7342 100644 --- a/site/src/pages/TaskPage/TaskPage.tsx +++ b/site/src/pages/TaskPage/TaskPage.tsx @@ -262,7 +262,7 @@ const TaskStartingAgent: FC = ({ agent }) => {

- Running your apps + Running startup scripts

Your task will be running in a few moments From 0c38cd116691ba06266d3b404e3f6a1c74ebbb94 Mon Sep 17 00:00:00 2001 From: Bruno Quaresma Date: Mon, 25 Aug 2025 14:08:42 +0000 Subject: [PATCH 3/6] Fix storybook --- site/src/pages/TaskPage/TaskPage.stories.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/site/src/pages/TaskPage/TaskPage.stories.tsx b/site/src/pages/TaskPage/TaskPage.stories.tsx index 1d2ab073ab158..5c405b44e5517 100644 --- a/site/src/pages/TaskPage/TaskPage.stories.tsx +++ b/site/src/pages/TaskPage/TaskPage.stories.tsx @@ -3,8 +3,8 @@ import { MockStartingWorkspace, MockStoppedWorkspace, MockWorkspace, - MockWorkspaceAgent, MockWorkspaceAgentLogSource, + MockWorkspaceAgentReady, MockWorkspaceAgentStarting, MockWorkspaceApp, MockWorkspaceAppStatus, @@ -254,7 +254,7 @@ const mockResources = ( ...MockWorkspaceResource, agents: [ { - ...MockWorkspaceAgent, + ...MockWorkspaceAgentReady, apps: [ ...(props?.apps ?? []), { From d7aad8729a6d349c1ddcc733dd7c3253b5f7fc2d Mon Sep 17 00:00:00 2001 From: Bruno Quaresma Date: Mon, 25 Aug 2025 14:16:07 +0000 Subject: [PATCH 4/6] Fix another story --- site/src/pages/TaskPage/TaskPage.stories.tsx | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/site/src/pages/TaskPage/TaskPage.stories.tsx b/site/src/pages/TaskPage/TaskPage.stories.tsx index 5c405b44e5517..acdb4736788ac 100644 --- a/site/src/pages/TaskPage/TaskPage.stories.tsx +++ b/site/src/pages/TaskPage/TaskPage.stories.tsx @@ -98,6 +98,12 @@ export const WaitingOnStatus: Story = { workspace: { ...MockWorkspace, latest_app_status: null, + latest_build: { + ...MockWorkspace.latest_build, + resources: [ + { ...MockWorkspaceResource, agents: [MockWorkspaceAgentReady] }, + ], + }, }, }); }, From 66d30d0a3ee2e1f208b25f49322af7ae8c606be4 Mon Sep 17 00:00:00 2001 From: Bruno Quaresma Date: Tue, 26 Aug 2025 13:17:28 +0000 Subject: [PATCH 5/6] Apply PR reviews --- site/src/pages/TaskPage/TaskPage.stories.tsx | 2 +- site/src/pages/TaskPage/TaskPage.tsx | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/site/src/pages/TaskPage/TaskPage.stories.tsx b/site/src/pages/TaskPage/TaskPage.stories.tsx index acdb4736788ac..e44fece019f7b 100644 --- a/site/src/pages/TaskPage/TaskPage.stories.tsx +++ b/site/src/pages/TaskPage/TaskPage.stories.tsx @@ -142,7 +142,7 @@ export const WaitingStartupScripts: Story = { level: "info", output: line, source_id: MockWorkspaceAgentLogSource.id, - created_at: new Date().toISOString(), + created_at: new Date("2024-01-01T12:00:00Z").toISOString(), })), ), }, diff --git a/site/src/pages/TaskPage/TaskPage.tsx b/site/src/pages/TaskPage/TaskPage.tsx index a88742ddf7342..148205fb061bd 100644 --- a/site/src/pages/TaskPage/TaskPage.tsx +++ b/site/src/pages/TaskPage/TaskPage.tsx @@ -17,7 +17,7 @@ import { AgentLogs } from "modules/resources/AgentLogs/AgentLogs"; import { useAgentLogs } from "modules/resources/useAgentLogs"; import { AI_PROMPT_PARAMETER_NAME, type Task } from "modules/tasks/tasks"; import { WorkspaceBuildLogs } from "modules/workspaces/WorkspaceBuildLogs/WorkspaceBuildLogs"; -import { type FC, type ReactNode, useEffect, useRef } from "react"; +import { type FC, type ReactNode, useLayoutEffect, useRef } from "react"; import { Helmet } from "react-helmet-async"; import { useQuery } from "react-query"; import { Panel, PanelGroup, PanelResizeHandle } from "react-resizable-panels"; @@ -192,7 +192,7 @@ const TaskBuildingWorkspace: FC = ({ task }) => { const scrollAreaRef = useRef(null); // biome-ignore lint/correctness/useExhaustiveDependencies: this effect should run when build logs change - useEffect(() => { + useLayoutEffect(() => { if (isChromatic()) { return; } @@ -206,16 +206,16 @@ const TaskBuildingWorkspace: FC = ({ task }) => { }, [buildLogs]); return ( -
+

Starting your workspace

-
+

Your task will be running in a few moments -

+

@@ -250,23 +250,23 @@ const TaskStartingAgent: FC = ({ agent }) => { const logs = useAgentLogs(agent, true); const listRef = useRef(null); - useEffect(() => { + useLayoutEffect(() => { if (listRef.current) { listRef.current.scrollToItem(logs.length - 1, "end"); } }, [logs]); return ( -
+

Running startup scripts

-
+

Your task will be running in a few moments -

+

From 199a13e4ef232f34f30c74567d52eb4d65d480e3 Mon Sep 17 00:00:00 2001 From: Bruno Quaresma Date: Tue, 26 Aug 2025 15:32:58 +0000 Subject: [PATCH 6/6] Fix merges --- site/src/pages/TaskPage/TaskPage.tsx | 12 ++++++------ site/src/pages/TaskPage/TaskTopbar.tsx | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/site/src/pages/TaskPage/TaskPage.tsx b/site/src/pages/TaskPage/TaskPage.tsx index aa8aec27d8cc5..4d84d47fb5ff7 100644 --- a/site/src/pages/TaskPage/TaskPage.tsx +++ b/site/src/pages/TaskPage/TaskPage.tsx @@ -219,6 +219,12 @@ const TaskBuildingWorkspace: FC = ({ task }) => {
+ + = ({ task }) => { logs={buildLogs ?? []} /> - -
diff --git a/site/src/pages/TaskPage/TaskTopbar.tsx b/site/src/pages/TaskPage/TaskTopbar.tsx index 4f51812b4712d..945a9fc179537 100644 --- a/site/src/pages/TaskPage/TaskTopbar.tsx +++ b/site/src/pages/TaskPage/TaskTopbar.tsx @@ -22,7 +22,7 @@ type TaskTopbarProps = { task: Task }; export const TaskTopbar: FC = ({ task }) => { return ( -
+