feat: enhance spec regeneration management by project

- Refactored spec regeneration status tracking to support multiple projects using a Map for running states and abort controllers.
- Updated `getSpecRegenerationStatus` to accept a project path, allowing retrieval of status specific to a project.
- Modified `setRunningState` to manage running states and abort controllers per project.
- Adjusted related route handlers to utilize project-specific status checks and updates.
- Introduced a new Graph View page and integrated it into the routing structure.
- Enhanced UI components to reflect the current project’s spec generation state.
This commit is contained in:
webdevcody
2026-01-11 01:37:26 -05:00
parent 7115460804
commit 5f3db1f25e
25 changed files with 890 additions and 588 deletions

View File

@@ -1,4 +1,5 @@
import type { NavigateOptions } from '@tanstack/react-router';
import { Loader2 } from 'lucide-react';
import { cn } from '@/lib/utils';
import { formatShortcut } from '@/store/app-store';
import type { NavSection } from '../types';
@@ -80,14 +81,23 @@ export function SidebarNavigation({
data-testid={`nav-${item.id}`}
>
<div className="relative">
<Icon
className={cn(
'w-[18px] h-[18px] shrink-0 transition-all duration-200',
isActive
? 'text-brand-500 drop-shadow-sm'
: 'group-hover:text-brand-400 group-hover:scale-110'
)}
/>
{item.isLoading ? (
<Loader2
className={cn(
'w-[18px] h-[18px] shrink-0 animate-spin',
isActive ? 'text-brand-500' : 'text-muted-foreground'
)}
/>
) : (
<Icon
className={cn(
'w-[18px] h-[18px] shrink-0 transition-all duration-200',
isActive
? 'text-brand-500 drop-shadow-sm'
: 'group-hover:text-brand-400 group-hover:scale-110'
)}
/>
)}
{/* Count badge for collapsed state */}
{!sidebarOpen && item.count !== undefined && item.count > 0 && (
<span

View File

@@ -1,3 +1,4 @@
import { useRef } from 'react';
import { Rocket, CheckCircle2, Zap, FileText, Sparkles, ArrowRight } from 'lucide-react';
import {
Dialog,
@@ -24,13 +25,25 @@ export function OnboardingDialog({
onSkip,
onGenerateSpec,
}: OnboardingDialogProps) {
// Track if we're closing because user clicked "Generate App Spec"
// to avoid incorrectly calling onSkip
const isGeneratingRef = useRef(false);
const handleGenerateSpec = () => {
isGeneratingRef.current = true;
onGenerateSpec();
};
return (
<Dialog
open={open}
onOpenChange={(isOpen) => {
if (!isOpen) {
if (!isOpen && !isGeneratingRef.current) {
// Only call onSkip when user dismisses dialog (escape, click outside, or skip button)
// NOT when they click "Generate App Spec"
onSkip();
}
isGeneratingRef.current = false;
onOpenChange(isOpen);
}}
>
@@ -108,7 +121,7 @@ export function OnboardingDialog({
Skip for now
</Button>
<Button
onClick={onGenerateSpec}
onClick={handleGenerateSpec}
className="bg-gradient-to-r from-brand-500 to-brand-600 hover:from-brand-600 hover:to-brand-600 text-white border-0"
>
<Sparkles className="w-4 h-4 mr-2" />

View File

@@ -10,6 +10,7 @@ import {
GitPullRequest,
Lightbulb,
Brain,
Network,
} from 'lucide-react';
import type { NavSection, NavItem } from '../types';
import type { KeyboardShortcut } from '@/hooks/use-keyboard-shortcuts';
@@ -27,6 +28,7 @@ interface UseNavigationProps {
context: string;
memory: string;
board: string;
graph: string;
agent: string;
terminal: string;
settings: string;
@@ -48,6 +50,8 @@ interface UseNavigationProps {
cycleNextProject: () => void;
/** Count of unviewed validations to show on GitHub Issues nav item */
unviewedValidationsCount?: number;
/** Whether spec generation is currently running for the current project */
isSpecGenerating?: boolean;
}
export function useNavigation({
@@ -65,6 +69,7 @@ export function useNavigation({
cyclePrevProject,
cycleNextProject,
unviewedValidationsCount,
isSpecGenerating,
}: UseNavigationProps) {
// Track if current project has a GitHub remote
const [hasGitHubRemote, setHasGitHubRemote] = useState(false);
@@ -104,6 +109,7 @@ export function useNavigation({
label: 'Spec Editor',
icon: FileText,
shortcut: shortcuts.spec,
isLoading: isSpecGenerating,
},
{
id: 'context',
@@ -138,6 +144,12 @@ export function useNavigation({
icon: LayoutGrid,
shortcut: shortcuts.board,
},
{
id: 'graph',
label: 'Graph View',
icon: Network,
shortcut: shortcuts.graph,
},
{
id: 'agent',
label: 'Agent Runner',
@@ -197,6 +209,7 @@ export function useNavigation({
hideTerminal,
hasGitHubRemote,
unviewedValidationsCount,
isSpecGenerating,
]);
// Build keyboard shortcuts for navigation

View File

@@ -13,6 +13,8 @@ export interface NavItem {
shortcut?: string;
/** Optional count badge to display next to the nav item */
count?: number;
/** Whether this nav item is in a loading state (shows spinner) */
isLoading?: boolean;
}
export interface SortableProjectItemProps {