From e57f66ccd38728fdd95466d0ead76b25472cee2d Mon Sep 17 00:00:00 2001 From: Bohdan Triapitsyn Date: Wed, 20 May 2026 23:22:09 +0300 Subject: [PATCH] refactor(ui): trim header trigger labels and fix dropdown anchoring OpenInAppButton and ProjectActionsButton now render icon-only triggers in the header instead of icon + text. Padding is tightened accordingly and the now-unused selectedButtonLabel / formatActionButtonLabel helpers are gone. With narrower triggers the previous center-aligned dropdown with translate -30px started overflowing the viewport on the left edge for ProjectActions. Switched ProjectActions dropdown to align="start" and OpenInApp dropdown to align="end" (matching their positions in the header) and removed the manual translate offset. Also fixed a long-standing inconsistency where clicking an already-running action in the ProjectActions dropdown re-ran it instead of stopping; the non-compact branch was missing toggleStopIfRunning=true that the compact branch already passed. MiniChat header now uses bg-sidebar to match the main desktop header palette. --- .../components/desktop/OpenInAppButton.tsx | 11 +++---- .../layout/ProjectActionsButton.tsx | 33 +++---------------- .../components/mini-chat/MiniChatLayout.tsx | 2 +- 3 files changed, 9 insertions(+), 37 deletions(-) diff --git a/packages/ui/src/components/desktop/OpenInAppButton.tsx b/packages/ui/src/components/desktop/OpenInAppButton.tsx index f890fb70..dc7ae966 100644 --- a/packages/ui/src/components/desktop/OpenInAppButton.tsx +++ b/packages/ui/src/components/desktop/OpenInAppButton.tsx @@ -142,8 +142,9 @@ export const OpenInAppButton = ({ directory, className }: OpenInAppButtonProps) type="button" onClick={() => void handleOpen(selectedApp)} className={cn( - 'inline-flex h-full items-center gap-2 px-3 typography-ui-label font-medium', - 'text-foreground hover:bg-interactive-hover transition-colors' + 'inline-flex h-full items-center px-2.5 typography-ui-label font-medium', + 'text-foreground hover:bg-interactive-hover transition-colors', + isScanning && 'animate-pulse' )} aria-label={t('openInApp.actions.openInAria', { app: selectedApp.label })} > @@ -152,9 +153,6 @@ export const OpenInAppButton = ({ directory, className }: OpenInAppButtonProps) iconDataUrl={selectedApp.iconDataUrl} fallbackIconDataUrl={selectedApp.fallbackIconDataUrl} /> - - {t('openInApp.actions.open')} - @@ -171,9 +169,8 @@ export const OpenInAppButton = ({ directory, className }: OpenInAppButtonProps) void handleCopyPath()}> diff --git a/packages/ui/src/components/layout/ProjectActionsButton.tsx b/packages/ui/src/components/layout/ProjectActionsButton.tsx index 54b08b75..5821babb 100644 --- a/packages/ui/src/components/layout/ProjectActionsButton.tsx +++ b/packages/ui/src/components/layout/ProjectActionsButton.tsx @@ -146,26 +146,6 @@ const extractBestUrl = (value: string): string | null => { return normalized[0] ?? null; }; -const formatActionButtonLabel = (value: string, fallbackLabel: string): string => { - const trimmed = value.trim(); - if (!trimmed) { - return fallbackLabel; - } - - const words = trimmed.split(/\s+/).filter(Boolean); - if (words.length >= 2) { - const first = words[0]; - const second = words[1].slice(0, 3); - const shortTwoWord = `${first} ${second}`.trim(); - if (words.length > 2 || shortTwoWord.length < trimmed.length) { - return `${shortTwoWord}...`; - } - return shortTwoWord; - } - - return trimmed.length > 12 ? `${trimmed.slice(0, 9).trimEnd()}...` : trimmed; -}; - export const ProjectActionsButton = ({ projectRef, directory, @@ -732,10 +712,6 @@ export const ProjectActionsButton = ({ const selectedIconName = resolvedSelected.id === AUTO_DISCOVER_ACTION_ID ? 'search' : PROJECT_ACTION_ICON_MAP[selectedIconKey] || 'play'; - const selectedButtonLabel = formatActionButtonLabel( - resolvedSelected.name, - t('projectActions.label.fallbackAction'), - ); const selectedRunKey = toProjectActionRunKey(normalizedDirectory, resolvedSelected.id); const selectedRunning = projectActionRuns[selectedRunKey]; const isStoppingSelected = selectedRunning?.status === 'stopping'; @@ -855,8 +831,8 @@ export const ProjectActionsButton = ({ onClick={handlePrimaryClick} disabled={isLoading || isStoppingSelected} className={cn( - 'inline-flex h-full items-center typography-ui-label font-medium text-foreground hover:bg-interactive-hover', - compact ? 'w-9 justify-center px-0' : 'gap-2 px-3', + 'inline-flex h-full items-center justify-center typography-ui-label font-medium text-foreground hover:bg-interactive-hover', + compact ? 'w-9 px-0' : 'px-2.5', 'transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary disabled:cursor-not-allowed' )} aria-label={selectedRunning @@ -870,7 +846,6 @@ export const ProjectActionsButton = ({ ? : } - {!compact ? {selectedButtonLabel} : null} {showSelectedPreviewButton ? ( @@ -907,7 +882,7 @@ export const ProjectActionsButton = ({ - + {t('projectActions.actions.addNewAction')} @@ -928,7 +903,7 @@ export const ProjectActionsButton = ({ key={entry.id} className="flex items-center gap-2" onClick={() => { - handleSelectAction(entry); + handleSelectAction(entry, true); }} > diff --git a/packages/ui/src/components/mini-chat/MiniChatLayout.tsx b/packages/ui/src/components/mini-chat/MiniChatLayout.tsx index 825cc250..5d5002c2 100644 --- a/packages/ui/src/components/mini-chat/MiniChatLayout.tsx +++ b/packages/ui/src/components/mini-chat/MiniChatLayout.tsx @@ -256,7 +256,7 @@ const MiniChatHeader: React.FC<{ mode: MiniChatMode }> = ({ mode }) => { return (