diff --git a/packages/ui/src/components/ui/sortable-tabs-strip.tsx b/packages/ui/src/components/ui/sortable-tabs-strip.tsx index 70d6ea56..091da0cc 100644 --- a/packages/ui/src/components/ui/sortable-tabs-strip.tsx +++ b/packages/ui/src/components/ui/sortable-tabs-strip.tsx @@ -42,6 +42,7 @@ type SortableTabsStripProps = { activePillInsetClassName?: string; activePillButtonClassName?: string; inactiveTabsIconOnly?: boolean; + iconOnlyActiveTab?: boolean; animateActivePill?: boolean; activePillLowercase?: boolean; className?: string; @@ -94,6 +95,7 @@ export const SortableTabsStrip: React.FC = ({ activePillInsetClassName, activePillButtonClassName, inactiveTabsIconOnly = false, + iconOnlyActiveTab = false, animateActivePill, activePillLowercase = true, className, @@ -367,6 +369,7 @@ export const SortableTabsStrip: React.FC = ({ const isActive = item.id === activeId; const showInactiveIconOnly = inactiveTabsIconOnly && usesActivePillIndicator && !isActive && Boolean(item.icon); const shouldShowLabel = !showInactiveIconOnly; + const shouldShowIcon = Boolean(item.icon) && (!iconOnlyActiveTab || isActive); const useIntrinsicActiveTab = inactiveTabsIconOnly && usesActivePillIndicator && isActive && !isScrollable && !useIntrinsicPillSizing; const closable = item.closable !== false && Boolean(onClose); const closeReplacesIcon = closable && Boolean(item.icon); @@ -436,7 +439,7 @@ export const SortableTabsStrip: React.FC = ({ > {usesActivePillIndicator ? ( <> - {item.icon ? ( + {shouldShowIcon ? ( {item.icon} {closeReplacesIcon ? ( @@ -463,7 +466,7 @@ export const SortableTabsStrip: React.FC = ({ ) : ( - {item.icon ? ( + {shouldShowIcon ? ( - value === 'commit' || value === 'branch' || value === 'pr' || value === 'worktree'; + value === 'commit' || value === 'branch' || value === 'pr'; type GitViewSnapshot = { @@ -526,13 +524,15 @@ export const GitView: React.FC = () => { { id: 'commit', label: t('gitView.tabs.commit'), icon: }, { id: 'branch', label: t('gitView.tabs.update'), icon: }, { id: 'pr', label: t('gitView.tabs.pr'), icon: }, - { id: 'worktree', label: t('gitView.tabs.worktree'), icon: }, ], [t]); const [actionTab, setActionTab] = React.useState(() => { if (typeof window === 'undefined') { return 'commit'; } const stored = window.localStorage.getItem(GIT_ACTION_TAB_STORAGE_KEY); + if (stored === 'worktree') { + return 'branch'; + } return isActionTab(stored) ? stored : 'commit'; }); const [remotes, setRemotes] = React.useState([]); @@ -2143,7 +2143,7 @@ export const GitView: React.FC = () => { return (
- { availableIdentities={availableIdentities} onSelectIdentity={handleApplyIdentity} isApplyingIdentity={isSettingIdentity} - isWorktreeMode={!!worktreeMetadata} - onOpenHistory={() => setIsHistoryDialogOpen(true)} - onOpenStashes={() => setIsStashesDialogOpen(true)} - /> + isWorktreeMode={!!worktreeMetadata} + onOpenHistory={() => setIsHistoryDialogOpen(true)} + actionTabItems={actionTabItems} + activeActionTab={actionTab} + onSelectActionTab={(tabID) => setActionTab(tabID as ActionTab)} + /> {/* In-progress operation banner */} {currentDirectory && ( @@ -2185,18 +2187,6 @@ export const GitView: React.FC = () => {
-
- setActionTab(tabID as ActionTab)} - layoutMode="fit" - variant="active-pill" - inactiveTabsIconOnly={isMobile} - className="h-full" - /> -
- { }} onRevertFile={handleRevertFile} isRevertingAll={isRevertingAll} + onOpenStashes={() => setIsStashesDialogOpen(true)} /> { {actionTab === 'branch' ? (
{canShowBranchWorkflows ? ( - + <> + + {integrateCommitsProps ? ( + { + if (!currentDirectory) return; + fetchStatus(currentDirectory, git); + fetchBranches(currentDirectory, git); + fetchLog(currentDirectory, git, logMaxCountLocal); + }} + /> + ) : null} + ) : (

{t('gitView.branch.actionsUnavailable')}

)}
) : null} - {actionTab === 'worktree' ? ( -
- {integrateCommitsProps ? ( - { - if (!currentDirectory) return; - fetchStatus(currentDirectory, git); - fetchBranches(currentDirectory, git); - fetchLog(currentDirectory, git, logMaxCountLocal); - }} - /> - ) : ( -
-
{t('gitView.integrate.title')}
-
- {t('gitView.worktree.availableInWorktreeMode')} -
-
- )} -
- ) : null} - {actionTab === 'pr' ? (
{pullRequestProps ? ( diff --git a/packages/ui/src/components/views/git/ChangesSection.tsx b/packages/ui/src/components/views/git/ChangesSection.tsx index fd081c8b..afc0484b 100644 --- a/packages/ui/src/components/views/git/ChangesSection.tsx +++ b/packages/ui/src/components/views/git/ChangesSection.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { useVirtualizer } from '@tanstack/react-virtual'; -import { RiFolder3Fill, RiFolderOpenFill } from '@remixicon/react'; +import { RiArchiveStackLine, RiFolder3Fill, RiFolderOpenFill } from '@remixicon/react'; import { Checkbox } from '@/components/ui/checkbox'; import { Button } from '@/components/ui/button'; import { @@ -33,6 +33,7 @@ interface ChangesSectionProps { isRevertingAll?: boolean; maxListHeightClassName?: string; onVisiblePathsChange?: (paths: string[]) => void; + onOpenStashes?: () => void; } const CHANGE_LIST_VIRTUALIZE_THRESHOLD = 120; @@ -183,6 +184,7 @@ export const ChangesSection: React.FC = ({ isRevertingAll = false, maxListHeightClassName, onVisiblePathsChange, + onOpenStashes, }) => { const { t } = useI18n(); const scrollRef = React.useRef(null); @@ -463,6 +465,19 @@ export const ChangesSection: React.FC = ({ {selectedCount}/{totalCount}
) : null} + {onOpenStashes ? ( + + ) : null}
{totalCount > 0 && onRevertAll ? ( diff --git a/packages/ui/src/components/views/git/GitHeader.tsx b/packages/ui/src/components/views/git/GitHeader.tsx index ec4cdac0..d28456bc 100644 --- a/packages/ui/src/components/views/git/GitHeader.tsx +++ b/packages/ui/src/components/views/git/GitHeader.tsx @@ -10,10 +10,10 @@ import { RiCodeLine, RiHeartLine, RiHistoryLine, - RiArchiveStackLine, RiUser3Line, } from '@remixicon/react'; import { Button } from '@/components/ui/button'; +import { SortableTabsStrip, type SortableTabsStripItem } from '@/components/ui/sortable-tabs-strip'; import { DropdownMenu, DropdownMenuContent, @@ -49,7 +49,9 @@ interface GitHeaderProps { isApplyingIdentity: boolean; isWorktreeMode: boolean; onOpenHistory?: () => void; - onOpenStashes?: () => void; + actionTabItems?: SortableTabsStripItem[]; + activeActionTab?: string; + onSelectActionTab?: (tabID: string) => void; } const IDENTITY_ICON_MAP: Record< @@ -208,7 +210,9 @@ export const GitHeader: React.FC = ({ isApplyingIdentity, isWorktreeMode, onOpenHistory, - onOpenStashes, + actionTabItems, + activeActionTab, + onSelectActionTab, }) => { const { t } = useI18n(); if (!status) { @@ -232,16 +236,6 @@ export const GitHeader: React.FC = ({ {t('gitView.history.title')} ) : null} - {onOpenStashes ? ( - - - - - {t('gitView.stashes.title')} - - ) : null}
); @@ -296,15 +290,28 @@ export const GitHeader: React.FC = ({ /> )}
+
+ {managementButtons} + {identityControl} +
-
-
- {syncButtons} - {managementButtons} + {actionTabItems && activeActionTab && onSelectActionTab ? ( +
+
+ +
+
{syncButtons}
-
{identityControl}
-
+ ) : null} ); };