From 074ff79af7959f7cec2bab502f72c2beefa42334 Mon Sep 17 00:00:00 2001 From: Jake Howell Date: Thu, 16 Apr 2026 22:06:36 +1000 Subject: [PATCH] fix: restore kebab menu flex (#24359) Agent log tabs could spill over the Copy/Download area, and the overflow kebab sometimes never wired up because ResizeObserver ran with no node or while inactive. This ties the hook to when the strip is real and clips the tab column. - `AgentRow`: `enabled` for `useKebabMenu` is `hasStartupFeatures && hasAnyLogs && showLogs`; `hasAnyLogs` is computed before the hook; tab list wrapper gets `overflow-hidden`. - `useKebabMenu`: attach `ResizeObserver` in `useLayoutEffect`; bail if missing container or `!enabled || !isActive`; deps include `enabled` and `isActive`. - `TabsList` (`overflowKebabMenu`): add `min-w-0 w-full max-w-full` with `flex-nowrap` so the list stays inside the flex column. --- site/src/components/Tabs/Tabs.tsx | 2 +- site/src/components/Tabs/utils/useKebabMenu.ts | 7 ++++--- site/src/modules/resources/AgentRow.tsx | 7 ++++--- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/site/src/components/Tabs/Tabs.tsx b/site/src/components/Tabs/Tabs.tsx index 1cb5eb686a..c03b24adb4 100644 --- a/site/src/components/Tabs/Tabs.tsx +++ b/site/src/components/Tabs/Tabs.tsx @@ -64,7 +64,7 @@ export const TabsList: FC = ({ data-slot="tabs-list" className={cn( tabsListVariants({ variant }), - overflowKebabMenu && "flex-nowrap", + overflowKebabMenu && "min-w-0 w-full max-w-full flex-nowrap", className, )} {...props} diff --git a/site/src/components/Tabs/utils/useKebabMenu.ts b/site/src/components/Tabs/utils/useKebabMenu.ts index a1f9045ce0..aa6336d47d 100644 --- a/site/src/components/Tabs/utils/useKebabMenu.ts +++ b/site/src/components/Tabs/utils/useKebabMenu.ts @@ -2,6 +2,7 @@ import { type RefObject, useCallback, useEffect, + useLayoutEffect, useRef, useState, } from "react"; @@ -105,9 +106,9 @@ export const useKebabMenu = ({ recalculateOverflow(availableWidthRef.current); }, [recalculateOverflow, tabs]); - useEffect(() => { + useLayoutEffect(() => { const container = containerRef.current; - if (!container) { + if (!container || !enabled || !isActive) { return; } @@ -121,7 +122,7 @@ export const useKebabMenu = ({ }); observer.observe(container); return () => observer.disconnect(); - }, [recalculateOverflow]); + }, [recalculateOverflow, enabled, isActive]); const overflowTabValuesSet = new Set(overflowTabValues); const { visibleTabs, overflowTabs } = tabs.reduce<{ diff --git a/site/src/modules/resources/AgentRow.tsx b/site/src/modules/resources/AgentRow.tsx index da50afde07..52ae1e1d04 100644 --- a/site/src/modules/resources/AgentRow.tsx +++ b/site/src/modules/resources/AgentRow.tsx @@ -260,6 +260,8 @@ export const AgentRow: FC = ({ ...(startupScriptLogTab ? [startupScriptLogTab] : []), ...sortedSourceLogTabs, ]; + const hasAnyLogs = agentLogs.length > 0; + const logTabsMeasureEnabled = hasStartupFeatures && hasAnyLogs && showLogs; const { containerRef: logTabsListContainerRef, visibleTabs: visibleLogTabs, @@ -267,7 +269,7 @@ export const AgentRow: FC = ({ getTabMeasureProps, } = useKebabMenu({ tabs: logTabs, - enabled: true, + enabled: logTabsMeasureEnabled, isActive: showLogs, }); const overflowLogTabValuesSet = new Set( @@ -287,7 +289,6 @@ export const AgentRow: FC = ({ const allLogsText = agentLogs.map((log) => log.output).join("\n"); const selectedLogsText = selectedLogs.map((log) => log.output).join("\n"); const hasSelectedLogs = selectedLogs.length > 0; - const hasAnyLogs = agentLogs.length > 0; const { showCopiedSuccess, copyToClipboard } = useClipboard(); const downloadableLogSets = logTabs .filter((tab) => tab.value !== "all") @@ -498,7 +499,7 @@ export const AgentRow: FC = ({
{visibleLogTabs.map((tab) => (