// @ts-nocheck import { useState, useEffect } from 'react'; import { createLogger } from '@automaker/utils/logger'; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from '@/components/ui/dialog'; import { Button } from '@/components/ui/button'; import { HotkeyButton } from '@/components/ui/hotkey-button'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { Checkbox } from '@/components/ui/checkbox'; import { CategoryAutocomplete } from '@/components/ui/category-autocomplete'; import { DescriptionImageDropZone, FeatureImagePath as DescriptionImagePath, FeatureTextFilePath as DescriptionTextFilePath, ImagePreviewMap, } from '@/components/ui/description-image-dropzone'; import { Collapsible, CollapsibleContent, CollapsibleTrigger } from '@/components/ui/collapsible'; import { Sparkles, ChevronDown, ChevronRight, GitBranch, History, Cpu, FolderKanban, } from 'lucide-react'; import { toast } from 'sonner'; import { getElectronAPI } from '@/lib/electron'; import { cn, modelSupportsThinking } from '@/lib/utils'; import { Feature, ModelAlias, ThinkingLevel, useAppStore, PlanningMode } from '@/store/app-store'; import type { ReasoningEffort, PhaseModelEntry, DescriptionHistoryEntry } from '@automaker/types'; import { TestingTabContent, PrioritySelector, WorkModeSelector, PlanningModeSelect, } 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 { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, } from '@/components/ui/dropdown-menu'; import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover'; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip'; import { DependencyTreeDialog } from './dependency-tree-dialog'; import { isClaudeModel, supportsReasoningEffort } from '@automaker/types'; const logger = createLogger('EditFeatureDialog'); interface EditFeatureDialogProps { feature: Feature | null; onClose: () => void; onUpdate: ( featureId: string, updates: { title: string; category: string; description: string; skipTests: boolean; model: ModelAlias; thinkingLevel: ThinkingLevel; reasoningEffort: ReasoningEffort; imagePaths: DescriptionImagePath[]; textFilePaths: DescriptionTextFilePath[]; branchName: string; // Can be empty string to use current branch priority: number; planningMode: PlanningMode; requirePlanApproval: boolean; }, descriptionHistorySource?: 'enhance' | 'edit', enhancementMode?: 'improve' | 'technical' | 'simplify' | 'acceptance' ) => void; categorySuggestions: string[]; branchSuggestions: string[]; branchCardCounts?: Record; // Map of branch name to unarchived card count currentBranch?: string; isMaximized: boolean; allFeatures: Feature[]; } export function EditFeatureDialog({ feature, onClose, onUpdate, categorySuggestions, branchSuggestions, branchCardCounts, currentBranch, isMaximized, allFeatures, }: EditFeatureDialogProps) { const [editingFeature, setEditingFeature] = useState(feature); // Derive initial workMode from feature's branchName const [workMode, setWorkMode] = useState(() => { // 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( () => new Map() ); const [isEnhancing, setIsEnhancing] = useState(false); const [enhancementMode, setEnhancementMode] = useState< 'improve' | 'technical' | 'simplify' | 'acceptance' >('improve'); const [enhanceOpen, setEnhanceOpen] = useState(false); const [showDependencyTree, setShowDependencyTree] = useState(false); const [planningMode, setPlanningMode] = useState(feature?.planningMode ?? 'skip'); const [requirePlanApproval, setRequirePlanApproval] = useState( feature?.requirePlanApproval ?? false ); // Model selection state const [modelEntry, setModelEntry] = useState(() => ({ model: (feature?.model as ModelAlias) || 'opus', thinkingLevel: feature?.thinkingLevel || 'none', reasoningEffort: feature?.reasoningEffort || 'none', })); // Check if current model supports planning mode (Claude/Anthropic only) const modelSupportsPlanningMode = isClaudeModel(modelEntry.model); // Track the source of description changes for history const [descriptionChangeSource, setDescriptionChangeSource] = useState< { source: 'enhance'; mode: 'improve' | 'technical' | 'simplify' | 'acceptance' } | 'edit' | null >(null); // Track the original description when the dialog opened for comparison const [originalDescription, setOriginalDescription] = useState(feature?.description ?? ''); // Track if history dropdown is open const [showHistory, setShowHistory] = useState(false); // Enhancement model override const enhancementOverride = useModelOverride({ phase: 'enhancementModel' }); useEffect(() => { setEditingFeature(feature); if (feature) { setPlanningMode(feature.planningMode ?? 'skip'); setRequirePlanApproval(feature.requirePlanApproval ?? false); // Derive workMode from feature's branchName setWorkMode(feature.branchName ? 'custom' : 'current'); // Reset history tracking state setOriginalDescription(feature.description ?? ''); setDescriptionChangeSource(null); setShowHistory(false); setEnhanceOpen(false); // Reset model entry setModelEntry({ model: (feature.model as ModelAlias) || 'opus', thinkingLevel: feature.thinkingLevel || 'none', reasoningEffort: feature.reasoningEffort || 'none', }); } else { setEditFeaturePreviewMap(new Map()); setDescriptionChangeSource(null); setShowHistory(false); } }, [feature]); const handleModelChange = (entry: PhaseModelEntry) => { setModelEntry(entry); }; const handleUpdate = () => { if (!editingFeature) return; // Validate branch selection for custom mode const isBranchSelectorEnabled = editingFeature.status === 'backlog'; if (isBranchSelectorEnabled && workMode === 'custom' && !editingFeature.branchName?.trim()) { toast.error('Please select a branch name'); return; } const selectedModel = modelEntry.model; const normalizedThinking: ThinkingLevel = modelSupportsThinking(selectedModel) ? (modelEntry.thinkingLevel ?? 'none') : 'none'; const normalizedReasoning: ReasoningEffort = supportsReasoningEffort(selectedModel) ? (modelEntry.reasoningEffort ?? 'none') : 'none'; // 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 ?? '', category: editingFeature.category, description: editingFeature.description, skipTests: editingFeature.skipTests ?? false, model: selectedModel, thinkingLevel: normalizedThinking, reasoningEffort: normalizedReasoning, imagePaths: editingFeature.imagePaths ?? [], textFilePaths: editingFeature.textFilePaths ?? [], branchName: finalBranchName, priority: editingFeature.priority ?? 2, planningMode, requirePlanApproval, workMode, }; // Determine if description changed and what source to use const descriptionChanged = editingFeature.description !== originalDescription; let historySource: 'enhance' | 'edit' | undefined; let historyEnhancementMode: 'improve' | 'technical' | 'simplify' | 'acceptance' | undefined; if (descriptionChanged && descriptionChangeSource) { if (descriptionChangeSource === 'edit') { historySource = 'edit'; } else { historySource = 'enhance'; historyEnhancementMode = descriptionChangeSource.mode; } } onUpdate(editingFeature.id, updates, historySource, historyEnhancementMode); setEditFeaturePreviewMap(new Map()); onClose(); }; const handleDialogClose = (open: boolean) => { if (!open) { onClose(); } }; const handleEnhanceDescription = async () => { if (!editingFeature?.description.trim() || isEnhancing) return; setIsEnhancing(true); try { const api = getElectronAPI(); const result = await api.enhancePrompt?.enhance( editingFeature.description, enhancementMode, enhancementOverride.effectiveModel, // API accepts string, extract from PhaseModelEntry enhancementOverride.effectiveModelEntry.thinkingLevel // Pass thinking level ); if (result?.success && result.enhancedText) { const enhancedText = result.enhancedText; setEditingFeature((prev) => (prev ? { ...prev, description: enhancedText } : prev)); // Track that this change was from enhancement setDescriptionChangeSource({ source: 'enhance', mode: enhancementMode }); toast.success('Description enhanced!'); } else { toast.error(result?.error || 'Failed to enhance description'); } } catch (error) { logger.error('Enhancement failed:', error); toast.error('Failed to enhance description'); } finally { setIsEnhancing(false); } }; if (!editingFeature) { return null; } // Shared card styling const cardClass = 'rounded-lg border border-border/50 bg-muted/30 p-4 space-y-3'; const sectionHeaderClass = 'flex items-center gap-2 text-sm font-medium text-foreground'; return ( { const target = e.target as HTMLElement; if (target.closest('[data-testid="category-autocomplete-list"]')) { e.preventDefault(); } }} onInteractOutside={(e: CustomEvent) => { const target = e.target as HTMLElement; if (target.closest('[data-testid="category-autocomplete-list"]')) { e.preventDefault(); } }} > Edit Feature Modify the feature details.
{/* Task Details Section */}
{/* Version History Button */} {feature?.descriptionHistory && feature.descriptionHistory.length > 0 && (

Version History

Click a version to restore it

{[...(feature.descriptionHistory || [])] .reverse() .map((entry: DescriptionHistoryEntry, index: number) => { const isCurrentVersion = entry.description === editingFeature.description; const date = new Date(entry.timestamp); const formattedDate = date.toLocaleDateString(undefined, { month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit', }); const sourceLabel = entry.source === 'initial' ? 'Original' : entry.source === 'enhance' ? `Enhanced (${entry.enhancementMode || 'improve'})` : 'Edited'; return ( ); })}
)}
{ setEditingFeature({ ...editingFeature, description: value, }); // Track that this change was a manual edit (unless already enhanced) if (!descriptionChangeSource || descriptionChangeSource === 'edit') { setDescriptionChangeSource('edit'); } }} images={editingFeature.imagePaths ?? []} onImagesChange={(images) => setEditingFeature({ ...editingFeature, imagePaths: images, }) } textFiles={editingFeature.textFilePaths ?? []} onTextFilesChange={(textFiles) => setEditingFeature({ ...editingFeature, textFilePaths: textFiles, }) } placeholder="Describe the feature..." previewMap={editFeaturePreviewMap} onPreviewMapChange={setEditFeaturePreviewMap} data-testid="edit-feature-description" />
setEditingFeature({ ...editingFeature, title: e.target.value, }) } placeholder="Leave blank to auto-generate" data-testid="edit-feature-title" />
{/* Collapsible Enhancement Section */}
setEnhancementMode('improve')}> Improve Clarity setEnhancementMode('technical')}> Add Technical Details setEnhancementMode('simplify')}> Simplify setEnhancementMode('acceptance')}> Add Acceptance Criteria
{/* AI & Execution Section */}
AI & Execution
{modelSupportsPlanningMode ? ( ) : (
{}} testIdPrefix="edit-feature-planning" compact disabled />

Planning modes are only available for Claude Provider

)}
setEditingFeature({ ...editingFeature, skipTests: !checked }) } data-testid="edit-feature-skip-tests-checkbox" />
setRequirePlanApproval(!!checked)} disabled={ !modelSupportsPlanningMode || planningMode === 'skip' || planningMode === 'lite' } data-testid="edit-feature-require-approval-checkbox" />
{/* Organization Section */}
Organization
setEditingFeature({ ...editingFeature, category: value, }) } suggestions={categorySuggestions} placeholder="e.g., Core, UI, API" data-testid="edit-feature-category" />
setEditingFeature({ ...editingFeature, priority, }) } testIdPrefix="edit-priority" />
{/* Work Mode Selector */}
setEditingFeature({ ...editingFeature, branchName: value, }) } branchSuggestions={branchSuggestions} branchCardCounts={branchCardCounts} currentBranch={currentBranch} disabled={editingFeature.status !== 'backlog'} testIdPrefix="edit-feature-work-mode" />
Save Changes
setShowDependencyTree(false)} feature={editingFeature} allFeatures={allFeatures} />
); }