import { useState, useCallback } from 'react'; import { useAppStore } from '@/store/app-store'; import { Spinner } from '@/components/ui/spinner'; // Extracted hooks import { useSpecLoading, useSpecSave, useSpecGeneration, useSpecParser } from './spec-view/hooks'; // Extracted components import { SpecHeader, SpecEditor, SpecEmptyState, SpecViewMode, SpecEditMode, SpecModeTabs, } from './spec-view/components'; // Extracted dialogs import { CreateSpecDialog, RegenerateSpecDialog } from './spec-view/dialogs'; // Types import type { SpecViewMode as SpecViewModeType } from './spec-view/types'; export function SpecView() { const { currentProject, appSpec } = useAppStore(); // View mode state - default to 'view' const [mode, setMode] = useState('view'); // Actions panel state (for tablet/mobile) const [showActionsPanel, setShowActionsPanel] = useState(false); // Loading state const { isLoading, specExists, isGenerationRunning, loadSpec } = useSpecLoading(); // Save state const { isSaving, hasChanges, saveSpec, handleChange } = useSpecSave(); // Parse the spec XML const { isValid: isParseValid, parsedSpec, errors: parseErrors } = useSpecParser(appSpec); // Generation state and handlers const { // Dialog visibility showCreateDialog, setShowCreateDialog, showRegenerateDialog, setShowRegenerateDialog, // Create state projectOverview, setProjectOverview, isCreating, generateFeatures, setGenerateFeatures, analyzeProjectOnCreate, setAnalyzeProjectOnCreate, featureCountOnCreate, setFeatureCountOnCreate, // Regenerate state projectDefinition, setProjectDefinition, isRegenerating, generateFeaturesOnRegenerate, setGenerateFeaturesOnRegenerate, analyzeProjectOnRegenerate, setAnalyzeProjectOnRegenerate, featureCountOnRegenerate, setFeatureCountOnRegenerate, // Feature generation isGeneratingFeatures, // Sync isSyncing, // Status currentPhase, errorMessage, // Handlers handleCreateSpec, handleRegenerate, handleGenerateFeatures, handleSync, } = useSpecGeneration({ loadSpec }); // Handle mode change - if parse is invalid, force source mode const handleModeChange = useCallback( (newMode: SpecViewModeType) => { if ((newMode === 'view' || newMode === 'edit') && !isParseValid) { // Can't switch to view/edit if parse is invalid return; } setMode(newMode); }, [isParseValid] ); // No project selected if (!currentProject) { return (

No project selected

); } // Loading state if (isLoading) { return (
); } // Empty state - only show when spec doesn't exist AND no generation is running // If generation is running but no spec exists, show the generating UI if (!specExists) { // If generation is running (from loading hook check), ensure we show the generating UI const showAsGenerating = isCreating || isGenerationRunning; return ( <> setShowCreateDialog(true)} /> ); } // Render content based on mode const renderContent = () => { // If the XML is invalid or spec is not parsed, we can only show the source editor. // The tabs for other modes are disabled, but this is an extra safeguard. if (!isParseValid || !parsedSpec) { return ; } switch (mode) { case 'view': return ; case 'edit': return ; case 'source': default: return ; } }; const isProcessing = isRegenerating || isGenerationRunning || isCreating || isGeneratingFeatures || isSyncing; // Main view - spec exists return (
setShowRegenerateDialog(true)} onGenerateFeaturesClick={handleGenerateFeatures} onSyncClick={handleSync} onSaveClick={saveSpec} showActionsPanel={showActionsPanel} onToggleActionsPanel={() => setShowActionsPanel(!showActionsPanel)} showSaveButton={mode !== 'view'} /> {/* Mode tabs and content container */}
{/* Mode tabs bar - inside the content area, centered */} {!isProcessing && (
{/* Show parse error indicator - positioned to the right */} {!isParseValid && parseErrors.length > 0 && ( XML has errors - fix in Source mode )}
)} {/* Show parse error banner if in source mode with errors */} {!isParseValid && parseErrors.length > 0 && mode === 'source' && (
XML Parse Errors: {parseErrors.join(', ')}
)} {renderContent()}
); }