From b18ca801746f8f654c63f94c18c834efa547555b Mon Sep 17 00:00:00 2001 From: Auto Date: Sun, 11 Jan 2026 11:01:02 +0200 Subject: [PATCH] fix: hide AssistantFAB during spec creation mode The Chat AI Assistant button (AssistantFAB) was appearing on top of the full-screen spec creation chat overlay, causing a visual bug where the button would overlap with the Send input area. Changes: - Add onStepChange callback prop to NewProjectModal to notify parent when the modal step changes - Add onSpecCreatingChange callback prop to ProjectSelector to propagate spec creation state up to App.tsx - Add isSpecCreating state to App.tsx to track when spec creation chat is active - Update AssistantFAB render condition to include !isSpecCreating - Disable 'A' keyboard shortcut during spec creation mode The fix propagates the spec creation state through the component hierarchy: NewProjectModal -> ProjectSelector -> App.tsx, allowing the FAB to be hidden when step === 'chat' in the new project modal. Co-Authored-By: Claude Opus 4.5 --- ui/src/App.tsx | 12 ++++++----- ui/src/components/NewProjectModal.tsx | 30 +++++++++++++++++---------- ui/src/components/ProjectSelector.tsx | 3 +++ 3 files changed, 29 insertions(+), 16 deletions(-) diff --git a/ui/src/App.tsx b/ui/src/App.tsx index fec9305..328a31b 100644 --- a/ui/src/App.tsx +++ b/ui/src/App.tsx @@ -39,6 +39,7 @@ function App() { const [debugPanelHeight, setDebugPanelHeight] = useState(288) // Default height const [assistantOpen, setAssistantOpen] = useState(false) const [showSettings, setShowSettings] = useState(false) + const [isSpecCreating, setIsSpecCreating] = useState(false) const queryClient = useQueryClient() const { data: projects, isLoading: projectsLoading } = useProjects() @@ -100,8 +101,8 @@ function App() { setShowExpandProject(true) } - // A : Toggle assistant panel (when project selected) - if ((e.key === 'a' || e.key === 'A') && selectedProject) { + // A : Toggle assistant panel (when project selected and not in spec creation) + if ((e.key === 'a' || e.key === 'A') && selectedProject && !isSpecCreating) { e.preventDefault() setAssistantOpen(prev => !prev) } @@ -132,7 +133,7 @@ function App() { window.addEventListener('keydown', handleKeyDown) return () => window.removeEventListener('keydown', handleKeyDown) - }, [selectedProject, showAddFeature, showExpandProject, selectedFeature, debugOpen, assistantOpen, features, showSettings]) + }, [selectedProject, showAddFeature, showExpandProject, selectedFeature, debugOpen, assistantOpen, features, showSettings, isSpecCreating]) // Combine WebSocket progress with feature data const progress = wsState.progress.total > 0 ? wsState.progress : { @@ -167,6 +168,7 @@ function App() { selectedProject={selectedProject} onSelectProject={handleSelectProject} isLoading={projectsLoading} + onSpecCreatingChange={setIsSpecCreating} /> {selectedProject && ( @@ -290,8 +292,8 @@ function App() { /> )} - {/* Assistant FAB and Panel - hide FAB when expand modal is open */} - {selectedProject && !showExpandProject && ( + {/* Assistant FAB and Panel - hide when expand modal or spec creation is open */} + {selectedProject && !showExpandProject && !isSpecCreating && ( <> setAssistantOpen(!assistantOpen)} diff --git a/ui/src/components/NewProjectModal.tsx b/ui/src/components/NewProjectModal.tsx index 2590d7e..b517fa5 100644 --- a/ui/src/components/NewProjectModal.tsx +++ b/ui/src/components/NewProjectModal.tsx @@ -25,12 +25,14 @@ interface NewProjectModalProps { isOpen: boolean onClose: () => void onProjectCreated: (projectName: string) => void + onStepChange?: (step: Step) => void } export function NewProjectModal({ isOpen, onClose, onProjectCreated, + onStepChange, }: NewProjectModalProps) { const [step, setStep] = useState('name') const [projectName, setProjectName] = useState('') @@ -46,6 +48,12 @@ export function NewProjectModal({ const createProject = useCreateProject() + // Wrapper to notify parent of step changes + const changeStep = (newStep: Step) => { + setStep(newStep) + onStepChange?.(newStep) + } + if (!isOpen) return null const handleNameSubmit = (e: React.FormEvent) => { @@ -63,18 +71,18 @@ export function NewProjectModal({ } setError(null) - setStep('folder') + changeStep('folder') } const handleFolderSelect = (path: string) => { // Append project name to the selected path const fullPath = path.endsWith('/') ? `${path}${projectName.trim()}` : `${path}/${projectName.trim()}` setProjectPath(fullPath) - setStep('method') + changeStep('method') } const handleFolderCancel = () => { - setStep('name') + changeStep('name') } const handleMethodSelect = async (method: SpecMethod) => { @@ -82,7 +90,7 @@ export function NewProjectModal({ if (!projectPath) { setError('Please select a project folder first') - setStep('folder') + changeStep('folder') return } @@ -94,7 +102,7 @@ export function NewProjectModal({ path: projectPath, specMethod: 'manual', }) - setStep('complete') + changeStep('complete') setTimeout(() => { onProjectCreated(project.name) handleClose() @@ -110,7 +118,7 @@ export function NewProjectModal({ path: projectPath, specMethod: 'claude', }) - setStep('chat') + changeStep('chat') } catch (err: unknown) { setError(err instanceof Error ? err.message : 'Failed to create project') } @@ -125,7 +133,7 @@ export function NewProjectModal({ try { await startAgent(projectName.trim(), yoloMode) // Success - navigate to project - setStep('complete') + changeStep('complete') setTimeout(() => { onProjectCreated(projectName.trim()) handleClose() @@ -144,7 +152,7 @@ export function NewProjectModal({ const handleChatCancel = () => { // Go back to method selection but keep the project - setStep('method') + changeStep('method') setSpecMethod(null) } @@ -155,7 +163,7 @@ export function NewProjectModal({ } const handleClose = () => { - setStep('name') + changeStep('name') setProjectName('') setProjectPath(null) setSpecMethod(null) @@ -168,10 +176,10 @@ export function NewProjectModal({ const handleBack = () => { if (step === 'method') { - setStep('folder') + changeStep('folder') setSpecMethod(null) } else if (step === 'folder') { - setStep('name') + changeStep('name') setProjectPath(null) } } diff --git a/ui/src/components/ProjectSelector.tsx b/ui/src/components/ProjectSelector.tsx index 8525a63..3c50769 100644 --- a/ui/src/components/ProjectSelector.tsx +++ b/ui/src/components/ProjectSelector.tsx @@ -10,6 +10,7 @@ interface ProjectSelectorProps { selectedProject: string | null onSelectProject: (name: string | null) => void isLoading: boolean + onSpecCreatingChange?: (isCreating: boolean) => void } export function ProjectSelector({ @@ -17,6 +18,7 @@ export function ProjectSelector({ selectedProject, onSelectProject, isLoading, + onSpecCreatingChange, }: ProjectSelectorProps) { const [isOpen, setIsOpen] = useState(false) const [showNewProjectModal, setShowNewProjectModal] = useState(false) @@ -166,6 +168,7 @@ export function ProjectSelector({ isOpen={showNewProjectModal} onClose={() => setShowNewProjectModal(false)} onProjectCreated={handleProjectCreated} + onStepChange={(step) => onSpecCreatingChange?.(step === 'chat')} /> {/* Delete Confirmation Dialog */}