mirror of
https://github.com/leonvanzyl/autocoder.git
synced 2026-03-19 11:53:09 +00:00
Migrate UI component library from custom implementations to shadcn/ui: - Add shadcn/ui primitives (Button, Card, Dialog, Input, etc.) - Replace custom styles with Tailwind CSS v4 theme configuration - Remove custom-theme.css in favor of globals.css with @theme directive Fix scroll overflow issues in multiple components: - ProjectSelector: "New Project" button no longer overlays project list - FolderBrowser: folder list now scrolls properly within modal - AgentCard: log modal content stays within bounds - ConversationHistory: conversation list scrolls correctly - KanbanColumn: feature cards scroll within fixed height - ScheduleModal: schedule form content scrolls properly Key technical changes: - Replace ScrollArea component with native overflow-y-auto divs - Add min-h-0 to flex containers to allow proper shrinking - Restructure dropdown layouts with flex-col for fixed footers New files: - ui/components.json (shadcn/ui configuration) - ui/src/components/ui/* (20 UI primitive components) - ui/src/lib/utils.ts (cn utility for class merging) - ui/tsconfig.app.json (app-specific TypeScript config) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
151 lines
4.8 KiB
TypeScript
151 lines
4.8 KiB
TypeScript
import { Globe, Square, Loader2, ExternalLink, AlertTriangle } from 'lucide-react'
|
|
import { useMutation, useQueryClient } from '@tanstack/react-query'
|
|
import type { DevServerStatus } from '../lib/types'
|
|
import { startDevServer, stopDevServer } from '../lib/api'
|
|
import { Button } from '@/components/ui/button'
|
|
|
|
// Re-export DevServerStatus from lib/types for consumers that import from here
|
|
export type { DevServerStatus }
|
|
|
|
// ============================================================================
|
|
// React Query Hooks (Internal)
|
|
// ============================================================================
|
|
|
|
/**
|
|
* Internal hook to start the dev server for a project.
|
|
* Invalidates the dev-server-status query on success.
|
|
*/
|
|
function useStartDevServer(projectName: string) {
|
|
const queryClient = useQueryClient()
|
|
|
|
return useMutation({
|
|
mutationFn: () => startDevServer(projectName),
|
|
onSuccess: () => {
|
|
queryClient.invalidateQueries({ queryKey: ['dev-server-status', projectName] })
|
|
},
|
|
})
|
|
}
|
|
|
|
/**
|
|
* Internal hook to stop the dev server for a project.
|
|
* Invalidates the dev-server-status query on success.
|
|
*/
|
|
function useStopDevServer(projectName: string) {
|
|
const queryClient = useQueryClient()
|
|
|
|
return useMutation({
|
|
mutationFn: () => stopDevServer(projectName),
|
|
onSuccess: () => {
|
|
queryClient.invalidateQueries({ queryKey: ['dev-server-status', projectName] })
|
|
},
|
|
})
|
|
}
|
|
|
|
// ============================================================================
|
|
// Component
|
|
// ============================================================================
|
|
|
|
interface DevServerControlProps {
|
|
projectName: string
|
|
status: DevServerStatus
|
|
url: string | null
|
|
}
|
|
|
|
/**
|
|
* DevServerControl provides start/stop controls for a project's development server.
|
|
*
|
|
* Features:
|
|
* - Toggle button to start/stop the dev server
|
|
* - Shows loading state during operations
|
|
* - Displays clickable URL when server is running
|
|
* - Uses neobrutalism design with cyan accent when running
|
|
*/
|
|
export function DevServerControl({ projectName, status, url }: DevServerControlProps) {
|
|
const startDevServerMutation = useStartDevServer(projectName)
|
|
const stopDevServerMutation = useStopDevServer(projectName)
|
|
|
|
const isLoading = startDevServerMutation.isPending || stopDevServerMutation.isPending
|
|
|
|
const handleStart = () => {
|
|
// Clear any previous errors before starting
|
|
stopDevServerMutation.reset()
|
|
startDevServerMutation.mutate()
|
|
}
|
|
const handleStop = () => {
|
|
// Clear any previous errors before stopping
|
|
startDevServerMutation.reset()
|
|
stopDevServerMutation.mutate()
|
|
}
|
|
|
|
// Server is stopped when status is 'stopped' or 'crashed' (can restart)
|
|
const isStopped = status === 'stopped' || status === 'crashed'
|
|
// Server is in a running state
|
|
const isRunning = status === 'running'
|
|
// Server has crashed
|
|
const isCrashed = status === 'crashed'
|
|
|
|
return (
|
|
<div className="flex items-center gap-2">
|
|
{isStopped ? (
|
|
<Button
|
|
onClick={handleStart}
|
|
disabled={isLoading}
|
|
variant={isCrashed ? "destructive" : "outline"}
|
|
size="sm"
|
|
title={isCrashed ? "Dev Server Crashed - Click to Restart" : "Start Dev Server"}
|
|
aria-label={isCrashed ? "Restart Dev Server (crashed)" : "Start Dev Server"}
|
|
>
|
|
{isLoading ? (
|
|
<Loader2 size={18} className="animate-spin" />
|
|
) : isCrashed ? (
|
|
<AlertTriangle size={18} />
|
|
) : (
|
|
<Globe size={18} />
|
|
)}
|
|
</Button>
|
|
) : (
|
|
<Button
|
|
onClick={handleStop}
|
|
disabled={isLoading}
|
|
size="sm"
|
|
className="bg-primary text-primary-foreground hover:bg-primary/90"
|
|
title="Stop Dev Server"
|
|
aria-label="Stop Dev Server"
|
|
>
|
|
{isLoading ? (
|
|
<Loader2 size={18} className="animate-spin" />
|
|
) : (
|
|
<Square size={18} />
|
|
)}
|
|
</Button>
|
|
)}
|
|
|
|
{/* Show URL as clickable link when server is running */}
|
|
{isRunning && url && (
|
|
<Button
|
|
asChild
|
|
size="sm"
|
|
className="bg-primary text-primary-foreground hover:bg-primary/90 gap-1"
|
|
>
|
|
<a
|
|
href={url}
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
title={`Open ${url} in new tab`}
|
|
>
|
|
<span className="font-mono text-xs">{url}</span>
|
|
<ExternalLink size={14} />
|
|
</a>
|
|
</Button>
|
|
)}
|
|
|
|
{/* Error display */}
|
|
{(startDevServerMutation.error || stopDevServerMutation.error) && (
|
|
<span className="text-xs font-mono text-destructive ml-2">
|
|
{String((startDevServerMutation.error || stopDevServerMutation.error)?.message || 'Operation failed')}
|
|
</span>
|
|
)}
|
|
</div>
|
|
)
|
|
}
|