From 20789bcd136d2021cf1cfe0ef4a78fbe19e27879 Mon Sep 17 00:00:00 2001 From: Mohamed Boudra Date: Sun, 2 Aug 2026 21:06:23 +0000 Subject: [PATCH] fix(composer): restore hover-gated track actions Keep row actions mounted but hidden on wide web layouts until the plain row wrapper is hovered. Native and compact layouts keep controls visible, preserving the established subagent behavior in the shared track component. --- .../app/e2e/support/helpers/heartbeats.ts | 1 + packages/app/src/composer/tracks.tsx | 73 +++++++++++-------- 2 files changed, 45 insertions(+), 29 deletions(-) diff --git a/packages/app/e2e/support/helpers/heartbeats.ts b/packages/app/e2e/support/helpers/heartbeats.ts index ae6552d666..3f074473c2 100644 --- a/packages/app/e2e/support/helpers/heartbeats.ts +++ b/packages/app/e2e/support/helpers/heartbeats.ts @@ -166,6 +166,7 @@ export async function removeSeededSubagent(scenario: AgentHeartbeatScenario): Pr export async function deleteHeartbeat(page: Page, heartbeat: SeededHeartbeat): Promise { const row = page.getByRole("link", { name: `${heartbeat.name}, ${heartbeat.cadence}` }); + await row.hover(); page.once("dialog", (dialog) => void dialog.accept()); await page.getByRole("button", { name: `Delete heartbeat ${heartbeat.name}` }).click(); await expect(row).toHaveCount(0, { timeout: 30_000 }); diff --git a/packages/app/src/composer/tracks.tsx b/packages/app/src/composer/tracks.tsx index 587338270b..7e196923e9 100644 --- a/packages/app/src/composer/tracks.tsx +++ b/packages/app/src/composer/tracks.tsx @@ -17,7 +17,8 @@ import { import { ChevronDown, ChevronRight } from "lucide-react-native"; import { StyleSheet, withUnistyles } from "react-native-unistyles"; import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip"; -import { MAX_CONTENT_WIDTH } from "@/constants/layout"; +import { MAX_CONTENT_WIDTH, useIsCompactFormFactor } from "@/constants/layout"; +import { isNative } from "@/constants/platform"; import type { Theme } from "@/styles/theme"; const ThemedChevronDown = withUnistyles(ChevronDown); @@ -166,39 +167,46 @@ export function ComposerTrackRow({ secondary, actions, }: ComposerTrackRowProps): ReactElement { + const isCompact = useIsCompactFormFactor(); const [hovered, setHovered] = useState(false); const [pressed, setPressed] = useState(false); - const handleHoverIn = useCallback(() => setHovered(true), []); - const handleHoverOut = useCallback(() => setHovered(false), []); + const handlePointerEnter = useCallback(() => setHovered(true), []); + const handlePointerLeave = useCallback(() => setHovered(false), []); const handlePressIn = useCallback(() => setPressed(true), []); const handlePressOut = useCallback(() => setPressed(false), []); + const showActions = hovered || isNative || isCompact; return ( - - {() => ( - - - {renderLeading?.()} - - {label} + + + + {renderLeading?.()} + + {label} + + {secondary ? ( + + {secondary} - {secondary ? ( - - {secondary} - - ) : null} - - {actions ? {actions()} : null} - - )} - + ) : null} + + {actions ? ( + + {actions()} + + ) : null} + + ); } @@ -350,10 +358,17 @@ const styles = StyleSheet.create((theme) => ({ fontSize: theme.fontSize.xs, color: theme.colors.foregroundMuted, }, - actionCluster: { + actionClusterVisible: { flexDirection: "row", alignItems: "center", gap: theme.spacing[1], + opacity: 1, + }, + actionClusterHidden: { + flexDirection: "row", + alignItems: "center", + gap: theme.spacing[1], + opacity: 0, }, actionButton: { padding: theme.spacing[1],