mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-02-02 20:43:36 +00:00
feat: enhance auto mode functionality with worktree support
- Updated auto mode handlers to support branch-specific operations, allowing for better management of features across different worktrees. - Introduced normalization of branch names to handle undefined values gracefully. - Enhanced status and response messages to reflect the current worktree context. - Updated the auto mode service to manage state and concurrency settings per worktree, improving user experience and flexibility. - Added UI elements to display current max concurrency for auto mode in both board and mobile views. This update aims to streamline the auto mode experience, making it more intuitive for users working with multiple branches and worktrees.
This commit is contained in:
@@ -29,6 +29,7 @@ import {
|
||||
Terminal,
|
||||
SquarePlus,
|
||||
SplitSquareHorizontal,
|
||||
Zap,
|
||||
} from 'lucide-react';
|
||||
import { toast } from 'sonner';
|
||||
import { cn } from '@/lib/utils';
|
||||
@@ -56,6 +57,8 @@ interface WorktreeActionsDropdownProps {
|
||||
gitRepoStatus: GitRepoStatus;
|
||||
/** When true, renders as a standalone button (not attached to another element) */
|
||||
standalone?: boolean;
|
||||
/** Whether auto mode is running for this worktree */
|
||||
isAutoModeRunning?: boolean;
|
||||
onOpenChange: (open: boolean) => void;
|
||||
onPull: (worktree: WorktreeInfo) => void;
|
||||
onPush: (worktree: WorktreeInfo) => void;
|
||||
@@ -73,6 +76,7 @@ interface WorktreeActionsDropdownProps {
|
||||
onOpenDevServerUrl: (worktree: WorktreeInfo) => void;
|
||||
onViewDevServerLogs: (worktree: WorktreeInfo) => void;
|
||||
onRunInitScript: (worktree: WorktreeInfo) => void;
|
||||
onToggleAutoMode?: (worktree: WorktreeInfo) => void;
|
||||
hasInitScript: boolean;
|
||||
}
|
||||
|
||||
@@ -88,6 +92,7 @@ export function WorktreeActionsDropdown({
|
||||
devServerInfo,
|
||||
gitRepoStatus,
|
||||
standalone = false,
|
||||
isAutoModeRunning = false,
|
||||
onOpenChange,
|
||||
onPull,
|
||||
onPush,
|
||||
@@ -105,6 +110,7 @@ export function WorktreeActionsDropdown({
|
||||
onOpenDevServerUrl,
|
||||
onViewDevServerLogs,
|
||||
onRunInitScript,
|
||||
onToggleAutoMode,
|
||||
hasInitScript,
|
||||
}: WorktreeActionsDropdownProps) {
|
||||
// Get available editors for the "Open In" submenu
|
||||
@@ -214,6 +220,26 @@ export function WorktreeActionsDropdown({
|
||||
<DropdownMenuSeparator />
|
||||
</>
|
||||
)}
|
||||
{/* Auto Mode toggle */}
|
||||
{onToggleAutoMode && (
|
||||
<>
|
||||
{isAutoModeRunning ? (
|
||||
<DropdownMenuItem onClick={() => onToggleAutoMode(worktree)} className="text-xs">
|
||||
<span className="flex items-center mr-2">
|
||||
<Zap className="w-3.5 h-3.5 text-yellow-500" />
|
||||
<span className="ml-0.5 h-2 w-2 rounded-full bg-green-500 animate-pulse" />
|
||||
</span>
|
||||
Stop Auto Mode
|
||||
</DropdownMenuItem>
|
||||
) : (
|
||||
<DropdownMenuItem onClick={() => onToggleAutoMode(worktree)} className="text-xs">
|
||||
<Zap className="w-3.5 h-3.5 mr-2" />
|
||||
Start Auto Mode
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
<DropdownMenuSeparator />
|
||||
</>
|
||||
)}
|
||||
<TooltipWrapper showTooltip={!!gitOpsDisabledReason} tooltipContent={gitOpsDisabledReason}>
|
||||
<DropdownMenuItem
|
||||
onClick={() => canPerformGitOps && onPull(worktree)}
|
||||
|
||||
@@ -29,6 +29,8 @@ interface WorktreeTabProps {
|
||||
aheadCount: number;
|
||||
behindCount: number;
|
||||
gitRepoStatus: GitRepoStatus;
|
||||
/** Whether auto mode is running for this worktree */
|
||||
isAutoModeRunning?: boolean;
|
||||
onSelectWorktree: (worktree: WorktreeInfo) => void;
|
||||
onBranchDropdownOpenChange: (open: boolean) => void;
|
||||
onActionsDropdownOpenChange: (open: boolean) => void;
|
||||
@@ -51,6 +53,7 @@ interface WorktreeTabProps {
|
||||
onOpenDevServerUrl: (worktree: WorktreeInfo) => void;
|
||||
onViewDevServerLogs: (worktree: WorktreeInfo) => void;
|
||||
onRunInitScript: (worktree: WorktreeInfo) => void;
|
||||
onToggleAutoMode?: (worktree: WorktreeInfo) => void;
|
||||
hasInitScript: boolean;
|
||||
}
|
||||
|
||||
@@ -75,6 +78,7 @@ export function WorktreeTab({
|
||||
aheadCount,
|
||||
behindCount,
|
||||
gitRepoStatus,
|
||||
isAutoModeRunning = false,
|
||||
onSelectWorktree,
|
||||
onBranchDropdownOpenChange,
|
||||
onActionsDropdownOpenChange,
|
||||
@@ -97,6 +101,7 @@ export function WorktreeTab({
|
||||
onOpenDevServerUrl,
|
||||
onViewDevServerLogs,
|
||||
onRunInitScript,
|
||||
onToggleAutoMode,
|
||||
hasInitScript,
|
||||
}: WorktreeTabProps) {
|
||||
let prBadge: JSX.Element | null = null;
|
||||
@@ -332,6 +337,26 @@ export function WorktreeTab({
|
||||
</TooltipProvider>
|
||||
)}
|
||||
|
||||
{isAutoModeRunning && (
|
||||
<TooltipProvider>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<span
|
||||
className={cn(
|
||||
'flex items-center justify-center h-7 px-1.5 rounded-none border-r-0',
|
||||
isSelected ? 'bg-primary text-primary-foreground' : 'bg-secondary/50'
|
||||
)}
|
||||
>
|
||||
<span className="h-2 w-2 rounded-full bg-green-500 animate-pulse" />
|
||||
</span>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<p>Auto Mode Running</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
)}
|
||||
|
||||
<WorktreeActionsDropdown
|
||||
worktree={worktree}
|
||||
isSelected={isSelected}
|
||||
@@ -343,6 +368,7 @@ export function WorktreeTab({
|
||||
isDevServerRunning={isDevServerRunning}
|
||||
devServerInfo={devServerInfo}
|
||||
gitRepoStatus={gitRepoStatus}
|
||||
isAutoModeRunning={isAutoModeRunning}
|
||||
onOpenChange={onActionsDropdownOpenChange}
|
||||
onPull={onPull}
|
||||
onPush={onPush}
|
||||
@@ -360,6 +386,7 @@ export function WorktreeTab({
|
||||
onOpenDevServerUrl={onOpenDevServerUrl}
|
||||
onViewDevServerLogs={onViewDevServerLogs}
|
||||
onRunInitScript={onRunInitScript}
|
||||
onToggleAutoMode={onToggleAutoMode}
|
||||
hasInitScript={hasInitScript}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -2,7 +2,7 @@ import { useEffect, useRef, useCallback, useState } from 'react';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { GitBranch, Plus, RefreshCw } from 'lucide-react';
|
||||
import { Spinner } from '@/components/ui/spinner';
|
||||
import { cn, pathsEqual } from '@/lib/utils';
|
||||
import { pathsEqual } from '@/lib/utils';
|
||||
import { toast } from 'sonner';
|
||||
import { getHttpApiClient } from '@/lib/http-api-client';
|
||||
import { useIsMobile } from '@/hooks/use-media-query';
|
||||
@@ -21,6 +21,7 @@ import {
|
||||
WorktreeActionsDropdown,
|
||||
BranchSwitchDropdown,
|
||||
} from './components';
|
||||
import { useAppStore } from '@/store/app-store';
|
||||
|
||||
export function WorktreePanel({
|
||||
projectPath,
|
||||
@@ -50,7 +51,6 @@ export function WorktreePanel({
|
||||
|
||||
const {
|
||||
isStartingDevServer,
|
||||
getWorktreeKey,
|
||||
isDevServerRunning,
|
||||
getDevServerInfo,
|
||||
handleStartDevServer,
|
||||
@@ -92,6 +92,67 @@ export function WorktreePanel({
|
||||
features,
|
||||
});
|
||||
|
||||
// Auto-mode state management using the store
|
||||
// Use separate selectors to avoid creating new object references on each render
|
||||
const autoModeByWorktree = useAppStore((state) => state.autoModeByWorktree);
|
||||
const currentProject = useAppStore((state) => state.currentProject);
|
||||
|
||||
// Helper to generate worktree key for auto-mode (inlined to avoid selector issues)
|
||||
const getAutoModeWorktreeKey = useCallback(
|
||||
(projectId: string, branchName: string | null): string => {
|
||||
return `${projectId}::${branchName ?? '__main__'}`;
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
// Helper to check if auto-mode is running for a specific worktree
|
||||
const isAutoModeRunningForWorktree = useCallback(
|
||||
(worktree: WorktreeInfo): boolean => {
|
||||
if (!currentProject) return false;
|
||||
const branchName = worktree.isMain ? null : worktree.branch;
|
||||
const key = getAutoModeWorktreeKey(currentProject.id, branchName);
|
||||
return autoModeByWorktree[key]?.isRunning ?? false;
|
||||
},
|
||||
[currentProject, autoModeByWorktree, getAutoModeWorktreeKey]
|
||||
);
|
||||
|
||||
// Handler to toggle auto-mode for a worktree
|
||||
const handleToggleAutoMode = useCallback(
|
||||
async (worktree: WorktreeInfo) => {
|
||||
if (!currentProject) return;
|
||||
|
||||
// Import the useAutoMode to get start/stop functions
|
||||
// Since useAutoMode is a hook, we'll use the API client directly
|
||||
const api = getHttpApiClient();
|
||||
const branchName = worktree.isMain ? null : worktree.branch;
|
||||
const isRunning = isAutoModeRunningForWorktree(worktree);
|
||||
|
||||
try {
|
||||
if (isRunning) {
|
||||
const result = await api.autoMode.stop(projectPath, branchName);
|
||||
if (result.success) {
|
||||
const desc = branchName ? `worktree ${branchName}` : 'main branch';
|
||||
toast.success(`Auto Mode stopped for ${desc}`);
|
||||
} else {
|
||||
toast.error(result.error || 'Failed to stop Auto Mode');
|
||||
}
|
||||
} else {
|
||||
const result = await api.autoMode.start(projectPath, branchName);
|
||||
if (result.success) {
|
||||
const desc = branchName ? `worktree ${branchName}` : 'main branch';
|
||||
toast.success(`Auto Mode started for ${desc}`);
|
||||
} else {
|
||||
toast.error(result.error || 'Failed to start Auto Mode');
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
toast.error('Error toggling Auto Mode');
|
||||
console.error('Auto mode toggle error:', error);
|
||||
}
|
||||
},
|
||||
[currentProject, projectPath, isAutoModeRunningForWorktree]
|
||||
);
|
||||
|
||||
// Track whether init script exists for the project
|
||||
const [hasInitScript, setHasInitScript] = useState(false);
|
||||
|
||||
@@ -244,6 +305,7 @@ export function WorktreePanel({
|
||||
isDevServerRunning={isDevServerRunning(selectedWorktree)}
|
||||
devServerInfo={getDevServerInfo(selectedWorktree)}
|
||||
gitRepoStatus={gitRepoStatus}
|
||||
isAutoModeRunning={isAutoModeRunningForWorktree(selectedWorktree)}
|
||||
onOpenChange={handleActionsDropdownOpenChange(selectedWorktree)}
|
||||
onPull={handlePull}
|
||||
onPush={handlePush}
|
||||
@@ -261,6 +323,7 @@ export function WorktreePanel({
|
||||
onOpenDevServerUrl={handleOpenDevServerUrl}
|
||||
onViewDevServerLogs={handleViewDevServerLogs}
|
||||
onRunInitScript={handleRunInitScript}
|
||||
onToggleAutoMode={handleToggleAutoMode}
|
||||
hasInitScript={hasInitScript}
|
||||
/>
|
||||
)}
|
||||
@@ -328,6 +391,7 @@ export function WorktreePanel({
|
||||
aheadCount={aheadCount}
|
||||
behindCount={behindCount}
|
||||
gitRepoStatus={gitRepoStatus}
|
||||
isAutoModeRunning={isAutoModeRunningForWorktree(mainWorktree)}
|
||||
onSelectWorktree={handleSelectWorktree}
|
||||
onBranchDropdownOpenChange={handleBranchDropdownOpenChange(mainWorktree)}
|
||||
onActionsDropdownOpenChange={handleActionsDropdownOpenChange(mainWorktree)}
|
||||
@@ -350,6 +414,7 @@ export function WorktreePanel({
|
||||
onOpenDevServerUrl={handleOpenDevServerUrl}
|
||||
onViewDevServerLogs={handleViewDevServerLogs}
|
||||
onRunInitScript={handleRunInitScript}
|
||||
onToggleAutoMode={handleToggleAutoMode}
|
||||
hasInitScript={hasInitScript}
|
||||
/>
|
||||
)}
|
||||
@@ -388,6 +453,7 @@ export function WorktreePanel({
|
||||
aheadCount={aheadCount}
|
||||
behindCount={behindCount}
|
||||
gitRepoStatus={gitRepoStatus}
|
||||
isAutoModeRunning={isAutoModeRunningForWorktree(worktree)}
|
||||
onSelectWorktree={handleSelectWorktree}
|
||||
onBranchDropdownOpenChange={handleBranchDropdownOpenChange(worktree)}
|
||||
onActionsDropdownOpenChange={handleActionsDropdownOpenChange(worktree)}
|
||||
@@ -410,6 +476,7 @@ export function WorktreePanel({
|
||||
onOpenDevServerUrl={handleOpenDevServerUrl}
|
||||
onViewDevServerLogs={handleViewDevServerLogs}
|
||||
onRunInitScript={handleRunInitScript}
|
||||
onToggleAutoMode={handleToggleAutoMode}
|
||||
hasInitScript={hasInitScript}
|
||||
/>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user