From 77bacf0ad4befe3ac529614e61290ca4c9f9c588 Mon Sep 17 00:00:00 2001 From: johndoe6345789 Date: Fri, 16 Jan 2026 00:47:04 +0000 Subject: [PATCH] Generated by Spark: OpenAI integration could help with various pages in the app --- PRD.md | 44 +++-- spark.meta.json | 5 +- src/App.tsx | 24 ++- src/components/CodeEditor.tsx | 153 ++++++++++++--- src/components/ComponentTreeBuilder.tsx | 43 +++- src/components/FileExplorer.tsx | 186 ++++++++++++++---- src/components/ModelDesigner.tsx | 92 ++++++++- src/components/StyleDesigner.tsx | 41 +++- src/lib/ai-service.ts | 249 ++++++++++++++++++++++++ src/vite-end.d.ts | 26 ++- 10 files changed, 768 insertions(+), 95 deletions(-) create mode 100644 src/lib/ai-service.ts diff --git a/PRD.md b/PRD.md index 50cf032..b98dcee 100644 --- a/PRD.md +++ b/PRD.md @@ -13,32 +13,39 @@ This is a full-featured low-code IDE with multiple integrated tools (code editor ## Essential Features ### Monaco Code Editor Integration -- **Functionality**: Full-featured code editor with syntax highlighting, autocomplete, and multi-file editing -- **Purpose**: Allows direct code manipulation for users who want precise control +- **Functionality**: Full-featured code editor with syntax highlighting, autocomplete, multi-file editing, and AI-powered code improvement and explanation +- **Purpose**: Allows direct code manipulation for users who want precise control, with AI assistance for learning and optimization - **Trigger**: Clicking on files in the file tree or switching to code view -- **Progression**: Select file → Editor opens with syntax highlighting → Edit code → Changes auto-saved to state → Preview updates -- **Success criteria**: Code edits persist, syntax highlighting works for JS/TS/CSS, multiple files can be open in tabs +- **Progression**: Select file → Editor opens with syntax highlighting → Edit code → Use AI to improve or explain code → Changes auto-saved to state → Preview updates +- **Success criteria**: Code edits persist, syntax highlighting works for JS/TS/CSS, multiple files can be open in tabs, AI explanations are helpful, AI improvements are relevant + +### AI-Powered Code Generation +- **Functionality**: Generate complete files, components, models, and themes using natural language descriptions via OpenAI integration +- **Purpose**: Accelerates development by automating boilerplate and scaffolding based on user intent +- **Trigger**: Clicking AI/Sparkle icons in various sections or the main "AI Generate" button +- **Progression**: User describes intent → AI processes request → Generated code appears → User can refine or accept +- **Success criteria**: Generated code is syntactically valid, follows conventions, matches user intent, integrates with existing project structure ### Prisma Schema Designer -- **Functionality**: Visual model designer for database schemas with drag-and-drop field creation -- **Purpose**: Simplifies database modeling without requiring Prisma syntax knowledge +- **Functionality**: Visual model designer for database schemas with drag-and-drop field creation and AI-powered model generation and field suggestions +- **Purpose**: Simplifies database modeling without requiring Prisma syntax knowledge, with intelligent AI assistance - **Trigger**: Opening the Models tab -- **Progression**: Create model → Add fields with types → Define relations → Visual graph updates → Generate Prisma schema code -- **Success criteria**: Can create models, fields, relations; generates valid Prisma syntax; visual representation is clear +- **Progression**: Create model manually or with AI → Add fields with types or get AI suggestions → Define relations → Visual graph updates → Generate Prisma schema code +- **Success criteria**: Can create models, fields, relations; AI suggestions are contextually relevant; generates valid Prisma syntax; visual representation is clear ### Component Tree Builder -- **Functionality**: Hierarchical tree view for building React component structure -- **Purpose**: Visual composition of component hierarchy without writing JSX +- **Functionality**: Hierarchical tree view for building React component structure with AI-powered component generation +- **Purpose**: Visual composition of component hierarchy without writing JSX, enhanced by AI scaffolding - **Trigger**: Opening the Components tab -- **Progression**: Select component type → Add to tree → Configure props → Nest children → View generated JSX → Export component -- **Success criteria**: Can add/remove/reorder components; props are editable; generates valid React code +- **Progression**: Select component type or describe to AI → Add to tree → Configure props → Nest children → View generated JSX → Export component +- **Success criteria**: Can add/remove/reorder components; AI-generated components are well-structured; props are editable; generates valid React code ### Style Designer -- **Functionality**: Visual interface for Material UI theming and component styling -- **Purpose**: Configure colors, typography, spacing without manual theme object creation +- **Functionality**: Visual interface for Material UI theming and component styling with AI theme generation from descriptions +- **Purpose**: Configure colors, typography, spacing without manual theme object creation, with AI design assistance - **Trigger**: Opening the Styling tab -- **Progression**: Select theme property → Adjust values with controls → Preview updates live → Export theme configuration -- **Success criteria**: Color pickers work; typography scales properly; generates valid MUI theme code +- **Progression**: Select theme property → Adjust values with controls or describe style to AI → Preview updates live → Export theme configuration +- **Success criteria**: Color pickers work; typography scales properly; AI themes match descriptions and have good contrast; generates valid MUI theme code ### Project Generator - **Functionality**: Exports complete Next.js project with all configurations @@ -48,11 +55,14 @@ This is a full-featured low-code IDE with multiple integrated tools (code editor - **Success criteria**: Generated project structure is valid; includes package.json; code runs without errors ## Edge Case Handling -- **Empty Projects**: Show welcome screen with quick-start templates when no project exists +- **Empty Projects**: Show welcome screen with quick-start templates when no project exists; AI can generate entire projects from scratch - **Invalid Prisma Schemas**: Validate models and show inline errors before generating code - **Circular Component Dependencies**: Detect and warn when component tree has circular references - **Missing Required Props**: Highlight components with missing required Material UI props - **Large Files**: Implement virtual scrolling and lazy loading for large component trees and file lists +- **AI Generation Failures**: Provide clear error messages and fallback to manual editing when AI requests fail +- **Rate Limiting**: Handle OpenAI API rate limits gracefully with user-friendly messages +- **Invalid AI Responses**: Validate and sanitize AI-generated code before insertion ## Design Direction The design should evoke a professional IDE environment while remaining approachable - think Visual Studio Code meets Figma. Clean panels, clear hierarchy, and purposeful use of space to avoid overwhelming users with options. diff --git a/spark.meta.json b/spark.meta.json index 706a311..a277bc5 100644 --- a/spark.meta.json +++ b/spark.meta.json @@ -1,3 +1,4 @@ { - "templateVersion": 1 -} + "templateVersion": 1, + "dbType": "kv" +} \ No newline at end of file diff --git a/src/App.tsx b/src/App.tsx index acbc05f..107d106 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -12,6 +12,7 @@ import { ComponentTreeBuilder } from '@/components/ComponentTreeBuilder' import { StyleDesigner } from '@/components/StyleDesigner' import { FileExplorer } from '@/components/FileExplorer' import { generateNextJSProject, generatePrismaSchema, generateMUITheme } from '@/lib/generators' +import { AIService } from '@/lib/ai-service' import { toast } from 'sonner' import { Dialog, @@ -108,10 +109,31 @@ function App() { } const handleGenerateWithAI = async () => { + const description = prompt('Describe the application you want to generate:') + if (!description) return + try { - toast.info('AI generation coming soon!') + toast.info('Generating application with AI...') + + const result = await AIService.generateCompleteApp(description) + + if (result) { + if (result.files && result.files.length > 0) { + setFiles((currentFiles) => [...(currentFiles || []), ...result.files]) + } + if (result.models && result.models.length > 0) { + setModels((currentModels) => [...(currentModels || []), ...result.models]) + } + if (result.theme) { + setTheme((currentTheme) => ({ ...(currentTheme || DEFAULT_THEME), ...result.theme })) + } + toast.success('Application generated successfully!') + } else { + toast.error('AI generation failed. Please try again.') + } } catch (error) { toast.error('AI generation failed') + console.error(error) } } diff --git a/src/components/CodeEditor.tsx b/src/components/CodeEditor.tsx index 9bfb916..53c4275 100644 --- a/src/components/CodeEditor.tsx +++ b/src/components/CodeEditor.tsx @@ -1,8 +1,21 @@ +import { useState } from 'react' import Editor from '@monaco-editor/react' import { Card } from '@/components/ui/card' import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs' +import { Button } from '@/components/ui/button' import { ProjectFile } from '@/types/project' -import { FileCode, X } from '@phosphor-icons/react' +import { FileCode, X, Sparkle, Info } from '@phosphor-icons/react' +import { AIService } from '@/lib/ai-service' +import { toast } from 'sonner' +import { + Dialog, + DialogContent, + DialogDescription, + DialogHeader, + DialogTitle, +} from '@/components/ui/dialog' +import { Textarea } from '@/components/ui/textarea' +import { ScrollArea } from '@/components/ui/scroll-area' interface CodeEditorProps { files: ProjectFile[] @@ -19,37 +32,110 @@ export function CodeEditor({ onFileSelect, onFileClose, }: CodeEditorProps) { + const [showExplainDialog, setShowExplainDialog] = useState(false) + const [explanation, setExplanation] = useState('') + const [isExplaining, setIsExplaining] = useState(false) + const activeFile = files.find((f) => f.id === activeFileId) const openFiles = files.filter((f) => f.id === activeFileId || files.length < 5) + const improveCodeWithAI = async () => { + if (!activeFile) return + + const instruction = prompt('How would you like to improve this code?') + if (!instruction) return + + try { + toast.info('Improving code with AI...') + const improvedCode = await AIService.improveCode(activeFile.content, instruction) + + if (improvedCode) { + onFileChange(activeFile.id, improvedCode) + toast.success('Code improved successfully!') + } else { + toast.error('AI improvement failed. Please try again.') + } + } catch (error) { + toast.error('Failed to improve code') + console.error(error) + } + } + + const explainCode = async () => { + if (!activeFile) return + + try { + setIsExplaining(true) + setShowExplainDialog(true) + setExplanation('Analyzing code...') + + const codeExplanation = await AIService.explainCode(activeFile.content) + + if (codeExplanation) { + setExplanation(codeExplanation) + } else { + setExplanation('Failed to generate explanation. Please try again.') + } + } catch (error) { + setExplanation('Error generating explanation.') + console.error(error) + } finally { + setIsExplaining(false) + } + } + return (
{openFiles.length > 0 ? ( <> -
- {openFiles.map((file) => ( - - - ))} + ))} +
+ {activeFile && ( +
+ + +
+ )}
{activeFile && ( @@ -80,6 +166,29 @@ export function CodeEditor({
)} + + + + + Code Explanation + + AI-generated explanation of {activeFile?.name} + + + +
+ {isExplaining ? ( +
+ + Analyzing code... +
+ ) : ( +

{explanation}

+ )} +
+
+
+
) } diff --git a/src/components/ComponentTreeBuilder.tsx b/src/components/ComponentTreeBuilder.tsx index 49ca24d..68bc4a7 100644 --- a/src/components/ComponentTreeBuilder.tsx +++ b/src/components/ComponentTreeBuilder.tsx @@ -6,8 +6,10 @@ import { Input } from '@/components/ui/input' import { Label } from '@/components/ui/label' import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select' import { ScrollArea } from '@/components/ui/scroll-area' -import { Plus, Trash, Tree, CaretRight, CaretDown } from '@phosphor-icons/react' +import { Plus, Trash, Tree, CaretRight, CaretDown, Sparkle } from '@phosphor-icons/react' import { Textarea } from '@/components/ui/textarea' +import { AIService } from '@/lib/ai-service' +import { toast } from 'sonner' interface ComponentTreeBuilderProps { components: ComponentNode[] @@ -129,6 +131,28 @@ export function ComponentTreeBuilder({ setExpandedNodes(newExpanded) } + const generateComponentWithAI = async () => { + const description = prompt('Describe the component you want to create:') + if (!description) return + + try { + toast.info('Generating component with AI...') + const component = await AIService.generateComponent(description) + + if (component) { + onComponentsChange([...components, component]) + setSelectedNodeId(component.id) + setExpandedNodes(new Set([...Array.from(expandedNodes), component.id])) + toast.success(`Component "${component.name}" created successfully!`) + } else { + toast.error('AI generation failed. Please try again.') + } + } catch (error) { + toast.error('Failed to generate component') + console.error(error) + } + } + const renderTreeNode = (node: ComponentNode, level: number = 0) => { const isExpanded = expandedNodes.has(node.id) const isSelected = selectedNodeId === node.id @@ -174,9 +198,20 @@ export function ComponentTreeBuilder({

Component Tree

- +
+ + +
diff --git a/src/components/FileExplorer.tsx b/src/components/FileExplorer.tsx index 2d56072..486ee81 100644 --- a/src/components/FileExplorer.tsx +++ b/src/components/FileExplorer.tsx @@ -3,7 +3,8 @@ import { ProjectFile } from '@/types/project' import { ScrollArea } from '@/components/ui/scroll-area' import { Button } from '@/components/ui/button' import { Input } from '@/components/ui/input' -import { FileCode, FolderOpen, Plus, Folder } from '@phosphor-icons/react' +import { Textarea } from '@/components/ui/textarea' +import { FileCode, FolderOpen, Plus, Folder, Sparkle } from '@phosphor-icons/react' import { Dialog, DialogContent, @@ -19,6 +20,9 @@ import { SelectTrigger, SelectValue, } from '@/components/ui/select' +import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs' +import { AIService } from '@/lib/ai-service' +import { toast } from 'sonner' interface FileExplorerProps { files: ProjectFile[] @@ -36,6 +40,9 @@ export function FileExplorer({ const [isAddDialogOpen, setIsAddDialogOpen] = useState(false) const [newFileName, setNewFileName] = useState('') const [newFileLanguage, setNewFileLanguage] = useState('typescript') + const [aiDescription, setAiDescription] = useState('') + const [aiFileType, setAiFileType] = useState<'component' | 'page' | 'api' | 'utility'>('component') + const [isGenerating, setIsGenerating] = useState(false) const handleAddFile = () => { if (!newFileName.trim()) return @@ -53,6 +60,43 @@ export function FileExplorer({ setIsAddDialogOpen(false) } + const handleGenerateFileWithAI = async () => { + if (!aiDescription.trim() || !newFileName.trim()) { + toast.error('Please provide both a filename and description') + return + } + + try { + setIsGenerating(true) + toast.info('Generating code with AI...') + + const code = await AIService.generateCodeFromDescription(aiDescription, aiFileType) + + if (code) { + const newFile: ProjectFile = { + id: `file-${Date.now()}`, + name: newFileName, + path: `/src/${newFileName}`, + content: code, + language: newFileLanguage, + } + + onFileAdd(newFile) + setNewFileName('') + setAiDescription('') + setIsAddDialogOpen(false) + toast.success('File generated successfully!') + } else { + toast.error('AI generation failed. Please try again.') + } + } catch (error) { + toast.error('Failed to generate file') + console.error(error) + } finally { + setIsGenerating(false) + } + } + const groupedFiles = files.reduce((acc, file) => { const dir = file.path.split('/').slice(0, -1).join('/') || '/' if (!acc[dir]) acc[dir] = [] @@ -77,40 +121,114 @@ export function FileExplorer({ Add New File -
-
- - setNewFileName(e.target.value)} - placeholder="example.tsx" - onKeyDown={(e) => { - if (e.key === 'Enter') handleAddFile() - }} - /> -
-
- - setNewFileName(e.target.value)} + placeholder="example.tsx" + onKeyDown={(e) => { + if (e.key === 'Enter') handleAddFile() + }} + /> +
+
+ + +
+ + + +
+ + setNewFileName(e.target.value)} + placeholder="UserCard.tsx" + /> +
+
+ + +
+
+ +