Fix workspace tab presentation hook ordering

This commit is contained in:
Mohamed Boudra
2026-03-19 15:45:29 +07:00
parent 7609ccee4c
commit 20fa1a3a3b
4 changed files with 193 additions and 141 deletions
@@ -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,11 +489,14 @@ function DragOverlayTabChipInner({
normalizedWorkspaceId: string;
}) {
const { theme } = useUnistyles();
const presentation = useWorkspaceTabPresentation({
tab,
serverId: normalizedServerId,
workspaceId: normalizedWorkspaceId,
});
return (
<WorkspaceTabPresentationResolver
tab={tab}
serverId={normalizedServerId}
workspaceId={normalizedWorkspaceId}
>
{(presentation) => {
const label =
presentation.titleState === "loading" ? "Loading..." : presentation.label;
@@ -519,6 +522,9 @@ function DragOverlayTabChipInner({
</Text>
</View>
);
}}
</WorkspaceTabPresentationResolver>
);
}
function SplitNodeView({
@@ -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({
@@ -579,6 +572,16 @@ function ResolvedDesktopTabChip({
]
);
return (
<WorkspaceTabPresentationResolver
tab={item.tab}
serverId={normalizedServerId}
workspaceId={normalizedWorkspaceId}
>
{(presentation) => {
const tooltipLabel =
presentation.titleState === "loading" ? "Loading agent title" : presentation.label;
return (
<View style={styles.tabSlot}>
{showDropIndicatorBefore ? (
@@ -608,6 +611,9 @@ function ResolvedDesktopTabChip({
) : null}
</View>
);
}}
</WorkspaceTabPresentationResolver>
);
}
const styles = StyleSheet.create((theme) => ({
@@ -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,13 +226,13 @@ function ResolvedMobileActiveTabTrigger({
normalizedServerId: string;
normalizedWorkspaceId: string;
}) {
const presentation = useWorkspaceTabPresentation({
tab: activeTab,
serverId: normalizedServerId,
workspaceId: normalizedWorkspaceId,
});
return (
<WorkspaceTabPresentationResolver
tab={activeTab}
serverId={normalizedServerId}
workspaceId={normalizedWorkspaceId}
>
{(presentation) => (
<>
<View style={styles.switcherTriggerIcon} testID="workspace-active-tab-icon">
<WorkspaceTabIcon presentation={presentation} active />
@@ -242,6 +242,8 @@ function ResolvedMobileActiveTabTrigger({
{presentation.titleState === "loading" ? "Loading..." : presentation.label}
</Text>
</>
)}
</WorkspaceTabPresentationResolver>
);
}
@@ -277,11 +279,6 @@ function MobileWorkspaceTabOption({
onCloseOtherTabs: (tabId: string) => Promise<void> | 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,6 +295,12 @@ function MobileWorkspaceTabOption({
});
return (
<WorkspaceTabPresentationResolver
tab={tab}
serverId={normalizedServerId}
workspaceId={normalizedWorkspaceId}
>
{(presentation) => (
<WorkspaceTabOptionRow
presentation={presentation}
selected={selected}
@@ -345,6 +348,8 @@ function MobileWorkspaceTabOption({
</DropdownMenu>
}
/>
)}
</WorkspaceTabPresentationResolver>
);
}
@@ -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<ReturnType<typeof getPanelRegistration>>;
};
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 (
<WorkspaceTabPresentationResolverInner
key={tab.kind}
registration={registration}
tab={tab}
serverId={serverId}
workspaceId={workspaceId}
>
{children}
</WorkspaceTabPresentationResolverInner>
);
}
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 = {