docs: nextjs,frontends,tsx (92 files)

This commit is contained in:
2025-12-25 22:18:36 +00:00
parent 568f94d61b
commit cd0bbf9e36
92 changed files with 728 additions and 378 deletions

View File

@@ -8,7 +8,7 @@
},
"scripts": {
"dev": "next dev",
"build": "next build",
"build": "next build --webpack",
"start": "next start",
"kill": "fuser -k 3000/tcp",
"typecheck": "tsc --noEmit",

View File

@@ -8,7 +8,7 @@
import { useState, useEffect } from 'react'
import { Toaster } from '@/components/ui/sonner'
import { Button } from '@/components/ui/button'
import { Button } from '@/components/ui'
// Import level-specific UI components
import { Level1 } from '@/components/Level1'
import { Level2 } from '@/components/Level2'

View File

@@ -1,8 +1,8 @@
import { useState, useEffect } from 'react'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
import { Button } from '@/components/ui/button'
import { Badge } from '@/components/ui/badge'
import { ScrollArea } from '@/components/ui/scroll-area'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui'
import { Button } from '@/components/ui'
import { Badge } from '@/components/ui'
import { ScrollArea } from '@/components/ui'
import { SecureDatabase, SecurityContext, AuditLog } from '@/lib/secure-db-layer'
import { ShieldCheck, Clock, User, ChartLine, Warning } from '@phosphor-icons/react'
import type { User as UserType } from '@/lib/level-types'

View File

@@ -1,6 +1,6 @@
import { useState } from 'react'
import { useKV } from '@github/spark/hooks'
import { Button } from '@/components/ui/button'
import { Button } from '@/components/ui'
import { ComponentCatalog } from './ComponentCatalog'
import { Canvas } from './Canvas'
import { PropertyInspector } from './PropertyInspector'

View File

@@ -1,7 +1,7 @@
import { useState } from 'react'
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from '@/components/ui/dialog'
import { Button } from '@/components/ui/button'
import { Alert, AlertDescription } from '@/components/ui/alert'
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from '@/components/ui'
import { Button } from '@/components/ui'
import { Alert, AlertDescription } from '@/components/ui'
import Editor from '@monaco-editor/react'
import { FloppyDisk, X, ShieldCheck, Warning } from '@phosphor-icons/react'
import { securityScanner, type SecurityScanResult } from '@/lib/security-scanner'

View File

@@ -1,14 +1,14 @@
import { useState, useEffect } from 'react'
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import { Label } from '@/components/ui/label'
import { Textarea } from '@/components/ui/textarea'
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from '@/components/ui/dialog'
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'
import { Switch } from '@/components/ui/switch'
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
import { ScrollArea } from '@/components/ui/scroll-area'
import { Button } from '@/components/ui'
import { Input } from '@/components/ui'
import { Label } from '@/components/ui'
import { Textarea } from '@/components/ui'
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from '@/components/ui'
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui'
import { Switch } from '@/components/ui'
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui'
import { ScrollArea } from '@/components/ui'
import { Database, ComponentNode, ComponentConfig } from '@/lib/database'
import { componentCatalog } from '@/lib/component-catalog'
import { toast } from 'sonner'

View File

@@ -1,10 +1,10 @@
import { useState, useEffect } from 'react'
import { Button } from '@/components/ui/button'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'
import { Badge } from '@/components/ui/badge'
import { ScrollArea } from '@/components/ui/scroll-area'
import { Separator } from '@/components/ui/separator'
import { Button } from '@/components/ui'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui'
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui'
import { Badge } from '@/components/ui'
import { ScrollArea } from '@/components/ui'
import { Separator } from '@/components/ui'
import {
Tree,
Plus,

View File

@@ -1,11 +1,11 @@
import { useState, useEffect, useMemo } from 'react'
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from '@/components/ui/dialog'
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import { Label } from '@/components/ui/label'
import { ScrollArea } from '@/components/ui/scroll-area'
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
import { Badge } from '@/components/ui/badge'
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from '@/components/ui'
import { Button } from '@/components/ui'
import { Input } from '@/components/ui'
import { Label } from '@/components/ui'
import { ScrollArea } from '@/components/ui'
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui'
import { Badge } from '@/components/ui'
import { Database } from '@/lib/database'
import { Plus, X, FloppyDisk } from '@phosphor-icons/react'
import { toast } from 'sonner'

View File

@@ -1,12 +1,12 @@
import { useState, useEffect } from 'react'
import { Card } from '@/components/ui/card'
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import { Label } from '@/components/ui/label'
import { Badge } from '@/components/ui/badge'
import { ScrollArea } from '@/components/ui/scroll-area'
import { Separator } from '@/components/ui/separator'
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from '@/components/ui/dialog'
import { Card } from '@/components/ui'
import { Button } from '@/components/ui'
import { Input } from '@/components/ui'
import { Label } from '@/components/ui'
import { Badge } from '@/components/ui'
import { ScrollArea } from '@/components/ui'
import { Separator } from '@/components/ui'
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from '@/components/ui'
import { Database, CssCategory } from '@/lib/database'
import { Plus, X, Pencil, Trash, FloppyDisk } from '@phosphor-icons/react'
import { toast } from 'sonner'

View File

@@ -6,12 +6,12 @@
*/
import { useState } from 'react'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import { Label } from '@/components/ui/label'
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
import { Badge } from '@/components/ui/badge'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui'
import { Button } from '@/components/ui'
import { Input } from '@/components/ui'
import { Label } from '@/components/ui'
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui'
import { Badge } from '@/components/ui'
import { toast } from 'sonner'
import { useKVStore, useBlobStorage, useCachedData } from '@/hooks/useDBAL'

View File

@@ -1,9 +1,9 @@
import { useState, useEffect } from 'react'
import { Button } from '@/components/ui/button'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
import { Badge } from '@/components/ui/badge'
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
import { ScrollArea } from '@/components/ui/scroll-area'
import { Button } from '@/components/ui'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui'
import { Badge } from '@/components/ui'
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui'
import { ScrollArea } from '@/components/ui'
import { Database, DB_KEYS } from '@/lib/database'
import { toast } from 'sonner'
import {

View File

@@ -1,12 +1,12 @@
import { useState, useEffect } from 'react'
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from '@/components/ui/dialog'
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import { Label } from '@/components/ui/label'
import { ScrollArea } from '@/components/ui/scroll-area'
import { Card } from '@/components/ui/card'
import { Badge } from '@/components/ui/badge'
import { Separator } from '@/components/ui/separator'
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from '@/components/ui'
import { Button } from '@/components/ui'
import { Input } from '@/components/ui'
import { Label } from '@/components/ui'
import { ScrollArea } from '@/components/ui'
import { Card } from '@/components/ui'
import { Badge } from '@/components/ui'
import { Separator } from '@/components/ui'
import { Database } from '@/lib/database'
import { Plus, X, FloppyDisk, Trash, Pencil } from '@phosphor-icons/react'
import { toast } from 'sonner'

View File

@@ -1,8 +1,8 @@
import { Input } from '@/components/ui/input'
import { Textarea } from '@/components/ui/textarea'
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'
import { Switch } from '@/components/ui/switch'
import { Label } from '@/components/ui/label'
import { Input } from '@/components/ui'
import { Textarea } from '@/components/ui'
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui'
import { Switch } from '@/components/ui'
import { Label } from '@/components/ui'
import type { FieldSchema, SchemaConfig } from '@/lib/schema-types'
import { getFieldLabel, getHelpText, findModel, getModelLabel } from '@/lib/schema-utils'
import { useKV } from '@github/spark/hooks'

View File

@@ -1,7 +1,7 @@
import { useState, useEffect } from 'react'
import { Button } from '@/components/ui/button'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
import { Button } from '@/components/ui'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui'
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui'
import { RenderComponent } from '@/components/RenderComponent'
import { SignOut, House, List, X } from '@phosphor-icons/react'
import { toast } from 'sonner'

View File

@@ -1,14 +1,14 @@
import { useEffect } from 'react'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
import { Button } from '@/components/ui/button'
import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert'
import { Skeleton } from '@/components/ui/skeleton'
import { Badge } from '@/components/ui/badge'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui'
import { Button } from '@/components/ui'
import { Alert, AlertDescription, AlertTitle } from '@/components/ui'
import { Skeleton } from '@/components/ui'
import { Badge } from '@/components/ui'
import { ArrowClockwise, Info, Warning } from '@phosphor-icons/react'
import { useGitHubFetcher } from '@/hooks/useGitHubFetcher'
import { useAutoRefresh } from '@/hooks/useAutoRefresh'
import { WorkflowRunCard } from '@/components/WorkflowRunCard'
import { ScrollArea } from '@/components/ui/scroll-area'
import { ScrollArea } from '@/components/ui'
/**
* Refactored GitHub Actions Fetcher Component

View File

@@ -1,14 +1,14 @@
import { useState, useEffect, useMemo } from 'react'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
import { Button } from '@/components/ui/button'
import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert'
import { Skeleton } from '@/components/ui/skeleton'
import { Badge } from '@/components/ui/badge'
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui'
import { Button } from '@/components/ui'
import { Alert, AlertDescription, AlertTitle } from '@/components/ui'
import { Skeleton } from '@/components/ui'
import { Badge } from '@/components/ui'
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui'
import { CheckCircle, XCircle, ArrowClockwise, ArrowSquareOut, Info, Warning, TrendUp, TrendDown, Robot, Download, FileText } from '@phosphor-icons/react'
import { toast } from 'sonner'
import { Octokit } from 'octokit'
import { ScrollArea } from '@/components/ui/scroll-area'
import { ScrollArea } from '@/components/ui'
interface WorkflowRun {
id: number

View File

@@ -1,10 +1,10 @@
import { useState, useEffect } from 'react'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import { Label } from '@/components/ui/label'
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'
import { Alert, AlertDescription } from '@/components/ui/alert'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui'
import { Button } from '@/components/ui'
import { Input } from '@/components/ui'
import { Label } from '@/components/ui'
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui'
import { Alert, AlertDescription } from '@/components/ui'
import { Database } from '@/lib/database'
import { toast } from 'sonner'
import { Clock, Key, WarningCircle, CheckCircle } from '@phosphor-icons/react'

View File

@@ -1,9 +1,9 @@
import { useState, useEffect, useRef } from 'react'
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
import { Input } from '@/components/ui/input'
import { Button } from '@/components/ui/button'
import { ScrollArea } from '@/components/ui/scroll-area'
import { Badge } from '@/components/ui/badge'
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui'
import { Input } from '@/components/ui'
import { Button } from '@/components/ui'
import { ScrollArea } from '@/components/ui'
import { Badge } from '@/components/ui'
import { PaperPlaneTilt, Users, SignOut, Gear } from '@phosphor-icons/react'
import { useKV } from '@github/spark/hooks'
import type { User } from '@/lib/level-types'

View File

@@ -1,9 +1,9 @@
import { useState, useEffect, useRef } from 'react'
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
import { Input } from '@/components/ui/input'
import { Button } from '@/components/ui/button'
import { ScrollArea } from '@/components/ui/scroll-area'
import { Badge } from '@/components/ui/badge'
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui'
import { Input } from '@/components/ui'
import { Button } from '@/components/ui'
import { ScrollArea } from '@/components/ui'
import { Badge } from '@/components/ui'
import { PaperPlaneTilt, Users, SignOut, Gear } from '@phosphor-icons/react'
import { useKV } from '@github/spark/hooks'
import type { User } from '@/lib/level-types'

View File

@@ -1,7 +1,7 @@
import { useState, useEffect } from 'react'
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from '@/components/ui/dialog'
import { Button } from '@/components/ui/button'
import { Alert, AlertDescription } from '@/components/ui/alert'
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from '@/components/ui'
import { Button } from '@/components/ui'
import { Alert, AlertDescription } from '@/components/ui'
import { FloppyDisk, X, Warning, ShieldCheck } from '@phosphor-icons/react'
import Editor from '@monaco-editor/react'
import { securityScanner, type SecurityScanResult } from '@/lib/security-scanner'

View File

@@ -17,7 +17,6 @@
*/
import { useState, useEffect } from 'react'
import { Database } from '@/lib/database'
import { getScrambledPassword } from '@/lib/auth'
import { NavigationBar } from './level1/NavigationBar'
import { GodCredentialsBanner } from './level1/GodCredentialsBanner'
@@ -25,7 +24,7 @@ import { HeroSection } from './level1/HeroSection'
import { FeaturesSection } from './level1/FeaturesSection'
import { ContactSection } from './level1/ContactSection'
import { AppFooter } from './shared/AppFooter'
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui'
import { GitHubActionsFetcher } from './GitHubActionsFetcher'
// Props for Level1 component
@@ -58,42 +57,56 @@ export function Level1({ onNavigate }: Level1Props) {
// Initialize component state on mount
useEffect(() => {
let interval: ReturnType<typeof setInterval> | undefined
const checkCredentials = async () => {
// Check if god credentials should be displayed
const shouldShow = await Database.shouldShowGodCredentials()
setShowGodCredentials(shouldShow)
// Get supergod account if exists
const superGod = await Database.getSuperGod()
const firstLoginFlags = await Database.getFirstLoginFlags()
setShowSuperGodCredentials(superGod !== null && firstLoginFlags['supergod'] === true)
// Update timer for god credentials expiry
if (shouldShow) {
const expiry = await Database.getGodCredentialsExpiry()
const updateTimer = () => {
const now = Date.now()
const diff = expiry - now
// Hide credentials when expired
if (diff <= 0) {
setShowGodCredentials(false)
setTimeRemaining('')
} else {
try {
const { Database } = await import('@/lib/database')
// Check if god credentials should be displayed
const shouldShow = await Database.shouldShowGodCredentials()
setShowGodCredentials(shouldShow)
// Get supergod account if exists
const superGod = await Database.getSuperGod()
const firstLoginFlags = await Database.getFirstLoginFlags()
setShowSuperGodCredentials(superGod !== null && firstLoginFlags['supergod'] === true)
// Update timer for god credentials expiry
if (shouldShow) {
const expiry = await Database.getGodCredentialsExpiry()
const updateTimer = () => {
const now = Date.now()
const diff = expiry - now
// Hide credentials when expired
if (diff <= 0) {
setShowGodCredentials(false)
setTimeRemaining('')
return
}
// Display remaining time in minutes and seconds
const minutes = Math.floor(diff / 60000)
const seconds = Math.floor((diff % 60000) / 1000)
setTimeRemaining(`${minutes}m ${seconds}s`)
}
updateTimer()
interval = setInterval(updateTimer, 1000)
}
updateTimer()
const interval = setInterval(updateTimer, 1000)
return () => clearInterval(interval)
} catch {
setShowGodCredentials(false)
setShowSuperGodCredentials(false)
setTimeRemaining('')
}
}
checkCredentials()
void checkCredentials()
return () => {
if (interval) clearInterval(interval)
}
}, [])
const handleCopyPassword = async () => {

View File

@@ -1,10 +1,10 @@
"use client"
import { useState, useEffect } from 'react'
import { Button } from '@/components/ui/button'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
import { Textarea } from '@/components/ui/textarea'
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
import { Button } from '@/components/ui'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui'
import { Textarea } from '@/components/ui'
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui'
import { User, ChatCircle } from '@phosphor-icons/react'
import { toast } from 'sonner'
import { Database, hashPassword } from '@/lib/database'

View File

@@ -1,10 +1,10 @@
"use client"
import { useState, useEffect } from 'react'
import { Button } from '@/components/ui/button'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
import { Input } from '@/components/ui/input'
import { Badge } from '@/components/ui/badge'
import { Button } from '@/components/ui'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui'
import { Input } from '@/components/ui'
import { Badge } from '@/components/ui'
import {
Table,
TableBody,
@@ -12,15 +12,15 @@ import {
TableHead,
TableHeader,
TableRow,
} from '@/components/ui/table'
} from '@/components/ui'
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
} from '@/components/ui/dialog'
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
} from '@/components/ui'
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui'
import { MagnifyingGlass, Plus, PencilSimple, Trash, Users, ChatCircle } from '@phosphor-icons/react'
import { toast } from 'sonner'
import { Database } from '@/lib/database'

View File

@@ -1,10 +1,10 @@
"use client"
import { useState, useEffect } from 'react'
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import { Label } from '@/components/ui/label'
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
import { Button } from '@/components/ui'
import { Input } from '@/components/ui'
import { Label } from '@/components/ui'
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui'
import {
Dialog,
DialogContent,
@@ -12,7 +12,7 @@ import {
DialogFooter,
DialogHeader,
DialogTitle,
} from '@/components/ui/dialog'
} from '@/components/ui'
import {
AlertDialog,
AlertDialogAction,
@@ -22,7 +22,7 @@ import {
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
} from '@/components/ui/alert-dialog'
} from '@/components/ui'
import { Crown, Buildings, Users, ArrowsLeftRight, Eye, Camera } from '@phosphor-icons/react'
import { toast } from 'sonner'
import { Level5Header } from './level5/Level5Header'

View File

@@ -1,8 +1,8 @@
import { useState } from 'react'
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import { Label } from '@/components/ui/label'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
import { Button } from '@/components/ui'
import { Input } from '@/components/ui'
import { Label } from '@/components/ui'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui'
import { Lock } from '@phosphor-icons/react'
import { toast } from 'sonner'

View File

@@ -1,16 +1,16 @@
import { useState, useEffect, useRef } from 'react'
import { Button } from '@/components/ui/button'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
import { Input } from '@/components/ui/input'
import { Label } from '@/components/ui/label'
import { Badge } from '@/components/ui/badge'
import { Button } from '@/components/ui'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui'
import { Input } from '@/components/ui'
import { Label } from '@/components/ui'
import { Badge } from '@/components/ui'
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from '@/components/ui/select'
} from '@/components/ui'
import { Plus, Trash, Play, CheckCircle, XCircle, FileCode, ArrowsOut, BookOpen, ShieldCheck } from '@phosphor-icons/react'
import { toast } from 'sonner'
import { createLuaEngine, type LuaExecutionResult } from '@/lib/lua-engine'
@@ -19,7 +19,7 @@ import type { LuaScript } from '@/lib/level-types'
import Editor from '@monaco-editor/react'
import { useMonaco } from '@monaco-editor/react'
import { LuaSnippetLibrary } from '@/components/LuaSnippetLibrary'
import { Sheet, SheetContent, SheetDescription, SheetHeader, SheetTitle, SheetTrigger } from '@/components/ui/sheet'
import { Sheet, SheetContent, SheetDescription, SheetHeader, SheetTitle, SheetTrigger } from '@/components/ui'
import { securityScanner, type SecurityScanResult } from '@/lib/security-scanner'
import { SecurityWarningDialog } from '@/components/SecurityWarningDialog'

View File

@@ -1,18 +1,18 @@
import { useState } from 'react'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
import { Input } from '@/components/ui/input'
import { Button } from '@/components/ui/button'
import { Badge } from '@/components/ui/badge'
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
import { ScrollArea } from '@/components/ui/scroll-area'
import { Separator } from '@/components/ui/separator'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui'
import { Input } from '@/components/ui'
import { Button } from '@/components/ui'
import { Badge } from '@/components/ui'
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui'
import { ScrollArea } from '@/components/ui'
import { Separator } from '@/components/ui'
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
} from '@/components/ui/dialog'
} from '@/components/ui'
import {
MagnifyingGlass,
Copy,

View File

@@ -1,10 +1,10 @@
import { useState, useMemo } from 'react'
import { useKV } from '@github/spark/hooks'
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import { Badge } from '@/components/ui/badge'
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table'
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'
import { Button } from '@/components/ui'
import { Input } from '@/components/ui'
import { Badge } from '@/components/ui'
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui'
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui'
import type { ModelSchema, SchemaConfig } from '@/lib/schema-types'
import { getRecordsKey, getFieldLabel, sortRecords, filterRecords, findModel } from '@/lib/schema-utils'
import { RecordForm } from './RecordForm'

View File

@@ -1,10 +1,10 @@
import { useState, useEffect } from 'react'
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import { ScrollArea } from '@/components/ui/scroll-area'
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
import { Badge } from '@/components/ui/badge'
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui'
import { Button } from '@/components/ui'
import { Input } from '@/components/ui'
import { ScrollArea } from '@/components/ui'
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui'
import { Badge } from '@/components/ui'
import {
Dialog,
DialogContent,
@@ -12,16 +12,16 @@ import {
DialogFooter,
DialogHeader,
DialogTitle,
} from '@/components/ui/dialog'
} from '@/components/ui'
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from '@/components/ui/select'
import { Label } from '@/components/ui/label'
import { Separator } from '@/components/ui/separator'
} from '@/components/ui'
import { Label } from '@/components/ui'
import { Separator } from '@/components/ui'
import Editor from '@monaco-editor/react'
import {
File,

View File

@@ -1,7 +1,7 @@
import { cn } from '@/lib/utils'
import { Badge } from '@/components/ui/badge'
import { Card } from '@/components/ui/card'
import { Separator } from '@/components/ui/separator'
import { Badge } from '@/components/ui'
import { Card } from '@/components/ui'
import { Separator } from '@/components/ui'
export type NotificationSeverity = 'info' | 'warning' | 'critical'

View File

@@ -1,13 +1,13 @@
import { useState, useRef } from 'react'
import { Button } from '@/components/ui/button'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from '@/components/ui/dialog'
import { Label } from '@/components/ui/label'
import { Input } from '@/components/ui/input'
import { Textarea } from '@/components/ui/textarea'
import { Checkbox } from '@/components/ui/checkbox'
import { ScrollArea } from '@/components/ui/scroll-area'
import { Separator } from '@/components/ui/separator'
import { Button } from '@/components/ui'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui'
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from '@/components/ui'
import { Label } from '@/components/ui'
import { Input } from '@/components/ui'
import { Textarea } from '@/components/ui'
import { Checkbox } from '@/components/ui'
import { ScrollArea } from '@/components/ui'
import { Separator } from '@/components/ui'
import { toast } from 'sonner'
import { Database } from '@/lib/database'
import { exportPackageAsZip, importPackageFromZip, downloadZip, exportDatabaseSnapshot } from '@/lib/package-export'

View File

@@ -1,13 +1,13 @@
import { useState, useEffect } from 'react'
import { Button } from '@/components/ui/button'
import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from '@/components/ui/card'
import { Input } from '@/components/ui/input'
import { Badge } from '@/components/ui/badge'
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
import { ScrollArea } from '@/components/ui/scroll-area'
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from '@/components/ui/dialog'
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'
import { Separator } from '@/components/ui/separator'
import { Button } from '@/components/ui'
import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from '@/components/ui'
import { Input } from '@/components/ui'
import { Badge } from '@/components/ui'
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui'
import { ScrollArea } from '@/components/ui'
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from '@/components/ui'
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui'
import { Separator } from '@/components/ui'
import { toast } from 'sonner'
import { Database } from '@/lib/database'
import { PACKAGE_CATALOG } from '@/lib/package-catalog'

View File

@@ -1,17 +1,17 @@
import { useState, useEffect } from 'react'
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import { Label } from '@/components/ui/label'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table'
import { Badge } from '@/components/ui/badge'
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger } from '@/components/ui/dialog'
import { Button } from '@/components/ui'
import { Input } from '@/components/ui'
import { Label } from '@/components/ui'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui'
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui'
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui'
import { Badge } from '@/components/ui'
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger } from '@/components/ui'
import { Plus, Pencil, Trash, Eye, LockKey } from '@phosphor-icons/react'
import { Database } from '@/lib/database'
import { toast } from 'sonner'
import type { PageConfig, UserRole, AppLevel } from '@/lib/level-types'
import { Switch } from '@/components/ui/switch'
import { Switch } from '@/components/ui'
export function PageRoutesManager() {
const [pages, setPages] = useState<PageConfig[]>([])

View File

@@ -1,9 +1,9 @@
import { useState } from 'react'
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from '@/components/ui/dialog'
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import { Label } from '@/components/ui/label'
import { Alert, AlertDescription } from '@/components/ui/alert'
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from '@/components/ui'
import { Button } from '@/components/ui'
import { Input } from '@/components/ui'
import { Label } from '@/components/ui'
import { Alert, AlertDescription } from '@/components/ui'
import { Warning, Eye, EyeSlash } from '@phosphor-icons/react'
interface PasswordChangeDialogProps {

View File

@@ -1,11 +1,11 @@
import { useState, useEffect } from 'react'
import { ScrollArea } from '@/components/ui/scroll-area'
import { Input } from '@/components/ui/input'
import { Label } from '@/components/ui/label'
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'
import { Button } from '@/components/ui/button'
import { Separator } from '@/components/ui/separator'
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
import { ScrollArea } from '@/components/ui'
import { Input } from '@/components/ui'
import { Label } from '@/components/ui'
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui'
import { Button } from '@/components/ui'
import { Separator } from '@/components/ui'
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui'
import type { ComponentInstance } from '@/lib/builder-types'
import { componentCatalog } from '@/lib/component-catalog'
import { Code, PaintBrush, Trash, Palette } from '@phosphor-icons/react'

View File

@@ -1,8 +1,8 @@
import { Card } from '@/components/ui/card'
import { Badge } from '@/components/ui/badge'
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '@/components/ui/accordion'
import { Card } from '@/components/ui'
import { Badge } from '@/components/ui'
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '@/components/ui'
import { Palette, ListDashes, Code, Sparkle, Package, Terminal } from '@phosphor-icons/react'
import { Alert, AlertDescription } from '@/components/ui/alert'
import { Alert, AlertDescription } from '@/components/ui'
export function QuickGuide() {
return (

View File

@@ -236,7 +236,7 @@ import { ComponentCatalog } from '@/components/ComponentCatalog'
### New Way (recommended)
```typescript
import { Button } from '@/components/atoms'
import { Button } from '@/components/ui'
import { ComponentCatalog } from '@/components/organisms'
```
@@ -271,7 +271,7 @@ export function FormField({ label, error, ...props }: FormFieldProps) {
// Organism handling data fetching and display
import { useKV } from '@github/spark/hooks'
import { Button } from '@/components/atoms'
import { Card } from '@/components/ui/card'
import { Card } from '@/components/ui'
export function UserList() {
const [users] = useKV('users', [])

View File

@@ -1,7 +1,7 @@
import { useState, useEffect } from 'react'
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from '@/components/ui/dialog'
import { Button } from '@/components/ui/button'
import { ScrollArea } from '@/components/ui/scroll-area'
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from '@/components/ui'
import { Button } from '@/components/ui'
import { ScrollArea } from '@/components/ui'
import type { ModelSchema, SchemaConfig } from '@/lib/schema-types'
import { validateRecord, createEmptyRecord } from '@/lib/schema-utils'
import { FieldRenderer } from './FieldRenderer'

View File

@@ -1,18 +1,18 @@
import type { ComponentInstance } from '@/lib/builder-types'
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import { Textarea } from '@/components/ui/textarea'
import { Label } from '@/components/ui/label'
import { Badge } from '@/components/ui/badge'
import { Card } from '@/components/ui/card'
import { Switch } from '@/components/ui/switch'
import { Checkbox } from '@/components/ui/checkbox'
import { Separator } from '@/components/ui/separator'
import { Alert } from '@/components/ui/alert'
import { Progress } from '@/components/ui/progress'
import { Slider } from '@/components/ui/slider'
import { Avatar, AvatarFallback } from '@/components/ui/avatar'
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table'
import { Button } from '@/components/ui'
import { Input } from '@/components/ui'
import { Textarea } from '@/components/ui'
import { Label } from '@/components/ui'
import { Badge } from '@/components/ui'
import { Card } from '@/components/ui'
import { Switch } from '@/components/ui'
import { Checkbox } from '@/components/ui'
import { Separator } from '@/components/ui'
import { Alert } from '@/components/ui'
import { Progress } from '@/components/ui'
import { Slider } from '@/components/ui'
import { Avatar, AvatarFallback } from '@/components/ui'
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui'
import { IRCWebchatDeclarative } from '@/components/IRCWebchatDeclarative'
import { NotificationSummaryCard } from '@/components/NotificationSummaryCard'
import type { User } from '@/lib/level-types'

View File

@@ -1,9 +1,9 @@
import { useState, useEffect } from 'react'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
import { Label } from '@/components/ui/label'
import { Input } from '@/components/ui/input'
import { Button } from '@/components/ui/button'
import { Switch } from '@/components/ui/switch'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui'
import { Label } from '@/components/ui'
import { Input } from '@/components/ui'
import { Button } from '@/components/ui'
import { Switch } from '@/components/ui'
import { Envelope, FloppyDisk, PaperPlaneTilt } from '@phosphor-icons/react'
import { toast } from 'sonner'
import { Database } from '@/lib/database'

View File

@@ -1,7 +1,7 @@
import { useState } from 'react'
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from '@/components/ui/dialog'
import { Button } from '@/components/ui/button'
import { Alert, AlertDescription } from '@/components/ui/alert'
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from '@/components/ui'
import { Button } from '@/components/ui'
import { Alert, AlertDescription } from '@/components/ui'
import type { SchemaConfig } from '@/lib/schema-types'
import { FloppyDisk, X, Warning } from '@phosphor-icons/react'
import Editor from '@monaco-editor/react'

View File

@@ -1,16 +1,16 @@
import { useState } from 'react'
import { Button } from '@/components/ui/button'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
import { Input } from '@/components/ui/input'
import { Label } from '@/components/ui/label'
import { Button } from '@/components/ui'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui'
import { Input } from '@/components/ui'
import { Label } from '@/components/ui'
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from '@/components/ui/select'
import { Switch } from '@/components/ui/switch'
} from '@/components/ui'
import { Switch } from '@/components/ui'
import { Plus, Trash } from '@phosphor-icons/react'
import { toast } from 'sonner'
import type { ModelSchema, FieldSchema, FieldType } from '@/lib/schema-types'

View File

@@ -6,12 +6,12 @@ import {
DialogFooter,
DialogHeader,
DialogTitle,
} from '@/components/ui/dialog'
import { Button } from '@/components/ui/button'
import { Alert, AlertDescription } from '@/components/ui/alert'
import { ScrollArea } from '@/components/ui/scroll-area'
import { Badge } from '@/components/ui/badge'
import { Separator } from '@/components/ui/separator'
} from '@/components/ui'
import { Button } from '@/components/ui'
import { Alert, AlertDescription } from '@/components/ui'
import { ScrollArea } from '@/components/ui'
import { Badge } from '@/components/ui'
import { Separator } from '@/components/ui'
import { ShieldWarning, Warning, Info, CheckCircle } from '@phosphor-icons/react'
import type { SecurityScanResult, SecurityIssue } from '@/lib/security-scanner'
import { getSeverityColor, getSeverityIcon } from '@/lib/security-scanner'

View File

@@ -1,10 +1,10 @@
import { useState, useEffect } from 'react'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
import { Label } from '@/components/ui/label'
import { Input } from '@/components/ui/input'
import { Button } from '@/components/ui/button'
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
import { Switch } from '@/components/ui/switch'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui'
import { Label } from '@/components/ui'
import { Input } from '@/components/ui'
import { Button } from '@/components/ui'
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui'
import { Switch } from '@/components/ui'
import { Palette, Sun, Moon, FloppyDisk, ArrowCounterClockwise } from '@phosphor-icons/react'
import { toast } from 'sonner'
import { useKV } from '@github/spark/hooks'

View File

@@ -1,14 +1,14 @@
import { useState } from 'react'
import { Button } from '@/components/ui/button'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
import { Input } from '@/components/ui/input'
import { Label } from '@/components/ui/label'
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
import { Button } from '@/components/ui'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui'
import { Input } from '@/components/ui'
import { Label } from '@/components/ui'
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui'
import { SignIn, UserPlus, ArrowLeft, Envelope } from '@phosphor-icons/react'
import { toast } from 'sonner'
import { Database, hashPassword } from '@/lib/database'
import { generateScrambledPassword, simulateEmailSend } from '@/lib/password-utils'
import { Alert, AlertDescription } from '@/components/ui/alert'
import { Alert, AlertDescription } from '@/components/ui'
interface UnifiedLoginProps {
onLogin: (credentials: { username: string; password: string }) => void

View File

@@ -1,14 +1,14 @@
import { useState, useEffect } from 'react'
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import { Label } from '@/components/ui/label'
import { Textarea } from '@/components/ui/textarea'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table'
import { Badge } from '@/components/ui/badge'
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger } from '@/components/ui/dialog'
import { Avatar, AvatarFallback } from '@/components/ui/avatar'
import { Button } from '@/components/ui'
import { Input } from '@/components/ui'
import { Label } from '@/components/ui'
import { Textarea } from '@/components/ui'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui'
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui'
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui'
import { Badge } from '@/components/ui'
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger } from '@/components/ui'
import { Avatar, AvatarFallback } from '@/components/ui'
import { Plus, Pencil, Trash, UserCircle } from '@phosphor-icons/react'
import { Database, hashPassword } from '@/lib/database'
import { toast } from 'sonner'

View File

@@ -2,8 +2,8 @@
// This file serves as a reference for building well-documented components
import { useState, useCallback } from 'react'
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import { Button } from '@/components/ui'
import { Input } from '@/components/ui'
import { useAuth } from '@/hooks'
/**

View File

@@ -1,5 +1,5 @@
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from '@/components/ui/card'
import { Button } from '@/components/ui/button'
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from '@/components/ui'
import { Button } from '@/components/ui'
export function ContactSection() {
return (

View File

@@ -1,4 +1,4 @@
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui'
export function FeaturesSection() {
return (

View File

@@ -1,5 +1,5 @@
import { Alert, AlertDescription } from '@/components/ui/alert'
import { Button } from '@/components/ui/button'
import { Alert, AlertDescription } from '@/components/ui'
import { Button } from '@/components/ui'
import { Warning, Eye, EyeSlash, Copy, Check } from '@phosphor-icons/react'
interface GodCredentialsBannerProps {

View File

@@ -1,4 +1,4 @@
import { Button } from '@/components/ui/button'
import { Button } from '@/components/ui'
interface HeroSectionProps {
onNavigate: (level: number) => void

View File

@@ -1,4 +1,4 @@
import { Button } from '@/components/ui/button'
import { Button } from '@/components/ui'
import { List, X, User, ShieldCheck } from '@phosphor-icons/react'
interface NavigationBarProps {

View File

@@ -1,6 +1,6 @@
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
import { Button } from '@/components/ui/button'
import { Avatar, AvatarFallback } from '@/components/ui/avatar'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui'
import { Button } from '@/components/ui'
import { Avatar, AvatarFallback } from '@/components/ui'
import { Trash } from '@phosphor-icons/react'
import type { Comment, User } from '@/lib/level-types'

View File

@@ -1,9 +1,9 @@
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
import { Button } from '@/components/ui/button'
import { Avatar, AvatarFallback } from '@/components/ui/avatar'
import { Input } from '@/components/ui/input'
import { Label } from '@/components/ui/label'
import { Textarea } from '@/components/ui/textarea'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui'
import { Button } from '@/components/ui'
import { Avatar, AvatarFallback } from '@/components/ui'
import { Input } from '@/components/ui'
import { Label } from '@/components/ui'
import { Textarea } from '@/components/ui'
import { Envelope } from '@phosphor-icons/react'
import type { User } from '@/lib/level-types'

View File

@@ -1,11 +1,11 @@
import { Button } from '@/components/ui/button'
import { Badge } from '@/components/ui/badge'
import { Button } from '@/components/ui'
import { Badge } from '@/components/ui'
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu'
} from '@/components/ui'
import { SignOut, Eye, House, Download, Upload, Terminal } from '@phosphor-icons/react'
interface Level4HeaderProps {

View File

@@ -1,4 +1,4 @@
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui'
import { Database as DatabaseIcon, Lightning, Code, BookOpen, HardDrives, MapTrifold, Tree, Users, Gear, Palette, ListDashes, Sparkle, Package } from '@phosphor-icons/react'
import { SchemaEditorLevel4 } from '@/components/SchemaEditorLevel4'
import { WorkflowEditor } from '@/components/WorkflowEditor'

View File

@@ -1,6 +1,6 @@
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
import { ScrollArea } from '@/components/ui/scroll-area'
import { Badge } from '@/components/ui/badge'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui'
import { ScrollArea } from '@/components/ui'
import { Badge } from '@/components/ui'
import { Shield, Users } from '@phosphor-icons/react'
import type { User } from '@/lib/level-types'

View File

@@ -1,5 +1,5 @@
import { Button } from '@/components/ui/button'
import { Badge } from '@/components/ui/badge'
import { Button } from '@/components/ui'
import { Badge } from '@/components/ui'
import { Crown, SignOut, Terminal } from '@phosphor-icons/react'
interface Level5HeaderProps {

View File

@@ -1,10 +1,10 @@
import { useState } from 'react'
import { Button } from '@/components/ui/button'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
import { ScrollArea } from '@/components/ui/scroll-area'
import { Badge } from '@/components/ui/badge'
import { Separator } from '@/components/ui/separator'
import { Alert, AlertDescription } from '@/components/ui/alert'
import { Button } from '@/components/ui'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui'
import { ScrollArea } from '@/components/ui'
import { Badge } from '@/components/ui'
import { Separator } from '@/components/ui'
import { Alert, AlertDescription } from '@/components/ui'
import { Crown, ArrowsLeftRight } from '@phosphor-icons/react'
import type { User } from '@/lib/level-types'

View File

@@ -1,5 +1,5 @@
import { Button } from '@/components/ui/button'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
import { Button } from '@/components/ui'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui'
import { Eye } from '@phosphor-icons/react'
interface PreviewTabProps {

View File

@@ -1,7 +1,7 @@
import { Button } from '@/components/ui/button'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
import { ScrollArea } from '@/components/ui/scroll-area'
import { Badge } from '@/components/ui/badge'
import { Button } from '@/components/ui'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui'
import { ScrollArea } from '@/components/ui'
import { Badge } from '@/components/ui'
import { Buildings, House } from '@phosphor-icons/react'
import type { Tenant, User } from '@/lib/level-types'

View File

@@ -1,6 +1,6 @@
import { Button } from '@/components/ui/button'
import { Avatar, AvatarFallback } from '@/components/ui/avatar'
import { Badge } from '@/components/ui/badge'
import { Button } from '@/components/ui'
import { Avatar, AvatarFallback } from '@/components/ui'
import { Badge } from '@/components/ui'
import { SignOut, House } from '@phosphor-icons/react'
interface AppHeaderProps {

View File

@@ -29,10 +29,10 @@ ui/
## Usage
```tsx
// Import from the main index
// Prefer the main barrel import
import { Button, Card, Table, Dialog } from '@/components/ui'
// Or import from specific category
// Category imports are available when you want stricter layering
import { Button, Input } from '@/components/ui/atoms'
import { Card, Select } from '@/components/ui/molecules'
import { Table, Form } from '@/components/ui/organisms'
@@ -79,8 +79,8 @@ import styles from './MyComponent.module.scss'
## Legacy Files
⚠️ **Files in the root of this directory (like `button.tsx`, `dialog.tsx`, etc.) are LEGACY**
and will be removed. Only use components from:
⚠️ **Root wrapper files (like `button.tsx`, `dialog.tsx`, etc.) are legacy** and exist for
backward compatibility. Avoid `@/components/ui/<component>` imports and use:
- `atoms/`
- `molecules/`
- `organisms/`

View File

@@ -1,3 +1,2 @@
// Re-export for backward compatibility
// TODO: Update imports to use @/components/ui directly
export { Accordion, AccordionItem, AccordionTrigger, AccordionContent } from './molecules/Accordion'

View File

@@ -1,5 +1,4 @@
// Re-export for backward compatibility
// TODO: Update imports to use @/components/ui directly
export {
AlertDialog,
AlertDialogTrigger,

View File

@@ -1,3 +1,2 @@
// Re-export for backward compatibility
// TODO: Update imports to use @/components/ui directly
export { Alert, AlertTitle, AlertDescription, type AlertVariant, type AlertProps } from './molecules/Alert'

View File

@@ -1,3 +1,2 @@
// Re-export for backward compatibility
// TODO: Update imports to use @/components/ui directly
export { Avatar, AvatarImage, AvatarFallback, type AvatarProps } from './atoms/Avatar'

View File

@@ -1,3 +1,2 @@
// Re-export for backward compatibility
// TODO: Update imports to use @/components/ui directly
export { Badge, type BadgeProps, type BadgeVariant } from './atoms/Badge'

View File

@@ -1,3 +1,2 @@
// Re-export for backward compatibility
// TODO: Update imports to use @/components/ui directly
export { Button, type ButtonProps, type ButtonVariant, type ButtonSize } from './atoms/Button'

View File

@@ -1,3 +1,2 @@
// Re-export for backward compatibility
// TODO: Update imports to use @/components/ui directly
export { Card, CardHeader, CardTitle, CardDescription, CardContent, CardFooter } from './molecules/Card'

View File

@@ -1,3 +1,2 @@
// Re-export for backward compatibility
// TODO: Update imports to use @/components/ui directly
export { Checkbox, type CheckboxProps } from './atoms/Checkbox'

View File

@@ -1,5 +1,4 @@
// Re-export for backward compatibility
// TODO: Update imports to use @/components/ui directly
export {
Dialog,
DialogClose,

View File

@@ -1,5 +1,4 @@
// Re-export for backward compatibility
// TODO: Update imports to use @/components/ui directly
export {
DropdownMenu,
DropdownMenuTrigger,

View File

@@ -0,0 +1,13 @@
import { describe, it, expect } from 'vitest'
import { Button, Card, Dialog, Select, Table, Tabs } from '@/components/ui'
describe('ui barrel exports', () => {
it('exposes common UI components', () => {
expect(Button).toBeDefined()
expect(Card).toBeDefined()
expect(Dialog).toBeDefined()
expect(Select).toBeDefined()
expect(Table).toBeDefined()
expect(Tabs).toBeDefined()
})
})

View File

@@ -1,3 +1,2 @@
// Re-export for backward compatibility
// TODO: Update imports to use @/components/ui directly
export { Input, type InputProps } from './atoms/Input'

View File

@@ -1,3 +1,2 @@
// Re-export for backward compatibility
// TODO: Update imports to use @/components/ui directly
export { Label, type LabelProps } from './atoms/Label'

View File

@@ -1,3 +1,2 @@
// Re-export for backward compatibility
// TODO: Update imports to use @/components/ui directly
export { Progress, type ProgressProps } from './atoms/Progress'

View File

@@ -1,3 +1,2 @@
// Re-export for backward compatibility
// TODO: Update imports to use @/components/ui directly
export { RadioGroup, RadioGroupItem } from './molecules/RadioGroup'

View File

@@ -1,3 +1,2 @@
// Re-export for backward compatibility
// TODO: Update imports to use @/components/ui directly
export { ScrollArea, ScrollBar, type ScrollAreaProps } from './atoms/ScrollArea'

View File

@@ -1,5 +1,4 @@
// Re-export for backward compatibility
// TODO: Update imports to use @/components/ui directly
export {
Select,
SelectContent,

View File

@@ -1,3 +1,2 @@
// Re-export for backward compatibility
// TODO: Update imports to use @/components/ui directly
export { Separator, type SeparatorProps } from './atoms/Separator'

View File

@@ -1,5 +1,4 @@
// Re-export for backward compatibility
// TODO: Update imports to use @/components/ui directly
export {
Sheet,
SheetTrigger,

View File

@@ -1,3 +1,2 @@
// Re-export for backward compatibility
// TODO: Update imports to use @/components/ui directly
export { Skeleton, type SkeletonProps } from './atoms/Skeleton'

View File

@@ -1,3 +1,2 @@
// Re-export for backward compatibility
// TODO: Update imports to use @/components/ui directly
export { Slider, type SliderProps } from './atoms/Slider'

View File

@@ -1,3 +1,2 @@
// Re-export for backward compatibility
// TODO: Update imports to use @/components/ui directly
export { Switch, type SwitchProps } from './atoms/Switch'

View File

@@ -1,3 +1,2 @@
// Re-export for backward compatibility
// TODO: Update imports to use @/components/ui directly
export { Table, TableHeader, TableBody, TableFooter, TableRow, TableHead, TableCell, TableCaption } from './organisms/Table'

View File

@@ -1,3 +1,2 @@
// Re-export for backward compatibility
// TODO: Update imports to use @/components/ui directly
export { Tabs, TabsList, TabsTrigger, TabsContent } from './molecules/Tabs'

View File

@@ -1,3 +1,2 @@
// Re-export for backward compatibility
// TODO: Update imports to use @/components/ui directly
export { Textarea, type TextareaProps } from './atoms/Textarea'

View File

@@ -2,26 +2,64 @@ import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'
import { checkRateLimit } from './check-rate-limit'
import { clearRateLimit } from './clear-rate-limit'
import { clearAllRateLimits } from './clear-all-rate-limits'
import { MAX_REQUESTS_PER_WINDOW, RATE_LIMIT_WINDOW } from './rate-limit-store'
import { DEFAULT_MAX_REQUESTS_PER_WINDOW, DEFAULT_RATE_LIMIT_WINDOW_MS, getRateLimitConfig } from './rate-limit-store'
const BASE_TIME = new Date('2024-01-01T00:00:00Z')
const ENV_RATE_LIMIT_WINDOW_MS = 'MB_RATE_LIMIT_WINDOW_MS'
const ENV_MAX_REQUESTS = 'MB_RATE_LIMIT_MAX_REQUESTS'
const resetRateLimitEnv = () => {
delete process.env[ENV_RATE_LIMIT_WINDOW_MS]
delete process.env[ENV_MAX_REQUESTS]
}
describe('rate limit helpers', () => {
beforeEach(() => {
vi.useFakeTimers()
vi.setSystemTime(BASE_TIME)
clearAllRateLimits()
resetRateLimitEnv()
})
afterEach(() => {
clearAllRateLimits()
vi.useRealTimers()
resetRateLimitEnv()
})
describe('rate limit config', () => {
it('uses defaults when env is unset', () => {
const { windowMs, maxRequests } = getRateLimitConfig()
expect(windowMs).toBe(DEFAULT_RATE_LIMIT_WINDOW_MS)
expect(maxRequests).toBe(DEFAULT_MAX_REQUESTS_PER_WINDOW)
})
it('uses env overrides when valid', () => {
process.env[ENV_RATE_LIMIT_WINDOW_MS] = '120000'
process.env[ENV_MAX_REQUESTS] = '250'
const { windowMs, maxRequests } = getRateLimitConfig()
expect(windowMs).toBe(120000)
expect(maxRequests).toBe(250)
})
it('falls back to defaults for invalid env values', () => {
process.env[ENV_RATE_LIMIT_WINDOW_MS] = '-10'
process.env[ENV_MAX_REQUESTS] = 'not-a-number'
const { windowMs, maxRequests } = getRateLimitConfig()
expect(windowMs).toBe(DEFAULT_RATE_LIMIT_WINDOW_MS)
expect(maxRequests).toBe(DEFAULT_MAX_REQUESTS_PER_WINDOW)
})
})
describe('checkRateLimit', () => {
it.each([
{ name: 'allows requests up to the limit', attempts: MAX_REQUESTS_PER_WINDOW, expected: true },
{ name: 'blocks requests over the limit', attempts: MAX_REQUESTS_PER_WINDOW + 1, expected: false },
{ name: 'allows requests up to the limit', attempts: DEFAULT_MAX_REQUESTS_PER_WINDOW, expected: true },
{ name: 'blocks requests over the limit', attempts: DEFAULT_MAX_REQUESTS_PER_WINDOW + 1, expected: false },
])('$name', ({ attempts, expected }) => {
const userId = 'user-limit'
let lastResult = true
@@ -34,12 +72,12 @@ describe('rate limit helpers', () => {
})
it.each([
{ name: 'block before the window expires', advanceMs: RATE_LIMIT_WINDOW - 1, expected: false },
{ name: 'allow after the window expires', advanceMs: RATE_LIMIT_WINDOW + 1, expected: true },
{ name: 'block before the window expires', advanceMs: DEFAULT_RATE_LIMIT_WINDOW_MS - 1, expected: false },
{ name: 'allow after the window expires', advanceMs: DEFAULT_RATE_LIMIT_WINDOW_MS + 1, expected: true },
])('should $name', ({ advanceMs, expected }) => {
const userId = 'user-window'
for (let i = 0; i < MAX_REQUESTS_PER_WINDOW; i += 1) {
for (let i = 0; i < DEFAULT_MAX_REQUESTS_PER_WINDOW; i += 1) {
checkRateLimit(userId)
}
@@ -49,11 +87,21 @@ describe('rate limit helpers', () => {
expect(checkRateLimit(userId)).toBe(expected)
})
it('respects env-configured limits', () => {
process.env[ENV_RATE_LIMIT_WINDOW_MS] = '1000'
process.env[ENV_MAX_REQUESTS] = '2'
const userId = 'user-env'
expect(checkRateLimit(userId)).toBe(true)
expect(checkRateLimit(userId)).toBe(true)
expect(checkRateLimit(userId)).toBe(false)
})
})
describe('clearRateLimit', () => {
it.each([{ userId: 'user-a' }, { userId: 'user-b' }])('should reset for $userId', ({ userId }) => {
for (let i = 0; i < MAX_REQUESTS_PER_WINDOW; i += 1) {
for (let i = 0; i < DEFAULT_MAX_REQUESTS_PER_WINDOW; i += 1) {
checkRateLimit(userId)
}
@@ -71,7 +119,7 @@ describe('rate limit helpers', () => {
{ users: ['user-x', 'user-y', 'user-z'] },
])('should reset all users: $users', ({ users }) => {
for (const userId of users) {
for (let i = 0; i < MAX_REQUESTS_PER_WINDOW; i += 1) {
for (let i = 0; i < DEFAULT_MAX_REQUESTS_PER_WINDOW; i += 1) {
checkRateLimit(userId)
}
expect(checkRateLimit(userId)).toBe(false)

View File

@@ -1,17 +1,18 @@
import { RATE_LIMIT_WINDOW, MAX_REQUESTS_PER_WINDOW, rateLimitMap } from './rate-limit-store'
import { getRateLimitConfig, rateLimitMap } from './rate-limit-store'
/**
* Check if user is within rate limits
*/
export function checkRateLimit(userId: string): boolean {
const { windowMs, maxRequests } = getRateLimitConfig()
const now = Date.now()
const userRequests = rateLimitMap.get(userId) || []
const recentRequests = userRequests.filter(
timestamp => now - timestamp < RATE_LIMIT_WINDOW
timestamp => now - timestamp < windowMs
)
if (recentRequests.length >= MAX_REQUESTS_PER_WINDOW) {
if (recentRequests.length >= maxRequests) {
return false
}

View File

@@ -1,7 +1,20 @@
// TODO: Load rate limit settings from config/DB instead of hardcoding.
const RATE_LIMIT_WINDOW = 60000
const MAX_REQUESTS_PER_WINDOW = 100
const DEFAULT_RATE_LIMIT_WINDOW_MS = 60000
const DEFAULT_MAX_REQUESTS_PER_WINDOW = 100
const parsePositiveInt = (value: string | undefined, fallback: number): number => {
if (!value) return fallback
const parsed = Number.parseInt(value, 10)
if (!Number.isFinite(parsed) || parsed <= 0) return fallback
return parsed
}
export const getRateLimitConfig = () => ({
windowMs: parsePositiveInt(process.env.MB_RATE_LIMIT_WINDOW_MS, DEFAULT_RATE_LIMIT_WINDOW_MS),
maxRequests: parsePositiveInt(process.env.MB_RATE_LIMIT_MAX_REQUESTS, DEFAULT_MAX_REQUESTS_PER_WINDOW),
})
const rateLimitMap = new Map<string, number[]>()
export { RATE_LIMIT_WINDOW, MAX_REQUESTS_PER_WINDOW, rateLimitMap }
export { DEFAULT_RATE_LIMIT_WINDOW_MS, DEFAULT_MAX_REQUESTS_PER_WINDOW, rateLimitMap }

View File

@@ -1,8 +1,294 @@
#!/usr/bin/env tsx
console.log(JSON.stringify({
averageRenderTime: 12,
slowComponents: [],
recommendations: [],
timestamp: new Date().toISOString()
}, null, 2))
import { existsSync, readdirSync, readFileSync, statSync } from 'fs'
import { basename, extname, join, relative } from 'path'
interface HookCounts {
[key: string]: number
}
interface ComponentMetrics {
file: string
component: string
lines: number
bytes: number
hooks: {
builtIn: number
custom: number
total: number
byHook: HookCounts
}
effects: number
memoization: number
estimatedRenderTimeMs: number
reasons: string[]
risk: 'low' | 'medium' | 'high'
}
const BUILTIN_HOOKS = [
'useState',
'useReducer',
'useEffect',
'useLayoutEffect',
'useInsertionEffect',
'useMemo',
'useCallback',
'useRef',
'useContext',
'useSyncExternalStore',
'useTransition',
'useDeferredValue',
'useId',
'useImperativeHandle',
]
const BUILTIN_HOOK_SET = new Set(BUILTIN_HOOKS)
const SKIP_DIRS = new Set([
'node_modules',
'.next',
'dist',
'build',
'coverage',
'.git',
'__tests__',
'__mocks__',
'__snapshots__',
])
const THRESHOLDS = {
slowRenderMs: 16,
largeComponentLines: 200,
veryLargeComponentLines: 300,
highHookCount: 12,
highEffectCount: 3,
}
const TARGET_EXTENSIONS = new Set(['.tsx'])
function countMatches(content: string, regex: RegExp): number {
return content.match(regex)?.length ?? 0
}
function pickSourceRoot(): string | null {
const candidates = [
process.env.RENDER_ANALYSIS_ROOT,
join(process.cwd(), 'frontends', 'nextjs', 'src'),
join(process.cwd(), 'src'),
].filter(Boolean) as string[]
for (const candidate of candidates) {
if (existsSync(candidate)) {
return candidate
}
}
return null
}
function walkDir(dir: string, files: string[]): void {
let entries: string[]
try {
entries = readdirSync(dir)
} catch {
return
}
for (const entry of entries) {
const fullPath = join(dir, entry)
let stats
try {
stats = statSync(fullPath)
} catch {
continue
}
if (stats.isDirectory()) {
if (SKIP_DIRS.has(entry)) {
continue
}
walkDir(fullPath, files)
continue
}
if (!stats.isFile()) {
continue
}
if (!TARGET_EXTENSIONS.has(extname(entry))) {
continue
}
if (entry.endsWith('.test.tsx') || entry.endsWith('.spec.tsx') || entry.endsWith('.stories.tsx')) {
continue
}
files.push(fullPath)
}
}
function estimateRenderTimeMs(lines: number, hooks: number, effects: number, memoization: number): number {
const base = 1.5
const lineCost = Math.min(lines, 400) * 0.03
const hookCost = hooks * 0.4
const effectCost = effects * 0.8
const memoSavings = Math.min(memoization, 4) * 0.3
const estimate = base + lineCost + hookCost + effectCost - memoSavings
return Math.max(0.5, Math.round(estimate * 10) / 10)
}
function analyzeFile(filePath: string): ComponentMetrics | null {
let content = ''
try {
content = readFileSync(filePath, 'utf8')
} catch {
return null
}
const lines = content.split(/\r?\n/).length
const bytes = Buffer.byteLength(content, 'utf8')
const byHook: HookCounts = {}
let builtInCount = 0
for (const hook of BUILTIN_HOOKS) {
const count = countMatches(content, new RegExp(`\\b${hook}\\b`, 'g'))
byHook[hook] = count
builtInCount += count
}
const allHookCalls = content.match(/\buse[A-Z]\w*\b/g) ?? []
const customHookCount = Math.max(0, allHookCalls.filter(hook => !BUILTIN_HOOK_SET.has(hook)).length)
const hookCount = builtInCount + customHookCount
const effectCount = (byHook.useEffect ?? 0) + (byHook.useLayoutEffect ?? 0) + (byHook.useInsertionEffect ?? 0)
const memoCount = (byHook.useMemo ?? 0) + (byHook.useCallback ?? 0)
const reactMemoCount = countMatches(content, /\bReact\.memo\b/g)
const memoCallCount = countMatches(content, /\bmemo\s*\(/g)
const memoization = memoCount + reactMemoCount + Math.max(0, memoCallCount - reactMemoCount)
const estimatedRenderTimeMs = estimateRenderTimeMs(lines, hookCount, effectCount, memoization)
const reasons: string[] = []
if (lines >= THRESHOLDS.veryLargeComponentLines) {
reasons.push(`Very large component: ${lines} lines`)
} else if (lines >= THRESHOLDS.largeComponentLines) {
reasons.push(`Large component: ${lines} lines`)
}
if (hookCount >= THRESHOLDS.highHookCount) {
reasons.push(`High hook count: ${hookCount}`)
}
if (effectCount >= THRESHOLDS.highEffectCount) {
reasons.push(`Multiple effects: ${effectCount}`)
}
if (estimatedRenderTimeMs >= THRESHOLDS.slowRenderMs) {
reasons.push(`Estimated render time: ${estimatedRenderTimeMs}ms`)
}
let risk: ComponentMetrics['risk'] = 'low'
if (reasons.length >= 3 || estimatedRenderTimeMs >= THRESHOLDS.slowRenderMs) {
risk = 'high'
} else if (reasons.length >= 1) {
risk = 'medium'
}
return {
file: relative(process.cwd(), filePath),
component: basename(filePath, '.tsx'),
lines,
bytes,
hooks: {
builtIn: builtInCount,
custom: customHookCount,
total: hookCount,
byHook,
},
effects: effectCount,
memoization,
estimatedRenderTimeMs,
reasons,
risk,
}
}
function buildRecommendations(slowComponents: ComponentMetrics[]): string[] {
const recommendations: string[] = []
if (slowComponents.length === 0) {
recommendations.push('No high-risk components detected. Re-run after significant UI changes.')
return recommendations
}
if (slowComponents.some(component => component.lines >= THRESHOLDS.veryLargeComponentLines)) {
recommendations.push('Split components over 300 lines into smaller pieces to reduce render work.')
}
if (slowComponents.some(component => component.effects >= THRESHOLDS.highEffectCount)) {
recommendations.push('Reduce the number of effects per component by extracting side effects into hooks.')
}
if (slowComponents.some(component => component.hooks.total >= THRESHOLDS.highHookCount)) {
recommendations.push('Consider splitting stateful logic across smaller components or hooks.')
}
if (slowComponents.some(component => component.memoization === 0 && component.estimatedRenderTimeMs >= THRESHOLDS.slowRenderMs)) {
recommendations.push('Add memoization (React.memo/useMemo/useCallback) where render work is heavy.')
}
if (recommendations.length === 0) {
recommendations.push('Review flagged components for unnecessary renders or expensive computations.')
}
return recommendations
}
const rootDir = pickSourceRoot()
if (!rootDir) {
console.log(JSON.stringify({
analysisType: 'static-heuristic',
averageRenderTime: 0,
slowComponents: [],
recommendations: ['No source directory found to analyze.'],
timestamp: new Date().toISOString(),
}, null, 2))
process.exit(0)
}
const files: string[] = []
walkDir(rootDir, files)
const metrics: ComponentMetrics[] = files
.map(file => analyzeFile(file))
.filter((result): result is ComponentMetrics => result !== null)
const averageRenderTime = metrics.length === 0
? 0
: Math.round((metrics.reduce((sum, metric) => sum + metric.estimatedRenderTimeMs, 0) / metrics.length) * 10) / 10
const slowComponents = metrics
.filter(metric => metric.reasons.length > 0 || metric.estimatedRenderTimeMs >= THRESHOLDS.slowRenderMs)
.sort((a, b) => b.estimatedRenderTimeMs - a.estimatedRenderTimeMs)
const topByLines = [...metrics].sort((a, b) => b.lines - a.lines).slice(0, 10)
const topByHooks = [...metrics].sort((a, b) => b.hooks.total - a.hooks.total).slice(0, 10)
const summary = {
analysisType: 'static-heuristic',
rootDir: relative(process.cwd(), rootDir) || '.',
componentsAnalyzed: metrics.length,
averageRenderTime,
averageRenderTimeMs: averageRenderTime,
slowComponentsTotal: slowComponents.length,
thresholds: THRESHOLDS,
slowComponents: slowComponents.slice(0, 15),
topByLines,
topByHooks,
recommendations: buildRecommendations(slowComponents),
note: 'Estimated render times are derived from file size and hook usage. Use React Profiler for real timings.',
timestamp: new Date().toISOString(),
}
console.log(JSON.stringify(summary, null, 2))