Generated by Spark: Fix all reported errors.

This commit is contained in:
2026-01-16 17:44:36 +00:00
committed by GitHub
parent a4ab812087
commit c390727763
8 changed files with 160 additions and 132 deletions

View File

@@ -191,7 +191,7 @@ function App() {
const [nextjsConfig, setNextjsConfig] = useKV<NextJsConfig>('project-nextjs-config', DEFAULT_NEXTJS_CONFIG)
const [npmSettings, setNpmSettings] = useKV<NpmSettings>('project-npm-settings', DEFAULT_NPM_SETTINGS)
const [featureToggles, setFeatureToggles] = useKV<FeatureToggles>('project-feature-toggles', DEFAULT_FEATURE_TOGGLES)
const [activeFileId, setActiveFileId] = useState<string | null>((files || [])[0]?.id || null)
const [activeFileId, setActiveFileId] = useState<string | null>(null)
const [activeTab, setActiveTab] = useState('dashboard')
const [exportDialogOpen, setExportDialogOpen] = useState(false)
const [shortcutsDialogOpen, setShortcutsDialogOpen] = useState(false)
@@ -199,21 +199,27 @@ function App() {
const [generatedCode, setGeneratedCode] = useState<Record<string, string>>({})
const [lastSaved, setLastSaved] = useState<number | null>(Date.now())
const safeFiles = files || []
const safeModels = models || []
const safeComponents = components || []
const safeComponentTrees = componentTrees || []
const safeWorkflows = workflows || []
const safeLambdas = lambdas || []
const safeTheme = (theme && theme.variants && theme.variants.length > 0) ? theme : DEFAULT_THEME
const safePlaywrightTests = playwrightTests || []
const safeStorybookStories = storybookStories || []
const safeUnitTests = unitTests || []
const safeFiles = Array.isArray(files) ? files : []
const safeModels = Array.isArray(models) ? models : []
const safeComponents = Array.isArray(components) ? components : []
const safeComponentTrees = Array.isArray(componentTrees) ? componentTrees : []
const safeWorkflows = Array.isArray(workflows) ? workflows : []
const safeLambdas = Array.isArray(lambdas) ? lambdas : []
const safeTheme = (theme && theme.variants && Array.isArray(theme.variants) && theme.variants.length > 0) ? theme : DEFAULT_THEME
const safePlaywrightTests = Array.isArray(playwrightTests) ? playwrightTests : []
const safeStorybookStories = Array.isArray(storybookStories) ? storybookStories : []
const safeUnitTests = Array.isArray(unitTests) ? unitTests : []
const safeFlaskConfig = flaskConfig || DEFAULT_FLASK_CONFIG
const safeNextjsConfig = nextjsConfig || DEFAULT_NEXTJS_CONFIG
const safeNpmSettings = npmSettings || DEFAULT_NPM_SETTINGS
const safeFeatureToggles = featureToggles || DEFAULT_FEATURE_TOGGLES
useEffect(() => {
if (safeFiles.length > 0 && !activeFileId) {
setActiveFileId(safeFiles[0].id)
}
}, [safeFiles, activeFileId])
useEffect(() => {
const params = new URLSearchParams(window.location.search)
const shortcut = params.get('shortcut')
@@ -253,7 +259,8 @@ function App() {
featureToggles,
])
const { errors: autoDetectedErrors } = useAutoRepair(safeFiles, false)
const { errors: autoDetectedErrors = [] } = useAutoRepair(safeFiles, false)
const errorCount = Array.isArray(autoDetectedErrors) ? autoDetectedErrors.length : 0
useKeyboardShortcuts([
{
@@ -552,7 +559,7 @@ Navigate to the backend directory and follow the setup instructions.
activeTab={activeTab}
onTabChange={setActiveTab}
featureToggles={safeFeatureToggles}
errorCount={autoDetectedErrors.length}
errorCount={errorCount}
lastSaved={lastSaved}
currentProject={getCurrentProject()}
onProjectLoad={handleLoadProject}

View File

@@ -38,63 +38,61 @@ export function PWAInstallPrompt() {
localStorage.setItem('pwa-install-dismissed', 'true')
}
if (!isInstallable || dismissed || !showPrompt) {
return null
}
return (
<AnimatePresence>
<motion.div
initial={{ opacity: 0, y: 50 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: 50 }}
className="fixed bottom-4 right-4 z-50 max-w-sm"
>
<Card className="p-6 shadow-lg border-2 border-primary/20 bg-card/95 backdrop-blur">
<div className="flex gap-4">
<div className="flex-shrink-0">
<div className="w-12 h-12 rounded-full bg-gradient-to-br from-primary to-accent flex items-center justify-center">
<Download size={24} weight="duotone" className="text-white" />
</div>
</div>
<div className="flex-1">
<div className="flex items-start justify-between mb-2">
<h3 className="font-bold text-lg">Install CodeForge</h3>
<Button
variant="ghost"
size="icon"
className="h-6 w-6 -mt-1"
onClick={handleDismiss}
>
<X size={16} />
</Button>
</div>
<p className="text-sm text-muted-foreground mb-4">
Install our app for a faster, offline-capable experience with quick access from your device.
</p>
<div className="flex gap-2 text-xs text-muted-foreground mb-4">
<div className="flex items-center gap-1">
<Desktop size={16} />
<span>Works offline</span>
</div>
<div className="flex items-center gap-1">
<DeviceMobile size={16} />
<span>Quick access</span>
{isInstallable && !dismissed && showPrompt && (
<motion.div
initial={{ opacity: 0, y: 50 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: 50 }}
className="fixed bottom-4 right-4 z-50 max-w-sm"
>
<Card className="p-6 shadow-lg border-2 border-primary/20 bg-card/95 backdrop-blur">
<div className="flex gap-4">
<div className="flex-shrink-0">
<div className="w-12 h-12 rounded-full bg-gradient-to-br from-primary to-accent flex items-center justify-center">
<Download size={24} weight="duotone" className="text-white" />
</div>
</div>
<div className="flex gap-2">
<Button onClick={handleInstall} className="flex-1">
<Download size={16} className="mr-2" />
Install
</Button>
<Button variant="outline" onClick={handleDismiss}>
Not now
</Button>
<div className="flex-1">
<div className="flex items-start justify-between mb-2">
<h3 className="font-bold text-lg">Install CodeForge</h3>
<Button
variant="ghost"
size="icon"
className="h-6 w-6 -mt-1"
onClick={handleDismiss}
>
<X size={16} />
</Button>
</div>
<p className="text-sm text-muted-foreground mb-4">
Install our app for a faster, offline-capable experience with quick access from your device.
</p>
<div className="flex gap-2 text-xs text-muted-foreground mb-4">
<div className="flex items-center gap-1">
<Desktop size={16} />
<span>Works offline</span>
</div>
<div className="flex items-center gap-1">
<DeviceMobile size={16} />
<span>Quick access</span>
</div>
</div>
<div className="flex gap-2">
<Button onClick={handleInstall} className="flex-1">
<Download size={16} className="mr-2" />
Install
</Button>
<Button variant="outline" onClick={handleDismiss}>
Not now
</Button>
</div>
</div>
</div>
</div>
</Card>
</motion.div>
</Card>
</motion.div>
)}
</AnimatePresence>
)
}

View File

@@ -10,13 +10,13 @@ export function PWAStatusBar() {
useEffect(() => {
if (!isOnline) {
setShowOffline(true)
} else {
} else if (showOffline) {
const timer = setTimeout(() => {
setShowOffline(false)
}, 3000)
return () => clearTimeout(timer)
}
}, [isOnline])
}, [isOnline, showOffline])
return (
<AnimatePresence>

View File

@@ -17,52 +17,50 @@ export function PWAUpdatePrompt() {
setDismissed(true)
}
if (!isUpdateAvailable || dismissed) {
return null
}
return (
<AnimatePresence>
<motion.div
initial={{ opacity: 0, y: -50 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -50 }}
className="fixed top-4 right-4 z-50 max-w-sm"
>
<Card className="p-4 shadow-lg border-2 border-accent/20 bg-card/95 backdrop-blur">
<div className="flex items-start gap-3">
<div className="flex-shrink-0">
<div className="w-10 h-10 rounded-full bg-gradient-to-br from-accent to-primary flex items-center justify-center">
<CloudArrowDown size={20} weight="duotone" className="text-white" />
{isUpdateAvailable && !dismissed && (
<motion.div
initial={{ opacity: 0, y: -50 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -50 }}
className="fixed top-4 right-4 z-50 max-w-sm"
>
<Card className="p-4 shadow-lg border-2 border-accent/20 bg-card/95 backdrop-blur">
<div className="flex items-start gap-3">
<div className="flex-shrink-0">
<div className="w-10 h-10 rounded-full bg-gradient-to-br from-accent to-primary flex items-center justify-center">
<CloudArrowDown size={20} weight="duotone" className="text-white" />
</div>
</div>
<div className="flex-1">
<div className="flex items-start justify-between mb-1">
<h3 className="font-semibold">Update Available</h3>
<Button
variant="ghost"
size="icon"
className="h-6 w-6 -mt-1"
onClick={handleDismiss}
>
<X size={14} />
</Button>
</div>
<p className="text-xs text-muted-foreground mb-3">
A new version is ready. Update now for the latest features and fixes.
</p>
<div className="flex gap-2">
<Button size="sm" onClick={handleUpdate} className="flex-1">
Update Now
</Button>
<Button size="sm" variant="outline" onClick={handleDismiss}>
Later
</Button>
</div>
</div>
</div>
<div className="flex-1">
<div className="flex items-start justify-between mb-1">
<h3 className="font-semibold">Update Available</h3>
<Button
variant="ghost"
size="icon"
className="h-6 w-6 -mt-1"
onClick={handleDismiss}
>
<X size={14} />
</Button>
</div>
<p className="text-xs text-muted-foreground mb-3">
A new version is ready. Update now for the latest features and fixes.
</p>
<div className="flex gap-2">
<Button size="sm" onClick={handleUpdate} className="flex-1">
Update Now
</Button>
<Button size="sm" variant="outline" onClick={handleDismiss}>
Later
</Button>
</div>
</div>
</div>
</Card>
</motion.div>
</Card>
</motion.div>
)}
</AnimatePresence>
)
}

View File

@@ -7,12 +7,16 @@ interface StatusIconProps {
}
export function StatusIcon({ type, size = 14, animate = false }: StatusIconProps) {
const baseClassName = type === 'saved' ? 'text-accent' : ''
const animateClassName = animate ? 'animate-in zoom-in duration-200' : ''
const className = [baseClassName, animateClassName].filter(Boolean).join(' ')
if (type === 'saved') {
return (
<CheckCircle
size={size}
weight="fill"
className={`text-accent ${animate ? 'animate-in zoom-in duration-200' : ''}`}
className={className}
/>
)
}

View File

@@ -11,20 +11,25 @@ export function useAutoRepair(
const [isScanning, setIsScanning] = useState(false)
const scanFiles = useCallback(async () => {
if (!enabled || files.length === 0) return
if (!enabled || !files || files.length === 0) return
setIsScanning(true)
try {
const allErrors: CodeError[] = []
for (const file of files) {
const fileErrors = await ErrorRepairService.detectErrors(file)
allErrors.push(...fileErrors)
if (file && file.content) {
const fileErrors = await ErrorRepairService.detectErrors(file)
if (Array.isArray(fileErrors)) {
allErrors.push(...fileErrors)
}
}
}
setErrors(allErrors)
} catch (error) {
console.error('Auto-scan failed:', error)
setErrors([])
} finally {
setIsScanning(false)
}
@@ -41,7 +46,7 @@ export function useAutoRepair(
}, [files, enabled, scanFiles])
return {
errors,
errors: Array.isArray(errors) ? errors : [],
isScanning,
scanFiles,
}

View File

@@ -11,18 +11,24 @@ interface KeyboardShortcut {
export function useKeyboardShortcuts(shortcuts: KeyboardShortcut[]) {
useEffect(() => {
const handleKeyDown = (event: KeyboardEvent) => {
for (const shortcut of shortcuts) {
const ctrlMatch = shortcut.ctrl ? (event.ctrlKey || event.metaKey) : !event.ctrlKey && !event.metaKey
const shiftMatch = shortcut.shift ? event.shiftKey : !event.shiftKey
const altMatch = shortcut.alt ? event.altKey : !event.altKey
const keyMatch = event.key.toLowerCase() === shortcut.key.toLowerCase()
if (typeof window === 'undefined') return
if (ctrlMatch && shiftMatch && altMatch && keyMatch) {
event.preventDefault()
shortcut.action()
break
const handleKeyDown = (event: KeyboardEvent) => {
try {
for (const shortcut of shortcuts) {
const ctrlMatch = shortcut.ctrl ? (event.ctrlKey || event.metaKey) : !event.ctrlKey && !event.metaKey
const shiftMatch = shortcut.shift ? event.shiftKey : !event.shiftKey
const altMatch = shortcut.alt ? event.altKey : !event.altKey
const keyMatch = event.key.toLowerCase() === shortcut.key.toLowerCase()
if (ctrlMatch && shiftMatch && altMatch && keyMatch) {
event.preventDefault()
shortcut.action()
break
}
}
} catch (error) {
console.error('[Keyboard Shortcuts] Error handling keydown:', error)
}
}
@@ -35,7 +41,7 @@ export function getShortcutDisplay(shortcut: Omit<KeyboardShortcut, 'action'>):
const parts: string[] = []
if (shortcut.ctrl) {
parts.push(navigator.platform.includes('Mac') ? '⌘' : 'Ctrl')
parts.push(typeof navigator !== 'undefined' && navigator.platform.includes('Mac') ? '⌘' : 'Ctrl')
}
if (shortcut.shift) {
parts.push('Shift')

View File

@@ -17,26 +17,36 @@ export function usePWA() {
const [state, setState] = useState<PWAState>({
isInstallable: false,
isInstalled: false,
isOnline: navigator.onLine,
isOnline: typeof navigator !== 'undefined' ? navigator.onLine : true,
isUpdateAvailable: false,
registration: null,
})
const [deferredPrompt, setDeferredPrompt] = useState<BeforeInstallPromptEvent | null>(null)
useEffect(() => {
if (typeof window === 'undefined') return
const checkInstalled = () => {
const isStandalone = window.matchMedia('(display-mode: standalone)').matches
const isIOSStandalone = (window.navigator as any).standalone === true
setState(prev => ({ ...prev, isInstalled: isStandalone || isIOSStandalone }))
try {
const isStandalone = window.matchMedia && window.matchMedia('(display-mode: standalone)').matches
const isIOSStandalone = (window.navigator as any).standalone === true
setState(prev => ({ ...prev, isInstalled: isStandalone || isIOSStandalone }))
} catch (error) {
console.error('[PWA] Error checking install status:', error)
}
}
checkInstalled()
const handleBeforeInstallPrompt = (e: Event) => {
e.preventDefault()
const installEvent = e as BeforeInstallPromptEvent
setDeferredPrompt(installEvent)
setState(prev => ({ ...prev, isInstallable: true }))
try {
e.preventDefault()
const installEvent = e as BeforeInstallPromptEvent
setDeferredPrompt(installEvent)
setState(prev => ({ ...prev, isInstallable: true }))
} catch (error) {
console.error('[PWA] Error handling beforeinstallprompt:', error)
}
}
const handleAppInstalled = () => {