Merge remote-tracking branch 'origin/v0.14.0rc' into feature/bug-complete-fix-for-the-plan-mode-system-inside-sbyt

Resolved conflicts in dialog components by keeping simplified code
without modelSupportsPlanningMode conditional (always true now).
This commit is contained in:
Shirone
2026-01-25 13:41:04 +01:00
24 changed files with 1197 additions and 1245 deletions

View File

@@ -7,6 +7,7 @@ import { useSettingsSync } from './hooks/use-settings-sync';
import { useCursorStatusInit } from './hooks/use-cursor-status-init'; import { useCursorStatusInit } from './hooks/use-cursor-status-init';
import { useProviderAuthInit } from './hooks/use-provider-auth-init'; import { useProviderAuthInit } from './hooks/use-provider-auth-init';
import { useAppStore } from './store/app-store'; import { useAppStore } from './store/app-store';
import { TooltipProvider } from '@/components/ui/tooltip';
import './styles/global.css'; import './styles/global.css';
import './styles/theme-imports'; import './styles/theme-imports';
import './styles/font-imports'; import './styles/font-imports';
@@ -75,9 +76,9 @@ export default function App() {
}, []); }, []);
return ( return (
<> <TooltipProvider delayDuration={300}>
<RouterProvider router={router} /> <RouterProvider router={router} />
{showSplash && !disableSplashScreen && <SplashScreen onComplete={handleSplashComplete} />} {showSplash && !disableSplashScreen && <SplashScreen onComplete={handleSplashComplete} />}
</> </TooltipProvider>
); );
} }

View File

@@ -5,7 +5,7 @@ import { formatShortcut } from '@/store/app-store';
import { Activity, Settings, BookOpen, MessageSquare, ExternalLink } from 'lucide-react'; import { Activity, Settings, BookOpen, MessageSquare, ExternalLink } from 'lucide-react';
import { useOSDetection } from '@/hooks/use-os-detection'; import { useOSDetection } from '@/hooks/use-os-detection';
import { getElectronAPI } from '@/lib/electron'; import { getElectronAPI } from '@/lib/electron';
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip'; import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip';
function getOSAbbreviation(os: string): string { function getOSAbbreviation(os: string): string {
switch (os) { switch (os) {
@@ -72,7 +72,6 @@ export function SidebarFooter({
<div className="flex flex-col items-center py-2 px-2 gap-1"> <div className="flex flex-col items-center py-2 px-2 gap-1">
{/* Running Agents */} {/* Running Agents */}
{!hideRunningAgents && ( {!hideRunningAgents && (
<TooltipProvider delayDuration={0}>
<Tooltip> <Tooltip>
<TooltipTrigger asChild> <TooltipTrigger asChild>
<button <button
@@ -121,11 +120,9 @@ export function SidebarFooter({
)} )}
</TooltipContent> </TooltipContent>
</Tooltip> </Tooltip>
</TooltipProvider>
)} )}
{/* Settings */} {/* Settings */}
<TooltipProvider delayDuration={0}>
<Tooltip> <Tooltip>
<TooltipTrigger asChild> <TooltipTrigger asChild>
<button <button
@@ -147,10 +144,7 @@ export function SidebarFooter({
data-testid="settings-button" data-testid="settings-button"
> >
<Settings <Settings
className={cn( className={cn('w-[18px] h-[18px]', isActiveRoute('settings') && 'text-brand-500')}
'w-[18px] h-[18px]',
isActiveRoute('settings') && 'text-brand-500'
)}
/> />
</button> </button>
</TooltipTrigger> </TooltipTrigger>
@@ -161,11 +155,9 @@ export function SidebarFooter({
</span> </span>
</TooltipContent> </TooltipContent>
</Tooltip> </Tooltip>
</TooltipProvider>
{/* Documentation */} {/* Documentation */}
{!hideWiki && ( {!hideWiki && (
<TooltipProvider delayDuration={0}>
<Tooltip> <Tooltip>
<TooltipTrigger asChild> <TooltipTrigger asChild>
<button <button
@@ -185,11 +177,9 @@ export function SidebarFooter({
Documentation Documentation
</TooltipContent> </TooltipContent>
</Tooltip> </Tooltip>
</TooltipProvider>
)} )}
{/* Feedback */} {/* Feedback */}
<TooltipProvider delayDuration={0}>
<Tooltip> <Tooltip>
<TooltipTrigger asChild> <TooltipTrigger asChild>
<button <button
@@ -209,7 +199,6 @@ export function SidebarFooter({
Feedback Feedback
</TooltipContent> </TooltipContent>
</Tooltip> </Tooltip>
</TooltipProvider>
</div> </div>
</div> </div>
); );

View File

@@ -15,7 +15,7 @@ import {
DropdownMenuSeparator, DropdownMenuSeparator,
DropdownMenuTrigger, DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu'; } from '@/components/ui/dropdown-menu';
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip'; import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip';
interface SidebarHeaderProps { interface SidebarHeaderProps {
sidebarOpen: boolean; sidebarOpen: boolean;
@@ -92,7 +92,6 @@ export function SidebarHeader({
isMac && isElectron() && 'pt-[10px]' isMac && isElectron() && 'pt-[10px]'
)} )}
> >
<TooltipProvider delayDuration={0}>
<Tooltip> <Tooltip>
<TooltipTrigger asChild> <TooltipTrigger asChild>
<button <button
@@ -139,14 +138,12 @@ export function SidebarHeader({
Go to Dashboard Go to Dashboard
</TooltipContent> </TooltipContent>
</Tooltip> </Tooltip>
</TooltipProvider>
{/* Collapsed project icon with dropdown */} {/* Collapsed project icon with dropdown */}
{currentProject && ( {currentProject && (
<> <>
<div className="w-full h-px bg-border/40 my-2" /> <div className="w-full h-px bg-border/40 my-2" />
<DropdownMenu open={dropdownOpen} onOpenChange={setDropdownOpen}> <DropdownMenu open={dropdownOpen} onOpenChange={setDropdownOpen}>
<TooltipProvider delayDuration={0}>
<Tooltip> <Tooltip>
<TooltipTrigger asChild> <TooltipTrigger asChild>
<DropdownMenuTrigger asChild> <DropdownMenuTrigger asChild>
@@ -163,7 +160,6 @@ export function SidebarHeader({
{currentProject.name} {currentProject.name}
</TooltipContent> </TooltipContent>
</Tooltip> </Tooltip>
</TooltipProvider>
<DropdownMenuContent <DropdownMenuContent
align="start" align="start"
side="right" side="right"

View File

@@ -13,7 +13,7 @@ import {
DropdownMenuItem, DropdownMenuItem,
DropdownMenuTrigger, DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu'; } from '@/components/ui/dropdown-menu';
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip'; import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip';
// Map section labels to icons // Map section labels to icons
const sectionIcons: Record<string, React.ComponentType<{ className?: string }>> = { const sectionIcons: Record<string, React.ComponentType<{ className?: string }>> = {
@@ -158,7 +158,6 @@ export function SidebarNavigation({
{/* Section icon with dropdown (collapsed sidebar) */} {/* Section icon with dropdown (collapsed sidebar) */}
{section.label && !sidebarOpen && SectionIcon && section.collapsible && isCollapsed && ( {section.label && !sidebarOpen && SectionIcon && section.collapsible && isCollapsed && (
<DropdownMenu> <DropdownMenu>
<TooltipProvider delayDuration={0}>
<Tooltip> <Tooltip>
<TooltipTrigger asChild> <TooltipTrigger asChild>
<DropdownMenuTrigger asChild> <DropdownMenuTrigger asChild>
@@ -178,7 +177,6 @@ export function SidebarNavigation({
{section.label} {section.label}
</TooltipContent> </TooltipContent>
</Tooltip> </Tooltip>
</TooltipProvider>
<DropdownMenuContent side="right" align="start" sideOffset={8} className="w-48"> <DropdownMenuContent side="right" align="start" sideOffset={8} className="w-48">
{section.items.map((item) => { {section.items.map((item) => {
const ItemIcon = item.icon; const ItemIcon = item.icon;

View File

@@ -7,7 +7,7 @@ import {
} from '@/store/app-store'; } from '@/store/app-store';
import type { KeyboardShortcuts } from '@/store/app-store'; import type { KeyboardShortcuts } from '@/store/app-store';
import { cn } from '@/lib/utils'; import { cn } from '@/lib/utils';
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip'; import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip';
import { Input } from '@/components/ui/input'; import { Input } from '@/components/ui/input';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { CheckCircle2, X, RotateCcw, Edit2 } from 'lucide-react'; import { CheckCircle2, X, RotateCcw, Edit2 } from 'lucide-react';
@@ -305,7 +305,6 @@ export function KeyboardMap({ onKeySelect, selectedKey, className }: KeyboardMap
}; };
return ( return (
<TooltipProvider>
<div className={cn('space-y-4', className)} data-testid="keyboard-map"> <div className={cn('space-y-4', className)} data-testid="keyboard-map">
{/* Legend */} {/* Legend */}
<div className="flex flex-wrap gap-4 justify-center text-xs"> <div className="flex flex-wrap gap-4 justify-center text-xs">
@@ -341,8 +340,8 @@ export function KeyboardMap({ onKeySelect, selectedKey, className }: KeyboardMap
shortcuts configured shortcuts configured
</span> </span>
<span> <span>
<strong className="text-foreground">{Object.keys(keyToShortcuts).length}</strong> keys <strong className="text-foreground">{Object.keys(keyToShortcuts).length}</strong> keys in
in use use
</span> </span>
<span> <span>
<strong className="text-foreground"> <strong className="text-foreground">
@@ -352,7 +351,6 @@ export function KeyboardMap({ onKeySelect, selectedKey, className }: KeyboardMap
</span> </span>
</div> </div>
</div> </div>
</TooltipProvider>
); );
} }
@@ -508,7 +506,6 @@ export function ShortcutReferencePanel({ editable = false }: ShortcutReferencePa
}; };
return ( return (
<TooltipProvider>
<div className="space-y-4" data-testid="shortcut-reference-panel"> <div className="space-y-4" data-testid="shortcut-reference-panel">
{editable && ( {editable && (
<div className="flex justify-end"> <div className="flex justify-end">
@@ -698,6 +695,5 @@ export function ShortcutReferencePanel({ editable = false }: ShortcutReferencePa
); );
})} })}
</div> </div>
</TooltipProvider>
); );
} }

View File

@@ -1,4 +1,4 @@
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip'; import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip';
import { ImageIcon } from 'lucide-react'; import { ImageIcon } from 'lucide-react';
import { cn } from '@/lib/utils'; import { cn } from '@/lib/utils';
@@ -18,7 +18,6 @@ export function BoardControls({ isMounted, onShowBoardBackground }: BoardControl
); );
return ( return (
<TooltipProvider>
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
{/* Board Background Button */} {/* Board Background Button */}
<Tooltip> <Tooltip>
@@ -36,6 +35,5 @@ export function BoardControls({ isMounted, onShowBoardBackground }: BoardControl
</TooltipContent> </TooltipContent>
</Tooltip> </Tooltip>
</div> </div>
</TooltipProvider>
); );
} }

View File

@@ -2,7 +2,7 @@
import { memo, useEffect, useMemo, useState } from 'react'; import { memo, useEffect, useMemo, useState } from 'react';
import { Feature, useAppStore } from '@/store/app-store'; import { Feature, useAppStore } from '@/store/app-store';
import { cn } from '@/lib/utils'; import { cn } from '@/lib/utils';
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip'; import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip';
import { AlertCircle, Lock, Hand, Sparkles, SkipForward } from 'lucide-react'; import { AlertCircle, Lock, Hand, Sparkles, SkipForward } from 'lucide-react';
import { getBlockingDependencies } from '@automaker/dependency-resolver'; import { getBlockingDependencies } from '@automaker/dependency-resolver';
import { useShallow } from 'zustand/react/shallow'; import { useShallow } from 'zustand/react/shallow';
@@ -28,7 +28,6 @@ export const CardBadges = memo(function CardBadges({ feature }: CardBadgesProps)
return ( return (
<div className="flex flex-wrap items-center gap-1.5 px-3 pt-1.5 min-h-[24px]"> <div className="flex flex-wrap items-center gap-1.5 px-3 pt-1.5 min-h-[24px]">
{/* Error badge */} {/* Error badge */}
<TooltipProvider delayDuration={200}>
<Tooltip> <Tooltip>
<TooltipTrigger asChild> <TooltipTrigger asChild>
<div <div
@@ -45,7 +44,6 @@ export const CardBadges = memo(function CardBadges({ feature }: CardBadgesProps)
<p>{feature.error}</p> <p>{feature.error}</p>
</TooltipContent> </TooltipContent>
</Tooltip> </Tooltip>
</TooltipProvider>
</div> </div>
); );
}); });
@@ -138,7 +136,6 @@ export const PriorityBadges = memo(function PriorityBadges({
<div className="absolute top-2 left-2 flex items-center gap-1"> <div className="absolute top-2 left-2 flex items-center gap-1">
{/* Priority badge */} {/* Priority badge */}
{feature.priority && ( {feature.priority && (
<TooltipProvider delayDuration={200}>
<Tooltip> <Tooltip>
<TooltipTrigger asChild> <TooltipTrigger asChild>
<div <div
@@ -168,12 +165,10 @@ export const PriorityBadges = memo(function PriorityBadges({
</p> </p>
</TooltipContent> </TooltipContent>
</Tooltip> </Tooltip>
</TooltipProvider>
)} )}
{/* Manual verification badge */} {/* Manual verification badge */}
{showManualVerification && ( {showManualVerification && (
<TooltipProvider delayDuration={200}>
<Tooltip> <Tooltip>
<TooltipTrigger asChild> <TooltipTrigger asChild>
<div <div
@@ -190,12 +185,10 @@ export const PriorityBadges = memo(function PriorityBadges({
<p>Manual verification required</p> <p>Manual verification required</p>
</TooltipContent> </TooltipContent>
</Tooltip> </Tooltip>
</TooltipProvider>
)} )}
{/* Blocked badge */} {/* Blocked badge */}
{isBlocked && ( {isBlocked && (
<TooltipProvider delayDuration={200}>
<Tooltip> <Tooltip>
<TooltipTrigger asChild> <TooltipTrigger asChild>
<div <div
@@ -223,12 +216,10 @@ export const PriorityBadges = memo(function PriorityBadges({
</p> </p>
</TooltipContent> </TooltipContent>
</Tooltip> </Tooltip>
</TooltipProvider>
)} )}
{/* Just Finished badge */} {/* Just Finished badge */}
{isJustFinished && ( {isJustFinished && (
<TooltipProvider delayDuration={200}>
<Tooltip> <Tooltip>
<TooltipTrigger asChild> <TooltipTrigger asChild>
<div <div
@@ -245,12 +236,10 @@ export const PriorityBadges = memo(function PriorityBadges({
<p>Agent just finished working on this feature</p> <p>Agent just finished working on this feature</p>
</TooltipContent> </TooltipContent>
</Tooltip> </Tooltip>
</TooltipProvider>
)} )}
{/* Pipeline exclusion badge */} {/* Pipeline exclusion badge */}
{hasPipelineExclusions && ( {hasPipelineExclusions && (
<TooltipProvider delayDuration={200}>
<Tooltip> <Tooltip>
<TooltipTrigger asChild> <TooltipTrigger asChild>
<div <div
@@ -278,7 +267,6 @@ export const PriorityBadges = memo(function PriorityBadges({
</p> </p>
</TooltipContent> </TooltipContent>
</Tooltip> </Tooltip>
</TooltipProvider>
)} )}
</div> </div>
); );

View File

@@ -78,7 +78,9 @@ export const KanbanColumn = memo(function KanbanColumn({
)} )}
> >
<div className={cn('w-2.5 h-2.5 rounded-full shrink-0', colorClass)} /> <div className={cn('w-2.5 h-2.5 rounded-full shrink-0', colorClass)} />
<h3 className="font-semibold text-sm text-foreground/90 flex-1 tracking-tight">{title}</h3> <h3 className="font-semibold text-sm text-foreground/90 flex-1 tracking-tight whitespace-nowrap">
{title}
</h3>
{headerAction} {headerAction}
<span className="text-xs font-medium text-muted-foreground/80 bg-muted/50 px-2 py-0.5 rounded-md tabular-nums"> <span className="text-xs font-medium text-muted-foreground/80 bg-muted/50 px-2 py-0.5 rounded-md tabular-nums">
{count} {count}

View File

@@ -132,7 +132,7 @@ const SortableColumnHeader = memo(function SortableColumnHeader({
)} )}
data-testid={`list-header-${column.id}`} data-testid={`list-header-${column.id}`}
> >
<span>{column.label}</span> <span className="whitespace-nowrap truncate">{column.label}</span>
<SortIcon column={column.id} sortConfig={sortConfig} /> <SortIcon column={column.id} sortConfig={sortConfig} />
</div> </div>
); );
@@ -156,7 +156,7 @@ const StaticColumnHeader = memo(function StaticColumnHeader({ column }: { column
)} )}
data-testid={`list-header-${column.id}`} data-testid={`list-header-${column.id}`}
> >
<span>{column.label}</span> <span className="whitespace-nowrap truncate">{column.label}</span>
</div> </div>
); );
}); });

View File

@@ -3,7 +3,7 @@
// @ts-nocheck // @ts-nocheck
import { memo, useCallback, useState, useEffect } from 'react'; import { memo, useCallback, useState, useEffect } from 'react';
import { cn } from '@/lib/utils'; import { cn } from '@/lib/utils';
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip'; import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip';
import { AlertCircle, Lock, Hand, Sparkles, FileText } from 'lucide-react'; import { AlertCircle, Lock, Hand, Sparkles, FileText } from 'lucide-react';
import type { Feature } from '@/store/app-store'; import type { Feature } from '@/store/app-store';
import { RowActions, type RowActionHandlers } from './row-actions'; import { RowActions, type RowActionHandlers } from './row-actions';
@@ -149,7 +149,6 @@ const IndicatorBadges = memo(function IndicatorBadges({
return ( return (
<div className="flex items-center gap-1 ml-2"> <div className="flex items-center gap-1 ml-2">
<TooltipProvider delayDuration={200}>
{badges.map((badge) => ( {badges.map((badge) => (
<Tooltip key={badge.key}> <Tooltip key={badge.key}>
<TooltipTrigger asChild> <TooltipTrigger asChild>
@@ -171,7 +170,6 @@ const IndicatorBadges = memo(function IndicatorBadges({
</TooltipContent> </TooltipContent>
</Tooltip> </Tooltip>
))} ))}
</TooltipProvider>
</div> </div>
); );
}); });

View File

@@ -50,7 +50,7 @@ import {
} from '../shared'; } from '../shared';
import type { WorkMode } from '../shared'; import type { WorkMode } from '../shared';
import { PhaseModelSelector } from '@/components/views/settings-view/model-defaults/phase-model-selector'; import { PhaseModelSelector } from '@/components/views/settings-view/model-defaults/phase-model-selector';
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip'; import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip';
import { import {
getAncestors, getAncestors,
formatAncestorContextForPrompt, formatAncestorContextForPrompt,
@@ -532,7 +532,6 @@ export function AddFeatureDialog({
<Cpu className="w-4 h-4 text-muted-foreground" /> <Cpu className="w-4 h-4 text-muted-foreground" />
<span>AI & Execution</span> <span>AI & Execution</span>
</div> </div>
<TooltipProvider>
<Tooltip> <Tooltip>
<TooltipTrigger asChild> <TooltipTrigger asChild>
<button <button
@@ -551,7 +550,6 @@ export function AddFeatureDialog({
<p>Change default model and planning settings for new features</p> <p>Change default model and planning settings for new features</p>
</TooltipContent> </TooltipContent>
</Tooltip> </Tooltip>
</TooltipProvider>
</div> </div>
<div className="space-y-1.5"> <div className="space-y-1.5">

View File

@@ -41,7 +41,7 @@ import {
} from '../shared'; } from '../shared';
import type { WorkMode } from '../shared'; import type { WorkMode } from '../shared';
import { PhaseModelSelector } from '@/components/views/settings-view/model-defaults/phase-model-selector'; import { PhaseModelSelector } from '@/components/views/settings-view/model-defaults/phase-model-selector';
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip'; import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip';
import { DependencyTreeDialog } from './dependency-tree-dialog'; import { DependencyTreeDialog } from './dependency-tree-dialog';
import { supportsReasoningEffort } from '@automaker/types'; import { supportsReasoningEffort } from '@automaker/types';
@@ -424,7 +424,6 @@ export function EditFeatureDialog({
<Cpu className="w-4 h-4 text-muted-foreground" /> <Cpu className="w-4 h-4 text-muted-foreground" />
<span>AI & Execution</span> <span>AI & Execution</span>
</div> </div>
<TooltipProvider>
<Tooltip> <Tooltip>
<TooltipTrigger asChild> <TooltipTrigger asChild>
<button <button
@@ -443,7 +442,6 @@ export function EditFeatureDialog({
<p>Change default model and planning settings for new features</p> <p>Change default model and planning settings for new features</p>
</TooltipContent> </TooltipContent>
</Tooltip> </Tooltip>
</TooltipProvider>
</div> </div>
<div className="space-y-1.5"> <div className="space-y-1.5">

View File

@@ -12,7 +12,8 @@ import { SortableContext, verticalListSortingStrategy } from '@dnd-kit/sortable'
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { KanbanColumn, KanbanCard, EmptyStateCard } from './components'; import { KanbanColumn, KanbanCard, EmptyStateCard } from './components';
import { Feature, useAppStore, formatShortcut } from '@/store/app-store'; import { Feature, useAppStore, formatShortcut } from '@/store/app-store';
import { Archive, Settings2, CheckSquare, GripVertical, Plus } from 'lucide-react'; import { Archive, Settings2, CheckSquare, GripVertical, Plus, CheckCircle2 } from 'lucide-react';
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip';
import { useResponsiveKanban } from '@/hooks/use-responsive-kanban'; import { useResponsiveKanban } from '@/hooks/use-responsive-kanban';
import { getColumnsWithPipeline, type ColumnId } from './constants'; import { getColumnsWithPipeline, type ColumnId } from './constants';
import type { PipelineConfig } from '@automaker/types'; import type { PipelineConfig } from '@automaker/types';
@@ -359,23 +360,30 @@ export function KanbanBoard({
column.id === 'verified' ? ( column.id === 'verified' ? (
<div className="flex items-center gap-1"> <div className="flex items-center gap-1">
{columnFeatures.length > 0 && ( {columnFeatures.length > 0 && (
<Tooltip>
<TooltipTrigger asChild>
<Button <Button
variant="ghost" variant="ghost"
size="sm" size="sm"
className="h-6 px-2 text-xs" className="h-6 w-6 p-0"
onClick={onArchiveAllVerified} onClick={onArchiveAllVerified}
data-testid="archive-all-verified-button" data-testid="archive-all-verified-button"
> >
<Archive className="w-3 h-3 mr-1" /> <CheckCircle2 className="w-3.5 h-3.5" />
Complete All
</Button> </Button>
</TooltipTrigger>
<TooltipContent>
<p>Complete All</p>
</TooltipContent>
</Tooltip>
)} )}
<Tooltip>
<TooltipTrigger asChild>
<Button <Button
variant="ghost" variant="ghost"
size="sm" size="sm"
className="h-6 w-6 p-0 relative" className="h-6 w-6 p-0 relative"
onClick={onShowCompletedModal} onClick={onShowCompletedModal}
title={`Completed Features (${completedCount})`}
data-testid="completed-features-button" data-testid="completed-features-button"
> >
<Archive className="w-3.5 h-3.5 text-muted-foreground" /> <Archive className="w-3.5 h-3.5 text-muted-foreground" />
@@ -385,6 +393,11 @@ export function KanbanBoard({
</span> </span>
)} )}
</Button> </Button>
</TooltipTrigger>
<TooltipContent>
<p>Completed Features ({completedCount})</p>
</TooltipContent>
</Tooltip>
</div> </div>
) : column.id === 'backlog' ? ( ) : column.id === 'backlog' ? (
<div className="flex items-center gap-1"> <div className="flex items-center gap-1">

View File

@@ -1,5 +1,5 @@
import type { ReactElement, ReactNode } from 'react'; import type { ReactElement, ReactNode } from 'react';
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip'; import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip';
interface TooltipWrapperProps { interface TooltipWrapperProps {
/** The element to wrap with a tooltip */ /** The element to wrap with a tooltip */
@@ -29,7 +29,6 @@ export function TooltipWrapper({
} }
return ( return (
<TooltipProvider>
<Tooltip> <Tooltip>
<TooltipTrigger asChild> <TooltipTrigger asChild>
{/* The div wrapper is necessary for tooltips to work on disabled elements */} {/* The div wrapper is necessary for tooltips to work on disabled elements */}
@@ -39,6 +38,5 @@ export function TooltipWrapper({
<p>{tooltipContent}</p> <p>{tooltipContent}</p>
</TooltipContent> </TooltipContent>
</Tooltip> </Tooltip>
</TooltipProvider>
); );
} }

View File

@@ -1,5 +1,5 @@
import { DropdownMenuItem } from '@/components/ui/dropdown-menu'; import { DropdownMenuItem } from '@/components/ui/dropdown-menu';
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip'; import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip';
import { Check, CircleDot, Globe, GitPullRequest, FlaskConical } from 'lucide-react'; import { Check, CircleDot, Globe, GitPullRequest, FlaskConical } from 'lucide-react';
import { Spinner } from '@/components/ui/spinner'; import { Spinner } from '@/components/ui/spinner';
import { cn } from '@/lib/utils'; import { cn } from '@/lib/utils';
@@ -101,14 +101,12 @@ export function WorktreeDropdownItem({
{/* Branch name with optional tooltip */} {/* Branch name with optional tooltip */}
{isBranchNameTruncated ? ( {isBranchNameTruncated ? (
<TooltipProvider>
<Tooltip> <Tooltip>
<TooltipTrigger asChild>{branchNameElement}</TooltipTrigger> <TooltipTrigger asChild>{branchNameElement}</TooltipTrigger>
<TooltipContent> <TooltipContent>
<p className="font-mono text-xs">{worktree.branch}</p> <p className="font-mono text-xs">{worktree.branch}</p>
</TooltipContent> </TooltipContent>
</Tooltip> </Tooltip>
</TooltipProvider>
) : ( ) : (
branchNameElement branchNameElement
)} )}

View File

@@ -8,7 +8,7 @@ import {
DropdownMenuTrigger, DropdownMenuTrigger,
DropdownMenuGroup, DropdownMenuGroup,
} from '@/components/ui/dropdown-menu'; } from '@/components/ui/dropdown-menu';
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip'; import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip';
import { import {
GitBranch, GitBranch,
ChevronDown, ChevronDown,
@@ -335,14 +335,12 @@ export function WorktreeDropdown({
const dropdownTrigger = <DropdownMenuTrigger asChild>{triggerButton}</DropdownMenuTrigger>; const dropdownTrigger = <DropdownMenuTrigger asChild>{triggerButton}</DropdownMenuTrigger>;
const triggerWithTooltip = isBranchNameTruncated ? ( const triggerWithTooltip = isBranchNameTruncated ? (
<TooltipProvider>
<Tooltip> <Tooltip>
<TooltipTrigger asChild>{dropdownTrigger}</TooltipTrigger> <TooltipTrigger asChild>{dropdownTrigger}</TooltipTrigger>
<TooltipContent> <TooltipContent>
<p className="font-mono text-xs">{displayBranch}</p> <p className="font-mono text-xs">{displayBranch}</p>
</TooltipContent> </TooltipContent>
</Tooltip> </Tooltip>
</TooltipProvider>
) : ( ) : (
dropdownTrigger dropdownTrigger
); );

View File

@@ -3,7 +3,7 @@ import { Button } from '@/components/ui/button';
import { Globe, CircleDot, GitPullRequest } from 'lucide-react'; import { Globe, CircleDot, GitPullRequest } from 'lucide-react';
import { Spinner } from '@/components/ui/spinner'; import { Spinner } from '@/components/ui/spinner';
import { cn } from '@/lib/utils'; import { cn } from '@/lib/utils';
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip'; import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip';
import { useDroppable } from '@dnd-kit/core'; import { useDroppable } from '@dnd-kit/core';
import type { import type {
WorktreeInfo, WorktreeInfo,
@@ -271,7 +271,6 @@ export function WorktreeTab({
</span> </span>
)} )}
{hasChanges && ( {hasChanges && (
<TooltipProvider>
<Tooltip> <Tooltip>
<TooltipTrigger asChild> <TooltipTrigger asChild>
<span <span
@@ -293,7 +292,6 @@ export function WorktreeTab({
</p> </p>
</TooltipContent> </TooltipContent>
</Tooltip> </Tooltip>
</TooltipProvider>
)} )}
{prBadge} {prBadge}
</Button> </Button>
@@ -340,7 +338,6 @@ export function WorktreeTab({
</span> </span>
)} )}
{hasChanges && ( {hasChanges && (
<TooltipProvider>
<Tooltip> <Tooltip>
<TooltipTrigger asChild> <TooltipTrigger asChild>
<span <span
@@ -362,14 +359,12 @@ export function WorktreeTab({
</p> </p>
</TooltipContent> </TooltipContent>
</Tooltip> </Tooltip>
</TooltipProvider>
)} )}
{prBadge} {prBadge}
</Button> </Button>
)} )}
{isDevServerRunning && ( {isDevServerRunning && (
<TooltipProvider>
<Tooltip> <Tooltip>
<TooltipTrigger asChild> <TooltipTrigger asChild>
<Button <Button
@@ -391,11 +386,9 @@ export function WorktreeTab({
<p>Open dev server (:{devServerInfo?.port})</p> <p>Open dev server (:{devServerInfo?.port})</p>
</TooltipContent> </TooltipContent>
</Tooltip> </Tooltip>
</TooltipProvider>
)} )}
{isAutoModeRunning && ( {isAutoModeRunning && (
<TooltipProvider>
<Tooltip> <Tooltip>
<TooltipTrigger asChild> <TooltipTrigger asChild>
<span <span
@@ -411,7 +404,6 @@ export function WorktreeTab({
<p>Auto Mode Running</p> <p>Auto Mode Running</p>
</TooltipContent> </TooltipContent>
</Tooltip> </Tooltip>
</TooltipProvider>
)} )}
<WorktreeActionsDropdown <WorktreeActionsDropdown

View File

@@ -1,6 +1,6 @@
import { useReactFlow, Panel } from '@xyflow/react'; import { useReactFlow, Panel } from '@xyflow/react';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip'; import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip';
import { import {
ZoomIn, ZoomIn,
ZoomOut, ZoomOut,
@@ -30,7 +30,6 @@ export function GraphControls({
return ( return (
<Panel position="bottom-left" className="flex flex-col gap-2"> <Panel position="bottom-left" className="flex flex-col gap-2">
<TooltipProvider delayDuration={200}>
<div <div
className="flex flex-col gap-1 p-1.5 rounded-lg backdrop-blur-sm border border-border shadow-lg text-popover-foreground" className="flex flex-col gap-1 p-1.5 rounded-lg backdrop-blur-sm border border-border shadow-lg text-popover-foreground"
style={{ backgroundColor: 'color-mix(in oklch, var(--popover) 90%, transparent)' }} style={{ backgroundColor: 'color-mix(in oklch, var(--popover) 90%, transparent)' }}
@@ -132,7 +131,6 @@ export function GraphControls({
<TooltipContent side="right">{isLocked ? 'Unlock Nodes' : 'Lock Nodes'}</TooltipContent> <TooltipContent side="right">{isLocked ? 'Unlock Nodes' : 'Lock Nodes'}</TooltipContent>
</Tooltip> </Tooltip>
</div> </div>
</TooltipProvider>
</Panel> </Panel>
); );
} }

View File

@@ -4,7 +4,7 @@ import { Checkbox } from '@/components/ui/checkbox';
import { Switch } from '@/components/ui/switch'; import { Switch } from '@/components/ui/switch';
import { Input } from '@/components/ui/input'; import { Input } from '@/components/ui/input';
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover'; import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover';
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip'; import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip';
import { import {
Filter, Filter,
X, X,
@@ -115,7 +115,6 @@ export function GraphFilterControls({
return ( return (
<Panel position="top-left" className="flex items-center gap-2"> <Panel position="top-left" className="flex items-center gap-2">
<TooltipProvider delayDuration={200}>
<div <div
className="flex items-center gap-2 p-2 rounded-lg backdrop-blur-sm border border-border shadow-lg text-popover-foreground" className="flex items-center gap-2 p-2 rounded-lg backdrop-blur-sm border border-border shadow-lg text-popover-foreground"
style={{ backgroundColor: 'color-mix(in oklch, var(--popover) 90%, transparent)' }} style={{ backgroundColor: 'color-mix(in oklch, var(--popover) 90%, transparent)' }}
@@ -165,11 +164,13 @@ export function GraphFilterControls({
</TooltipTrigger> </TooltipTrigger>
<TooltipContent>Filter by Category</TooltipContent> <TooltipContent>Filter by Category</TooltipContent>
</Tooltip> </Tooltip>
<PopoverContent align="start" className="w-56 p-2"> <PopoverContent
align="start"
className="w-56 p-2"
onOpenAutoFocus={(e) => e.preventDefault()}
>
<div className="space-y-2"> <div className="space-y-2">
<div className="text-xs font-medium text-muted-foreground px-2 py-1"> <div className="text-xs font-medium text-muted-foreground px-2 py-1">Categories</div>
Categories
</div>
{/* Select All option */} {/* Select All option */}
<div <div
@@ -239,7 +240,11 @@ export function GraphFilterControls({
</TooltipTrigger> </TooltipTrigger>
<TooltipContent>Filter by Status</TooltipContent> <TooltipContent>Filter by Status</TooltipContent>
</Tooltip> </Tooltip>
<PopoverContent align="start" className="w-56 p-2"> <PopoverContent
align="start"
className="w-56 p-2"
onOpenAutoFocus={(e) => e.preventDefault()}
>
<div className="space-y-2"> <div className="space-y-2">
<div className="text-xs font-medium text-muted-foreground px-2 py-1">Status</div> <div className="text-xs font-medium text-muted-foreground px-2 py-1">Status</div>
@@ -356,7 +361,6 @@ export function GraphFilterControls({
</> </>
)} )}
</div> </div>
</TooltipProvider>
</Panel> </Panel>
); );
} }

View File

@@ -26,7 +26,7 @@ import {
DropdownMenuItem, DropdownMenuItem,
DropdownMenuTrigger, DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu'; } from '@/components/ui/dropdown-menu';
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip'; import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip';
type TaskNodeProps = NodeProps & { type TaskNodeProps = NodeProps & {
data: TaskNodeData; data: TaskNodeData;
@@ -286,7 +286,6 @@ export const TaskNode = memo(function TaskNode({ data, selected }: TaskNodeProps
{/* Blocked indicator */} {/* Blocked indicator */}
{data.isBlocked && !data.error && data.status === 'backlog' && ( {data.isBlocked && !data.error && data.status === 'backlog' && (
<TooltipProvider delayDuration={200}>
<Tooltip> <Tooltip>
<TooltipTrigger asChild> <TooltipTrigger asChild>
<div className="p-1 rounded bg-orange-500/20"> <div className="p-1 rounded bg-orange-500/20">
@@ -297,12 +296,10 @@ export const TaskNode = memo(function TaskNode({ data, selected }: TaskNodeProps
<p>Blocked by {data.blockingDependencies.length} dependencies</p> <p>Blocked by {data.blockingDependencies.length} dependencies</p>
</TooltipContent> </TooltipContent>
</Tooltip> </Tooltip>
</TooltipProvider>
)} )}
{/* Error indicator */} {/* Error indicator */}
{data.error && ( {data.error && (
<TooltipProvider delayDuration={200}>
<Tooltip> <Tooltip>
<TooltipTrigger asChild> <TooltipTrigger asChild>
<div className="p-1 rounded bg-[var(--status-error-bg)]"> <div className="p-1 rounded bg-[var(--status-error-bg)]">
@@ -313,12 +310,10 @@ export const TaskNode = memo(function TaskNode({ data, selected }: TaskNodeProps
<p>{data.error}</p> <p>{data.error}</p>
</TooltipContent> </TooltipContent>
</Tooltip> </Tooltip>
</TooltipProvider>
)} )}
{/* Stopped indicator - task is in_progress but not actively running */} {/* Stopped indicator - task is in_progress but not actively running */}
{isStopped && ( {isStopped && (
<TooltipProvider delayDuration={200}>
<Tooltip> <Tooltip>
<TooltipTrigger asChild> <TooltipTrigger asChild>
<div className="p-1 rounded bg-[var(--status-warning-bg)]"> <div className="p-1 rounded bg-[var(--status-warning-bg)]">
@@ -329,7 +324,6 @@ export const TaskNode = memo(function TaskNode({ data, selected }: TaskNodeProps
<p>Task paused - click menu to resume</p> <p>Task paused - click menu to resume</p>
</TooltipContent> </TooltipContent>
</Tooltip> </Tooltip>
</TooltipProvider>
)} )}
{/* Actions dropdown */} {/* Actions dropdown */}

View File

@@ -8,7 +8,7 @@ import {
SelectTrigger, SelectTrigger,
SelectValue, SelectValue,
} from '@/components/ui/select'; } from '@/components/ui/select';
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip'; import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip';
import { toast } from 'sonner'; import { toast } from 'sonner';
import { LogOut, User, Code2, RefreshCw } from 'lucide-react'; import { LogOut, User, Code2, RefreshCw } from 'lucide-react';
import { Spinner } from '@/components/ui/spinner'; import { Spinner } from '@/components/ui/spinner';
@@ -134,7 +134,6 @@ export function AccountSection() {
})} })}
</SelectContent> </SelectContent>
</Select> </Select>
<TooltipProvider>
<Tooltip> <Tooltip>
<TooltipTrigger asChild> <TooltipTrigger asChild>
<Button <Button
@@ -151,7 +150,6 @@ export function AccountSection() {
<p>Refresh available editors</p> <p>Refresh available editors</p>
</TooltipContent> </TooltipContent>
</Tooltip> </Tooltip>
</TooltipProvider>
</div> </div>
</div> </div>

View File

@@ -14,8 +14,8 @@ export interface ResponsiveKanbanConfig {
* Default configuration for responsive Kanban columns * Default configuration for responsive Kanban columns
*/ */
const DEFAULT_CONFIG: ResponsiveKanbanConfig = { const DEFAULT_CONFIG: ResponsiveKanbanConfig = {
columnWidth: 288, // 18rem = 288px (w-72) columnWidth: 320, // Increased from 288px to accommodate longer column titles
columnMinWidth: 280, // Minimum column width - ensures usability columnMinWidth: 320, // Increased from 280px to prevent title overflow
columnMaxWidth: Infinity, // No max width - columns scale evenly to fill viewport columnMaxWidth: Infinity, // No max width - columns scale evenly to fill viewport
gap: 20, // gap-5 = 20px gap: 20, // gap-5 = 20px
padding: 40, // px-5 on both sides = 40px (matches gap between columns) padding: 40, // px-5 on both sides = 40px (matches gap between columns)

View File

@@ -237,39 +237,34 @@ function cleanFragmentedText(content: string): string {
/** /**
* Extracts a summary from completed feature context * Extracts a summary from completed feature context
* Looks for content between <summary> and </summary> tags * Looks for content between <summary> and </summary> tags
* Returns the LAST summary found to ensure we get the most recent/updated one
*/ */
function extractSummary(content: string): string | undefined { function extractSummary(content: string): string | undefined {
// First, clean up any fragmented text from streaming // First, clean up any fragmented text from streaming
const cleanedContent = cleanFragmentedText(content); const cleanedContent = cleanFragmentedText(content);
// Look for <summary> tags - capture everything between opening and closing tags // Define regex patterns to try in order of priority
const summaryTagMatch = cleanedContent.match(/<summary>([\s\S]*?)<\/summary>/i); // Each pattern specifies which capture group contains the summary content
if (summaryTagMatch) { const regexesToTry = [
// Clean up the extracted summary content as well { regex: /<summary>([\s\S]*?)<\/summary>/gi, group: 1 },
return cleanFragmentedText(summaryTagMatch[1]).trim(); { regex: /## Summary[^\n]*\n([\s\S]*?)(?=\n## [^#]|\n🔧|$)/gi, group: 1 },
} {
regex:
/✓ (?:Feature|Verification|Task) (?:successfully|completed|verified)[^\n]*(?:\n[^\n]{1,200})?/gi,
group: 0,
},
{
regex: /(?:What was done|Changes made|Implemented)[^\n]*\n([\s\S]*?)(?=\n## [^#]|\n🔧|$)/gi,
group: 1,
},
];
// Fallback: Look for summary sections - capture everything including subsections (###) for (const { regex, group } of regexesToTry) {
// Stop at same-level ## sections (but not ###), or tool markers, or end const matches = [...cleanedContent.matchAll(regex)];
const summaryMatch = cleanedContent.match(/## Summary[^\n]*\n([\s\S]*?)(?=\n## [^#]|\n🔧|$)/i); if (matches.length > 0) {
if (summaryMatch) { const lastMatch = matches[matches.length - 1];
return cleanFragmentedText(summaryMatch[1]).trim(); return cleanFragmentedText(lastMatch[group]).trim();
} }
// Look for completion markers and extract surrounding text
const completionMatch = cleanedContent.match(
/✓ (?:Feature|Verification|Task) (?:successfully|completed|verified)[^\n]*(?:\n[^\n]{1,200})?/i
);
if (completionMatch) {
return cleanFragmentedText(completionMatch[0]).trim();
}
// Look for "What was done" type sections
const whatWasDoneMatch = cleanedContent.match(
/(?:What was done|Changes made|Implemented)[^\n]*\n([\s\S]*?)(?=\n## [^#]|\n🔧|$)/i
);
if (whatWasDoneMatch) {
return cleanFragmentedText(whatWasDoneMatch[1]).trim();
} }
return undefined; return undefined;

View File

@@ -1198,46 +1198,48 @@ function mergeConsecutiveEntries(entries: LogEntry[]): LogEntry[] {
/** /**
* Extracts summary content from raw log output * Extracts summary content from raw log output
* Returns the summary text if found, or null if no summary exists * Returns the LAST summary text if found, or null if no summary exists
* This ensures we get the most recent/updated summary when multiple exist
*/ */
export function extractSummary(rawOutput: string): string | null { export function extractSummary(rawOutput: string): string | null {
if (!rawOutput || !rawOutput.trim()) { if (!rawOutput || !rawOutput.trim()) {
return null; return null;
} }
// Try to find <summary> tags first (preferred format) // First, clean up any fragmented text from streaming
const summaryTagMatch = rawOutput.match(/<summary>([\s\S]*?)<\/summary>/); // This handles cases where streaming providers send partial text chunks
if (summaryTagMatch) { // that got separated by newlines during accumulation (e.g., "<sum\n\nmary>")
return summaryTagMatch[1].trim(); const cleanedOutput = cleanFragmentedText(rawOutput);
}
// Try to find markdown ## Summary section // Define regex patterns to try in order of priority
const summaryHeaderMatch = rawOutput.match(/^##\s+Summary\s*\n([\s\S]*?)(?=\n##\s+|$)/m); // Each pattern specifies a processor function to extract the summary from the match
if (summaryHeaderMatch) { const regexesToTry: Array<{
return summaryHeaderMatch[1].trim(); regex: RegExp;
} processor: (m: RegExpMatchArray) => string;
}> = [
{ regex: /<summary>([\s\S]*?)<\/summary>/gi, processor: (m) => m[1] },
{ regex: /^##\s+Summary[^\n]*\n([\s\S]*?)(?=\n##\s+[^#]|\n🔧|$)/gm, processor: (m) => m[1] },
{
regex: /^##\s+(Feature|Changes|Implementation)[^\n]*\n([\s\S]*?)(?=\n##\s+[^#]|\n🔧|$)/gm,
processor: (m) => `## ${m[1]}\n${m[2]}`,
},
{
regex: /(^|\n)(All tasks completed[\s\S]*?)(?=\n🔧|\n📋|\n⚡|\n❌|$)/g,
processor: (m) => m[2],
},
{
regex:
/(^|\n)((I've|I have) (successfully |now )?(completed|finished|implemented)[\s\S]*?)(?=\n🔧|\n📋|\n⚡|\n❌|$)/g,
processor: (m) => m[2],
},
];
// Try other summary formats (Feature, Changes, Implementation) for (const { regex, processor } of regexesToTry) {
const otherHeaderMatch = rawOutput.match( const matches = [...cleanedOutput.matchAll(regex)];
/^##\s+(Feature|Changes|Implementation)\s*\n([\s\S]*?)(?=\n##\s+|$)/m if (matches.length > 0) {
); const lastMatch = matches[matches.length - 1];
if (otherHeaderMatch) { return cleanFragmentedText(processor(lastMatch)).trim();
return `## ${otherHeaderMatch[1]}\n${otherHeaderMatch[2].trim()}`;
} }
// Try to find summary introduction lines
const introMatch = rawOutput.match(
/(^|\n)(All tasks completed[\s\S]*?)(?=\n🔧|\n📋|\n⚡|\n❌|$)/
);
if (introMatch) {
return introMatch[2].trim();
}
const completionMatch = rawOutput.match(
/(^|\n)((I've|I have) (successfully |now )?(completed|finished|implemented)[\s\S]*?)(?=\n🔧|\n📋|\n⚡|\n❌|$)/
);
if (completionMatch) {
return completionMatch[2].trim();
} }
return null; return null;