From 20fa1a3a3bbb378a706cef925d510184de7b124d Mon Sep 17 00:00:00 2001 From: Mohamed Boudra Date: Thu, 19 Mar 2026 15:45:29 +0700 Subject: [PATCH] Fix workspace tab presentation hook ordering --- .../app/src/components/split-container.tsx | 60 ++++---- .../workspace/workspace-desktop-tabs-row.tsx | 76 +++++----- .../screens/workspace/workspace-screen.tsx | 139 +++++++++--------- .../workspace/workspace-tab-presentation.tsx | 59 ++++++-- 4 files changed, 193 insertions(+), 141 deletions(-) diff --git a/packages/app/src/components/split-container.tsx b/packages/app/src/components/split-container.tsx index f596e36a0..75c56ebd0 100644 --- a/packages/app/src/components/split-container.tsx +++ b/packages/app/src/components/split-container.tsx @@ -41,7 +41,7 @@ import { type WorkspaceDesktopTabRowItem, } from "@/screens/workspace/workspace-desktop-tabs-row"; import { - useWorkspaceTabPresentation, + WorkspaceTabPresentationResolver, WorkspaceTabIcon, } from "@/screens/workspace/workspace-tab-presentation"; import type { WorkspaceTabDescriptor } from "@/screens/workspace/workspace-tabs-types"; @@ -489,35 +489,41 @@ function DragOverlayTabChipInner({ normalizedWorkspaceId: string; }) { const { theme } = useUnistyles(); - const presentation = useWorkspaceTabPresentation({ - tab, - serverId: normalizedServerId, - workspaceId: normalizedWorkspaceId, - }); - const label = - presentation.titleState === "loading" ? "Loading..." : presentation.label; return ( - - - - {label} - - + {(presentation) => { + const label = + presentation.titleState === "loading" ? "Loading..." : presentation.label; + + return ( + + + + {label} + + + ); + }} + ); } diff --git a/packages/app/src/screens/workspace/workspace-desktop-tabs-row.tsx b/packages/app/src/screens/workspace/workspace-desktop-tabs-row.tsx index f06c42320..6370cef31 100644 --- a/packages/app/src/screens/workspace/workspace-desktop-tabs-row.tsx +++ b/packages/app/src/screens/workspace/workspace-desktop-tabs-row.tsx @@ -15,7 +15,7 @@ import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip import { WORKSPACE_SECONDARY_HEADER_HEIGHT } from "@/constants/layout"; import { useWorkspaceTabLayout } from "@/screens/workspace/use-workspace-tab-layout"; import { - useWorkspaceTabPresentation, + WorkspaceTabPresentationResolver, WorkspaceTabIcon, type WorkspaceTabPresentation, } from "@/screens/workspace/workspace-tab-presentation"; @@ -546,13 +546,6 @@ function ResolvedDesktopTabChip({ showDropIndicatorBefore: boolean; showDropIndicatorAfter: boolean; }) { - const presentation = useWorkspaceTabPresentation({ - tab: item.tab, - serverId: normalizedServerId, - workspaceId: normalizedWorkspaceId, - }); - const tooltipLabel = - presentation.titleState === "loading" ? "Loading agent title" : presentation.label; const resolvedTab = useMemo( () => buildWorkspaceDesktopTabActions({ @@ -580,33 +573,46 @@ function ResolvedDesktopTabChip({ ); return ( - - {showDropIndicatorBefore ? ( - - ) : null} - - {showDropIndicatorAfter ? ( - - ) : null} - + + {(presentation) => { + const tooltipLabel = + presentation.titleState === "loading" ? "Loading agent title" : presentation.label; + + return ( + + {showDropIndicatorBefore ? ( + + ) : null} + + {showDropIndicatorAfter ? ( + + ) : null} + + ); + }} + ); } diff --git a/packages/app/src/screens/workspace/workspace-screen.tsx b/packages/app/src/screens/workspace/workspace-screen.tsx index 3195edeaa..5254a2d6c 100644 --- a/packages/app/src/screens/workspace/workspace-screen.tsx +++ b/packages/app/src/screens/workspace/workspace-screen.tsx @@ -83,7 +83,7 @@ import { useArchiveAgent } from "@/hooks/use-archive-agent"; import { buildProviderCommand } from "@/utils/provider-command-templates"; import { generateDraftId } from "@/stores/draft-keys"; import { - useWorkspaceTabPresentation, + WorkspaceTabPresentationResolver, WorkspaceTabIcon, WorkspaceTabOptionRow, } from "@/screens/workspace/workspace-tab-presentation"; @@ -226,22 +226,24 @@ function ResolvedMobileActiveTabTrigger({ normalizedServerId: string; normalizedWorkspaceId: string; }) { - const presentation = useWorkspaceTabPresentation({ - tab: activeTab, - serverId: normalizedServerId, - workspaceId: normalizedWorkspaceId, - }); - return ( - <> - - - + + {(presentation) => ( + <> + + + - - {presentation.titleState === "loading" ? "Loading..." : presentation.label} - - + + {presentation.titleState === "loading" ? "Loading..." : presentation.label} + + + )} + ); } @@ -277,11 +279,6 @@ function MobileWorkspaceTabOption({ onCloseOtherTabs: (tabId: string) => Promise | void; }) { const { theme } = useUnistyles(); - const presentation = useWorkspaceTabPresentation({ - tab, - serverId: normalizedServerId, - workspaceId: normalizedWorkspaceId, - }); const menuTestIDBase = `workspace-tab-menu-${tab.key}`; const menuEntries = buildWorkspaceTabMenuEntries({ surface: "mobile", @@ -298,53 +295,61 @@ function MobileWorkspaceTabOption({ }); return ( - - [ - styles.mobileTabMenuTrigger, - (open || pressed) && styles.mobileTabMenuTriggerActive, - ]} - > - - - - {menuEntries.map((entry) => - entry.kind === "separator" ? ( - - ) : ( - - {entry.label} - - ) - )} - - - } - /> + + {(presentation) => ( + + [ + styles.mobileTabMenuTrigger, + (open || pressed) && styles.mobileTabMenuTriggerActive, + ]} + > + + + + {menuEntries.map((entry) => + entry.kind === "separator" ? ( + + ) : ( + + {entry.label} + + ) + )} + + + } + /> + )} + ); } diff --git a/packages/app/src/screens/workspace/workspace-tab-presentation.tsx b/packages/app/src/screens/workspace/workspace-tab-presentation.tsx index 550087b82..d0c821ff1 100644 --- a/packages/app/src/screens/workspace/workspace-tab-presentation.tsx +++ b/packages/app/src/screens/workspace/workspace-tab-presentation.tsx @@ -21,23 +21,56 @@ export interface WorkspaceTabPresentation { statusBucket: SidebarStateBucket | null; } -export function useWorkspaceTabPresentation(input: { +type WorkspaceTabPresentationResolverProps = { tab: WorkspaceTabDescriptor; serverId: string; workspaceId: string; -}): WorkspaceTabPresentation { + children: (presentation: WorkspaceTabPresentation) => ReactNode; +}; + +type WorkspaceTabPresentationResolverInnerProps = WorkspaceTabPresentationResolverProps & { + registration: NonNullable>; +}; + +export function WorkspaceTabPresentationResolver({ + tab, + serverId, + workspaceId, + children, +}: WorkspaceTabPresentationResolverProps): ReactElement { ensurePanelsRegistered(); - const registration = getPanelRegistration(input.tab.kind); - invariant(registration, `No panel registration for kind: ${input.tab.kind}`); - const descriptor = registration.useDescriptor(input.tab.target, { - serverId: input.serverId, - workspaceId: input.workspaceId, + const registration = getPanelRegistration(tab.kind); + invariant(registration, `No panel registration for kind: ${tab.kind}`); + + return ( + + {children} + + ); +} + +function WorkspaceTabPresentationResolverInner({ + registration, + tab, + serverId, + workspaceId, + children, +}: WorkspaceTabPresentationResolverInnerProps): ReactElement { + const descriptor = registration.useDescriptor(tab.target as never, { + serverId, + workspaceId, }); - return useMemo( + const presentation = useMemo( () => ({ - key: input.tab.key, - kind: input.tab.kind, + key: tab.key, + kind: tab.kind, label: descriptor.label, subtitle: descriptor.subtitle, titleState: descriptor.titleState, @@ -50,10 +83,12 @@ export function useWorkspaceTabPresentation(input: { descriptor.statusBucket, descriptor.subtitle, descriptor.titleState, - input.tab.key, - input.tab.kind, + tab.key, + tab.kind, ] ); + + return <>{children(presentation)}; } type WorkspaceTabIconProps = {