mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-02-03 08:53:36 +00:00
feat: implement work mode selection in feature dialogs
- Added WorkModeSelector component to allow users to choose between 'current', 'auto', and 'custom' work modes for feature management. - Updated AddFeatureDialog and EditFeatureDialog to utilize the new work mode functionality, replacing the previous branch selector logic. - Enhanced useBoardActions hook to handle branch name generation based on the selected work mode. - Adjusted settings to default to using worktrees, improving the overall feature creation experience. These changes streamline the feature management process by providing clearer options for branch handling and worktree isolation.
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
// @ts-nocheck
|
||||
import { useState, useEffect } from 'react';
|
||||
import { useState, useEffect, useRef } from 'react';
|
||||
import { createLogger } from '@automaker/utils/logger';
|
||||
import {
|
||||
Dialog,
|
||||
@@ -46,11 +46,12 @@ import {
|
||||
import {
|
||||
TestingTabContent,
|
||||
PrioritySelector,
|
||||
BranchSelector,
|
||||
WorkModeSelector,
|
||||
PlanningModeSelect,
|
||||
AncestorContextSection,
|
||||
ProfileTypeahead,
|
||||
} from '../shared';
|
||||
import type { WorkMode } from '../shared';
|
||||
import { PhaseModelSelector } from '@/components/views/settings-view/model-defaults/phase-model-selector';
|
||||
import { ModelOverrideTrigger, useModelOverride } from '@/components/shared';
|
||||
import {
|
||||
@@ -84,6 +85,7 @@ type FeatureData = {
|
||||
planningMode: PlanningMode;
|
||||
requirePlanApproval: boolean;
|
||||
dependencies?: string[];
|
||||
workMode: WorkMode;
|
||||
};
|
||||
|
||||
interface AddFeatureDialogProps {
|
||||
@@ -123,7 +125,7 @@ export function AddFeatureDialog({
|
||||
}: AddFeatureDialogProps) {
|
||||
const isSpawnMode = !!parentFeature;
|
||||
const navigate = useNavigate();
|
||||
const [useCurrentBranch, setUseCurrentBranch] = useState(true);
|
||||
const [workMode, setWorkMode] = useState<WorkMode>('current');
|
||||
|
||||
// Form state
|
||||
const [title, setTitle] = useState('');
|
||||
@@ -161,22 +163,27 @@ export function AddFeatureDialog({
|
||||
const [selectedAncestorIds, setSelectedAncestorIds] = useState<Set<string>>(new Set());
|
||||
|
||||
// Get defaults from store
|
||||
const { defaultPlanningMode, defaultRequirePlanApproval, defaultAIProfileId, useWorktrees } =
|
||||
useAppStore();
|
||||
const { defaultPlanningMode, defaultRequirePlanApproval, defaultAIProfileId } = useAppStore();
|
||||
|
||||
// Enhancement model override
|
||||
const enhancementOverride = useModelOverride({ phase: 'enhancementModel' });
|
||||
|
||||
// Sync defaults when dialog opens
|
||||
// Track previous open state to detect when dialog opens
|
||||
const wasOpenRef = useRef(false);
|
||||
|
||||
// Sync defaults only when dialog opens (transitions from closed to open)
|
||||
useEffect(() => {
|
||||
if (open) {
|
||||
const justOpened = open && !wasOpenRef.current;
|
||||
wasOpenRef.current = open;
|
||||
|
||||
if (justOpened) {
|
||||
const defaultProfile = defaultAIProfileId
|
||||
? aiProfiles.find((p) => p.id === defaultAIProfileId)
|
||||
: null;
|
||||
|
||||
setSkipTests(defaultSkipTests);
|
||||
setBranchName(defaultBranch || '');
|
||||
setUseCurrentBranch(true);
|
||||
setWorkMode('current');
|
||||
setPlanningMode(defaultPlanningMode);
|
||||
setRequirePlanApproval(defaultRequirePlanApproval);
|
||||
|
||||
@@ -248,7 +255,7 @@ export function AddFeatureDialog({
|
||||
return null;
|
||||
}
|
||||
|
||||
if (useWorktrees && !useCurrentBranch && !branchName.trim()) {
|
||||
if (workMode === 'custom' && !branchName.trim()) {
|
||||
toast.error('Please select a branch name');
|
||||
return null;
|
||||
}
|
||||
@@ -262,7 +269,10 @@ export function AddFeatureDialog({
|
||||
? modelEntry.reasoningEffort || 'none'
|
||||
: 'none';
|
||||
|
||||
const finalBranchName = useCurrentBranch ? currentBranch || '' : branchName || '';
|
||||
// For 'current' mode, use empty string (work on current branch)
|
||||
// For 'auto' mode, use empty string (will be auto-generated in use-board-actions)
|
||||
// For 'custom' mode, use the specified branch name
|
||||
const finalBranchName = workMode === 'custom' ? branchName || '' : '';
|
||||
|
||||
// Build final description with ancestor context in spawn mode
|
||||
let finalDescription = description;
|
||||
@@ -303,6 +313,7 @@ export function AddFeatureDialog({
|
||||
planningMode,
|
||||
requirePlanApproval,
|
||||
dependencies: isSpawnMode && parentFeature ? [parentFeature.id] : undefined,
|
||||
workMode,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -318,7 +329,7 @@ export function AddFeatureDialog({
|
||||
setPriority(2);
|
||||
setSelectedProfileId(undefined);
|
||||
setModelEntry({ model: 'opus' });
|
||||
setUseCurrentBranch(true);
|
||||
setWorkMode('current');
|
||||
setPlanningMode(defaultPlanningMode);
|
||||
setRequirePlanApproval(defaultRequirePlanApproval);
|
||||
setPreviewMap(new Map());
|
||||
@@ -643,21 +654,19 @@ export function AddFeatureDialog({
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Branch Selector */}
|
||||
{useWorktrees && (
|
||||
<div className="pt-2">
|
||||
<BranchSelector
|
||||
useCurrentBranch={useCurrentBranch}
|
||||
onUseCurrentBranchChange={setUseCurrentBranch}
|
||||
branchName={branchName}
|
||||
onBranchNameChange={setBranchName}
|
||||
branchSuggestions={branchSuggestions}
|
||||
branchCardCounts={branchCardCounts}
|
||||
currentBranch={currentBranch}
|
||||
testIdPrefix="feature"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{/* Work Mode Selector */}
|
||||
<div className="pt-2">
|
||||
<WorkModeSelector
|
||||
workMode={workMode}
|
||||
onWorkModeChange={setWorkMode}
|
||||
branchName={branchName}
|
||||
onBranchNameChange={setBranchName}
|
||||
branchSuggestions={branchSuggestions}
|
||||
branchCardCounts={branchCardCounts}
|
||||
currentBranch={currentBranch}
|
||||
testIdPrefix="feature-work-mode"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -670,7 +679,7 @@ export function AddFeatureDialog({
|
||||
onClick={handleAddAndStart}
|
||||
variant="secondary"
|
||||
data-testid="confirm-add-and-start-feature"
|
||||
disabled={useWorktrees && !useCurrentBranch && !branchName.trim()}
|
||||
disabled={workMode === 'custom' && !branchName.trim()}
|
||||
>
|
||||
<Play className="w-4 h-4 mr-2" />
|
||||
Make
|
||||
@@ -681,7 +690,7 @@ export function AddFeatureDialog({
|
||||
hotkey={{ key: 'Enter', cmdCtrl: true }}
|
||||
hotkeyActive={open}
|
||||
data-testid="confirm-add-feature"
|
||||
disabled={useWorktrees && !useCurrentBranch && !branchName.trim()}
|
||||
disabled={workMode === 'custom' && !branchName.trim()}
|
||||
>
|
||||
{isSpawnMode ? 'Spawn Task' : 'Add Feature'}
|
||||
</HotkeyButton>
|
||||
|
||||
@@ -46,10 +46,11 @@ import type { ReasoningEffort, PhaseModelEntry, DescriptionHistoryEntry } from '
|
||||
import {
|
||||
TestingTabContent,
|
||||
PrioritySelector,
|
||||
BranchSelector,
|
||||
WorkModeSelector,
|
||||
PlanningModeSelect,
|
||||
ProfileTypeahead,
|
||||
} from '../shared';
|
||||
import type { WorkMode } from '../shared';
|
||||
import { PhaseModelSelector } from '@/components/views/settings-view/model-defaults/phase-model-selector';
|
||||
import { ModelOverrideTrigger, useModelOverride } from '@/components/shared';
|
||||
import {
|
||||
@@ -118,9 +119,11 @@ export function EditFeatureDialog({
|
||||
}: EditFeatureDialogProps) {
|
||||
const navigate = useNavigate();
|
||||
const [editingFeature, setEditingFeature] = useState<Feature | null>(feature);
|
||||
const [useCurrentBranch, setUseCurrentBranch] = useState(() => {
|
||||
// If feature has no branchName, default to using current branch
|
||||
return !feature?.branchName;
|
||||
// Derive initial workMode from feature's branchName
|
||||
const [workMode, setWorkMode] = useState<WorkMode>(() => {
|
||||
// If feature has a branchName, it's using 'custom' mode
|
||||
// Otherwise, it's on 'current' branch (no worktree isolation)
|
||||
return feature?.branchName ? 'custom' : 'current';
|
||||
});
|
||||
const [editFeaturePreviewMap, setEditFeaturePreviewMap] = useState<ImagePreviewMap>(
|
||||
() => new Map()
|
||||
@@ -156,9 +159,6 @@ export function EditFeatureDialog({
|
||||
// Track if history dropdown is open
|
||||
const [showHistory, setShowHistory] = useState(false);
|
||||
|
||||
// Get worktrees setting from store
|
||||
const { useWorktrees } = useAppStore();
|
||||
|
||||
// Enhancement model override
|
||||
const enhancementOverride = useModelOverride({ phase: 'enhancementModel' });
|
||||
|
||||
@@ -167,8 +167,8 @@ export function EditFeatureDialog({
|
||||
if (feature) {
|
||||
setPlanningMode(feature.planningMode ?? 'skip');
|
||||
setRequirePlanApproval(feature.requirePlanApproval ?? false);
|
||||
// If feature has no branchName, default to using current branch
|
||||
setUseCurrentBranch(!feature.branchName);
|
||||
// Derive workMode from feature's branchName
|
||||
setWorkMode(feature.branchName ? 'custom' : 'current');
|
||||
// Reset history tracking state
|
||||
setOriginalDescription(feature.description ?? '');
|
||||
setDescriptionChangeSource(null);
|
||||
@@ -222,14 +222,9 @@ export function EditFeatureDialog({
|
||||
const handleUpdate = () => {
|
||||
if (!editingFeature) return;
|
||||
|
||||
// Validate branch selection when "other branch" is selected and branch selector is enabled
|
||||
// Validate branch selection for custom mode
|
||||
const isBranchSelectorEnabled = editingFeature.status === 'backlog';
|
||||
if (
|
||||
useWorktrees &&
|
||||
isBranchSelectorEnabled &&
|
||||
!useCurrentBranch &&
|
||||
!editingFeature.branchName?.trim()
|
||||
) {
|
||||
if (isBranchSelectorEnabled && workMode === 'custom' && !editingFeature.branchName?.trim()) {
|
||||
toast.error('Please select a branch name');
|
||||
return;
|
||||
}
|
||||
@@ -242,12 +237,10 @@ export function EditFeatureDialog({
|
||||
? (modelEntry.reasoningEffort ?? 'none')
|
||||
: 'none';
|
||||
|
||||
// Use current branch if toggle is on
|
||||
// If currentBranch is provided (non-primary worktree), use it
|
||||
// Otherwise (primary worktree), use empty string which means "unassigned" (show only on primary)
|
||||
const finalBranchName = useCurrentBranch
|
||||
? currentBranch || ''
|
||||
: editingFeature.branchName || '';
|
||||
// For 'current' mode, use empty string (work on current branch)
|
||||
// For 'auto' mode, use empty string (will be auto-generated in use-board-actions)
|
||||
// For 'custom' mode, use the specified branch name
|
||||
const finalBranchName = workMode === 'custom' ? editingFeature.branchName || '' : '';
|
||||
|
||||
const updates = {
|
||||
title: editingFeature.title ?? '',
|
||||
@@ -263,6 +256,7 @@ export function EditFeatureDialog({
|
||||
priority: editingFeature.priority ?? 2,
|
||||
planningMode,
|
||||
requirePlanApproval,
|
||||
workMode,
|
||||
};
|
||||
|
||||
// Determine if description changed and what source to use
|
||||
@@ -688,27 +682,25 @@ export function EditFeatureDialog({
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Branch Selector */}
|
||||
{useWorktrees && (
|
||||
<div className="pt-2">
|
||||
<BranchSelector
|
||||
useCurrentBranch={useCurrentBranch}
|
||||
onUseCurrentBranchChange={setUseCurrentBranch}
|
||||
branchName={editingFeature.branchName ?? ''}
|
||||
onBranchNameChange={(value) =>
|
||||
setEditingFeature({
|
||||
...editingFeature,
|
||||
branchName: value,
|
||||
})
|
||||
}
|
||||
branchSuggestions={branchSuggestions}
|
||||
branchCardCounts={branchCardCounts}
|
||||
currentBranch={currentBranch}
|
||||
disabled={editingFeature.status !== 'backlog'}
|
||||
testIdPrefix="edit-feature"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{/* Work Mode Selector */}
|
||||
<div className="pt-2">
|
||||
<WorkModeSelector
|
||||
workMode={workMode}
|
||||
onWorkModeChange={setWorkMode}
|
||||
branchName={editingFeature.branchName ?? ''}
|
||||
onBranchNameChange={(value) =>
|
||||
setEditingFeature({
|
||||
...editingFeature,
|
||||
branchName: value,
|
||||
})
|
||||
}
|
||||
branchSuggestions={branchSuggestions}
|
||||
branchCardCounts={branchCardCounts}
|
||||
currentBranch={currentBranch}
|
||||
disabled={editingFeature.status !== 'backlog'}
|
||||
testIdPrefix="edit-feature-work-mode"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -731,9 +723,8 @@ export function EditFeatureDialog({
|
||||
hotkeyActive={!!editingFeature}
|
||||
data-testid="confirm-edit-feature"
|
||||
disabled={
|
||||
useWorktrees &&
|
||||
editingFeature.status === 'backlog' &&
|
||||
!useCurrentBranch &&
|
||||
workMode === 'custom' &&
|
||||
!editingFeature.branchName?.trim()
|
||||
}
|
||||
>
|
||||
|
||||
Reference in New Issue
Block a user