From cf2cd1bd2f396ba463b5001a88166ae5a1a17c28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Flor=C3=AAncio?= Date: Fri, 29 May 2026 11:08:42 +0100 Subject: [PATCH] feat(web): unify panel loading states with grid spinner (#1142) --- apps/web/components/panel-loading-state.tsx | 30 +++++++++++++++++++ .../task/file-browser-load-state.tsx | 14 ++------- .../components/task/passthrough-terminal.tsx | 18 ++++------- .../components/task/preview-session-tabs.tsx | 12 ++------ 4 files changed, 41 insertions(+), 33 deletions(-) create mode 100644 apps/web/components/panel-loading-state.tsx diff --git a/apps/web/components/panel-loading-state.tsx b/apps/web/components/panel-loading-state.tsx new file mode 100644 index 0000000000..7c9cb32e07 --- /dev/null +++ b/apps/web/components/panel-loading-state.tsx @@ -0,0 +1,30 @@ +"use client"; + +import { GridSpinner } from "@/components/grid-spinner"; +import { cn } from "@/lib/utils"; + +type PanelLoadingStateProps = { + label: string; + /** Override the outer container layout (e.g. absolute overlay for the terminal). */ + className?: string; + testId?: string; +}; + +/** + * Shared panel loading placeholder: a grid spinner above a label, matching the + * terminal's "Connecting terminal..." look. Used by the file browser, terminal, + * and agent preview panels so loading states stay visually consistent. + */ +export function PanelLoadingState({ label, className, testId }: PanelLoadingStateProps) { + return ( +
+
+ + {label} +
+
+ ); +} diff --git a/apps/web/components/task/file-browser-load-state.tsx b/apps/web/components/task/file-browser-load-state.tsx index 7d64be0bf6..4bb63013fe 100644 --- a/apps/web/components/task/file-browser-load-state.tsx +++ b/apps/web/components/task/file-browser-load-state.tsx @@ -1,6 +1,6 @@ "use client"; -import { IconLoader2 } from "@tabler/icons-react"; +import { PanelLoadingState } from "@/components/panel-loading-state"; import type { FileTreeNode } from "@/lib/types/backend"; type RenderSessionOrLoadStateInput = { @@ -31,18 +31,10 @@ export function renderSessionOrLoadState({ ); } if ((loadState === "loading" || isLoadingTree) && !tree) { - return
Loading files...
; + return ; } if (loadState === "waiting") { - return ( -
- - Preparing workspace... -
- ); + return ; } if (loadState === "manual") { return ( diff --git a/apps/web/components/task/passthrough-terminal.tsx b/apps/web/components/task/passthrough-terminal.tsx index da50d2fd58..2ee57586f6 100644 --- a/apps/web/components/task/passthrough-terminal.tsx +++ b/apps/web/components/task/passthrough-terminal.tsx @@ -6,7 +6,7 @@ import { FitAddon } from "@xterm/addon-fit"; import { AttachAddon } from "@xterm/addon-attach"; import { WebglAddon } from "@xterm/addon-webgl"; import "@xterm/xterm/css/xterm.css"; -import { GridSpinner } from "@/components/grid-spinner"; +import { PanelLoadingState } from "@/components/panel-loading-state"; import { useAppStore } from "@/components/state-provider"; import { useSession } from "@/hooks/domains/session/use-session"; import { useSessionAgentctl } from "@/hooks/domains/session/use-session-agentctl"; @@ -277,17 +277,11 @@ export function PassthroughTerminal(props: PassthroughTerminalProps) { {!isConnected && ( -
-
- - - {mode === "agent" ? "Preparing workspace..." : "Connecting terminal..."} - -
-
+ )} ); diff --git a/apps/web/components/task/preview-session-tabs.tsx b/apps/web/components/task/preview-session-tabs.tsx index 14a23be223..1ec72aee85 100644 --- a/apps/web/components/task/preview-session-tabs.tsx +++ b/apps/web/components/task/preview-session-tabs.tsx @@ -1,10 +1,10 @@ "use client"; import { useCallback, useMemo } from "react"; -import { IconLoader2 } from "@tabler/icons-react"; import { Button } from "@kandev/ui/button"; import { AgentLogo } from "@/components/agent-logo"; import { GridSpinner } from "@/components/grid-spinner"; +import { PanelLoadingState } from "@/components/panel-loading-state"; import { SessionTabs, type SessionTab } from "@/components/session-tabs"; import { useAppStore } from "@/components/state-provider"; import { useToast } from "@/components/toast-provider"; @@ -177,15 +177,7 @@ function RunningSpinner() { } function PreviewLoadingState({ label }: { label: string }) { - return ( -
- - {label} -
- ); + return ; } function PreviewEmptyState() {