auto-claude: subtask-5-2 - Add pop-out button to ProjectTabBar component

Changes:
- Added pop-out button to SortableProjectTab alongside settings button
- Uses ExternalLink icon from lucide-react
- Integrated with window store to track popped-out projects
- Added translation keys in both English and French
- Button disabled when project is already popped out
- Calls window.electronAPI.window.popOutProject() IPC channel

Co-Authored-By: Claude Sonnet 4.5 <[email protected]>
This commit is contained in:
AndyMik90
2026-02-15 15:32:44 +01:00
co-authored by Claude Sonnet 4.5
parent 2da3f84068
commit aba2c325b6
4 changed files with 63 additions and 5 deletions
@@ -6,6 +6,7 @@ import { Button } from './ui/button';
import { SortableProjectTab } from './SortableProjectTab';
import { UsageIndicator } from './UsageIndicator';
import { AuthStatusIndicator } from './AuthStatusIndicator';
import { useWindowStore } from '../stores/window-store';
import type { Project } from '../../shared/types';
interface ProjectTabBarProps {
@@ -29,6 +30,22 @@ export function ProjectTabBar({
onSettingsClick
}: ProjectTabBarProps) {
const { t } = useTranslation('common');
const { isProjectPoppedOut, setWindowLoading, addPoppedOutProject } = useWindowStore();
// Handler for popping out a project into a new window
const handlePopOutProject = async (projectId: string) => {
try {
setWindowLoading(projectId, true);
const { windowId } = await window.electronAPI.window.popOutProject(projectId);
addPoppedOutProject(projectId);
console.log(`Project ${projectId} popped out to window ${windowId}`);
} catch (error) {
console.error('Failed to pop out project:', error);
// TODO: Show error notification to user
} finally {
setWindowLoading(projectId, false);
}
};
// Keyboard shortcuts for tab navigation
useEffect(() => {
@@ -107,6 +124,8 @@ export function ProjectTabBar({
}}
// Pass control props only for active tab
onSettingsClick={isActiveTab ? onSettingsClick : undefined}
onPopOutClick={isActiveTab ? () => handlePopOutProject(project.id) : undefined}
isPoppedOut={isProjectPoppedOut(project.id)}
/>
);
})}
@@ -1,7 +1,7 @@
import { useSortable } from '@dnd-kit/sortable';
import { CSS } from '@dnd-kit/utilities';
import { useTranslation } from 'react-i18next';
import { Settings2 } from 'lucide-react';
import { Settings2, ExternalLink } from 'lucide-react';
import { cn } from '../lib/utils';
import { Tooltip, TooltipContent, TooltipTrigger } from './ui/tooltip';
import type { Project } from '../../shared/types';
@@ -15,6 +15,8 @@ interface SortableProjectTabProps {
onClose: (e: React.MouseEvent) => void;
// Optional control props for active tab
onSettingsClick?: () => void;
onPopOutClick?: () => void;
isPoppedOut?: boolean;
}
// Detect if running on macOS for keyboard shortcut display
@@ -28,7 +30,9 @@ export function SortableProjectTab({
tabIndex,
onSelect,
onClose,
onSettingsClick
onSettingsClick,
onPopOutClick,
isPoppedOut = false
}: SortableProjectTabProps) {
const { t } = useTranslation('common');
// Build tooltip with keyboard shortcut hint (only for tabs 1-9)
@@ -112,9 +116,40 @@ export function SortableProjectTab({
</TooltipContent>
</Tooltip>
{/* Active tab controls - settings and archive, always accessible */}
{/* Active tab controls - settings, pop-out, always accessible */}
{isActive && (
<div className="flex items-center gap-0.5 mr-0.5 sm:mr-1 flex-shrink-0">
{/* Pop-out icon - responsive sizing */}
{onPopOutClick && (
<Tooltip delayDuration={200}>
<TooltipTrigger asChild>
<button
type="button"
className={cn(
'h-5 w-5 sm:h-6 sm:w-6 p-0 rounded',
'flex items-center justify-center',
'text-muted-foreground hover:text-foreground',
'hover:bg-muted/50 transition-colors',
'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1',
isPoppedOut && 'opacity-50 cursor-not-allowed'
)}
onClick={(e) => {
e.stopPropagation();
if (!isPoppedOut) {
onPopOutClick();
}
}}
disabled={isPoppedOut}
aria-label={t('projectTab.popOutAriaLabel')}
>
<ExternalLink className="h-3 w-3 sm:h-3.5 sm:w-3.5" />
</button>
</TooltipTrigger>
<TooltipContent side="bottom">
<span>{t('projectTab.popOut')}</span>
</TooltipContent>
</Tooltip>
)}
{/* Settings icon - responsive sizing */}
{onSettingsClick && (
<Tooltip delayDuration={200}>
@@ -7,7 +7,9 @@
"hideArchivedTasks": "Hide archived tasks",
"closeTab": "Close tab",
"closeTabAriaLabel": "Close tab (removes project from app)",
"addProjectAriaLabel": "Add project"
"addProjectAriaLabel": "Add project",
"popOut": "Pop out project",
"popOutAriaLabel": "Pop out project into separate window"
},
"accessibility": {
"deleteFeatureAriaLabel": "Delete feature",
@@ -7,7 +7,9 @@
"hideArchivedTasks": "Masquer les tâches archivées",
"closeTab": "Fermer l'onglet",
"closeTabAriaLabel": "Fermer l'onglet (retire le projet de l'application)",
"addProjectAriaLabel": "Ajouter un projet"
"addProjectAriaLabel": "Ajouter un projet",
"popOut": "Ouvrir dans une fenêtre séparée",
"popOutAriaLabel": "Ouvrir le projet dans une fenêtre séparée"
},
"accessibility": {
"deleteFeatureAriaLabel": "Supprimer la fonctionnalité",