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) => (
-
)}
+
+
)
}
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()
- }}
- />
-
-
-
-
-
-
- Add File
-
-
+ {isGenerating ? (
+ <>Generating...>
+ ) : (
+ <>
+
+ Generate with AI
+ >
+ )}
+
+
+
diff --git a/src/components/ModelDesigner.tsx b/src/components/ModelDesigner.tsx
index bbd6529..74aa1ec 100644
--- a/src/components/ModelDesigner.tsx
+++ b/src/components/ModelDesigner.tsx
@@ -7,8 +7,10 @@ import { Label } from '@/components/ui/label'
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'
import { Switch } from '@/components/ui/switch'
import { ScrollArea } from '@/components/ui/scroll-area'
-import { Plus, Trash, Database } from '@phosphor-icons/react'
+import { Plus, Trash, Database, Sparkle, Lightbulb } from '@phosphor-icons/react'
import { Badge } from '@/components/ui/badge'
+import { AIService } from '@/lib/ai-service'
+import { toast } from 'sonner'
interface ModelDesignerProps {
models: PrismaModel[]
@@ -97,14 +99,77 @@ export function ModelDesigner({ models, onModelsChange }: ModelDesignerProps) {
})
}
+ const generateModelWithAI = async () => {
+ const description = prompt('Describe the database model you want to create:')
+ if (!description) return
+
+ try {
+ toast.info('Generating model with AI...')
+ const model = await AIService.generatePrismaModel(description, models)
+
+ if (model) {
+ onModelsChange([...models, model])
+ setSelectedModelId(model.id)
+ toast.success(`Model "${model.name}" created successfully!`)
+ } else {
+ toast.error('AI generation failed. Please try again.')
+ }
+ } catch (error) {
+ toast.error('Failed to generate model')
+ console.error(error)
+ }
+ }
+
+ const suggestFields = async () => {
+ if (!selectedModel) return
+
+ try {
+ toast.info('Getting field suggestions...')
+ const existingFieldNames = selectedModel.fields.map(f => f.name)
+ const suggestions = await AIService.suggestFieldsForModel(selectedModel.name, existingFieldNames)
+
+ if (suggestions && suggestions.length > 0) {
+ const newFields: PrismaField[] = suggestions.map((fieldName, index) => ({
+ id: `field-${Date.now()}-${index}`,
+ name: fieldName,
+ type: 'String',
+ isRequired: false,
+ isUnique: false,
+ isArray: false,
+ }))
+
+ updateModel(selectedModel.id, {
+ fields: [...selectedModel.fields, ...newFields],
+ })
+ toast.success(`Added ${suggestions.length} suggested fields!`)
+ } else {
+ toast.error('No suggestions available')
+ }
+ } catch (error) {
+ toast.error('Failed to get suggestions')
+ console.error(error)
+ }
+ }
+
return (
@@ -155,10 +220,21 @@ export function ModelDesigner({ models, onModelsChange }: ModelDesignerProps) {
Fields
-
-
- Add Field
-
+
+
+
+ Suggest
+
+
+
+ Add Field
+
+
diff --git a/src/components/StyleDesigner.tsx b/src/components/StyleDesigner.tsx
index e978203..af89548 100644
--- a/src/components/StyleDesigner.tsx
+++ b/src/components/StyleDesigner.tsx
@@ -3,7 +3,10 @@ import { Card } from '@/components/ui/card'
import { Label } from '@/components/ui/label'
import { Input } from '@/components/ui/input'
import { Slider } from '@/components/ui/slider'
-import { PaintBrush } from '@phosphor-icons/react'
+import { Button } from '@/components/ui/button'
+import { PaintBrush, Sparkle } from '@phosphor-icons/react'
+import { AIService } from '@/lib/ai-service'
+import { toast } from 'sonner'
interface StyleDesignerProps {
theme: ThemeConfig
@@ -15,14 +18,40 @@ export function StyleDesigner({ theme, onThemeChange }: StyleDesignerProps) {
onThemeChange({ ...theme, ...updates })
}
+ const generateThemeWithAI = async () => {
+ const description = prompt('Describe the visual style you want (e.g., "modern and professional", "vibrant and playful"):')
+ if (!description) return
+
+ try {
+ toast.info('Generating theme with AI...')
+ const generatedTheme = await AIService.generateThemeFromDescription(description)
+
+ if (generatedTheme) {
+ onThemeChange({ ...theme, ...generatedTheme })
+ toast.success('Theme generated successfully!')
+ } else {
+ toast.error('AI generation failed. Please try again.')
+ }
+ } catch (error) {
+ toast.error('Failed to generate theme')
+ console.error(error)
+ }
+ }
+
return (
-
-
Material UI Theme Designer
-
- Customize your application's visual theme
-
+
+
+
Material UI Theme Designer
+
+ Customize your application's visual theme
+
+
+
+
+ Generate with AI
+
diff --git a/src/lib/ai-service.ts b/src/lib/ai-service.ts
new file mode 100644
index 0000000..abdb8b9
--- /dev/null
+++ b/src/lib/ai-service.ts
@@ -0,0 +1,249 @@
+import { PrismaModel, ComponentNode, ThemeConfig, ProjectFile } from '@/types/project'
+
+export class AIService {
+ static async generateComponent(description: string): Promise {
+ try {
+ const promptText = `You are a React component generator. Generate a component tree structure based on this description: "${description}"
+
+Return a valid JSON object with a single property "component" containing the component structure. The component should follow this format:
+{
+ "component": {
+ "id": "unique-id",
+ "type": "Box" (use Material UI component names like Box, Typography, Button, TextField, Grid, Paper, Card, etc.),
+ "name": "ComponentName",
+ "props": {
+ "sx": { "p": 2 } (Material UI sx props)
+ },
+ "children": [] (array of nested components following same structure)
+ }
+}
+
+Make sure to use appropriate Material UI components and props. Keep the structure clean and semantic.`
+
+ const response = await window.spark.llm(promptText, 'gpt-4o', true)
+ const parsed = JSON.parse(response)
+ return parsed.component
+ } catch (error) {
+ console.error('AI component generation failed:', error)
+ return null
+ }
+ }
+
+ static async generatePrismaModel(description: string, existingModels: PrismaModel[]): Promise {
+ try {
+ const existingModelNames = existingModels.map(m => m.name).join(', ')
+ const promptText = `You are a Prisma schema designer. Create a database model based on this description: "${description}"
+
+Existing models in the schema: ${existingModelNames || 'none'}
+
+Return a valid JSON object with a single property "model" containing the model structure:
+{
+ "model": {
+ "id": "unique-id-here",
+ "name": "ModelName" (PascalCase, singular),
+ "fields": [
+ {
+ "id": "field-id-1",
+ "name": "id",
+ "type": "String",
+ "isRequired": true,
+ "isUnique": true,
+ "isArray": false,
+ "defaultValue": "uuid()"
+ },
+ {
+ "id": "field-id-2",
+ "name": "fieldName",
+ "type": "String" (String, Int, Boolean, DateTime, Float, or existing model name for relations),
+ "isRequired": true,
+ "isUnique": false,
+ "isArray": false
+ }
+ ]
+ }
+}
+
+Include an id field with uuid() default. Add createdAt and updatedAt DateTime fields with @default(now()) and @updatedAt. Use appropriate field types and relationships.`
+
+ const response = await window.spark.llm(promptText, 'gpt-4o', true)
+ const parsed = JSON.parse(response)
+ return parsed.model
+ } catch (error) {
+ console.error('AI model generation failed:', error)
+ return null
+ }
+ }
+
+ static async generateCodeFromDescription(
+ description: string,
+ fileType: 'component' | 'page' | 'api' | 'utility'
+ ): Promise {
+ try {
+ const fileTypeInstructions = {
+ component: "Create a reusable React component with TypeScript. Use Material UI components and proper typing.",
+ page: "Create a Next.js page component with 'use client' directive if needed. Use Material UI and proper page structure.",
+ api: "Create a Next.js API route handler with proper types and error handling.",
+ utility: "Create a utility function with TypeScript types and JSDoc comments."
+ }
+
+ const promptText = `You are a Next.js developer. ${fileTypeInstructions[fileType]}
+
+Description: "${description}"
+
+Generate clean, production-ready code following Next.js 14 and Material UI best practices. Include all necessary imports.
+
+Return ONLY the code without any markdown formatting or explanations.`
+
+ const code = await window.spark.llm(promptText, 'gpt-4o', false)
+ return code.trim()
+ } catch (error) {
+ console.error('AI code generation failed:', error)
+ return null
+ }
+ }
+
+ static async improveCode(code: string, instruction: string): Promise {
+ try {
+ const promptText = `You are a code improvement assistant. Improve the following code based on this instruction: "${instruction}"
+
+Original code:
+${code}
+
+Return ONLY the improved code without any markdown formatting or explanations.`
+
+ const improved = await window.spark.llm(promptText, 'gpt-4o', false)
+ return improved.trim()
+ } catch (error) {
+ console.error('AI code improvement failed:', error)
+ return null
+ }
+ }
+
+ static async generateThemeFromDescription(description: string): Promise | null> {
+ try {
+ const promptText = `You are a UI/UX designer. Generate a Material UI theme configuration based on this description: "${description}"
+
+Return a valid JSON object with a single property "theme" containing:
+{
+ "theme": {
+ "primaryColor": "#hex-color",
+ "secondaryColor": "#hex-color",
+ "errorColor": "#hex-color",
+ "warningColor": "#ff9800",
+ "successColor": "#hex-color",
+ "fontFamily": "font-name, fallback",
+ "fontSize": {
+ "small": 12,
+ "medium": 14,
+ "large": 20
+ },
+ "spacing": 8,
+ "borderRadius": 4
+ }
+}
+
+Choose colors that match the description and ensure good contrast. Use common font stacks.`
+
+ const response = await window.spark.llm(promptText, 'gpt-4o', true)
+ const parsed = JSON.parse(response)
+ return parsed.theme
+ } catch (error) {
+ console.error('AI theme generation failed:', error)
+ return null
+ }
+ }
+
+ static async suggestFieldsForModel(modelName: string, existingFields: string[]): Promise {
+ try {
+ const promptText = `You are a database architect. Suggest additional useful fields for a Prisma model named "${modelName}".
+
+Existing fields: ${existingFields.join(', ')}
+
+Return a valid JSON object with a single property "fields" containing an array of field name suggestions (strings only):
+{
+ "fields": ["fieldName1", "fieldName2", "fieldName3"]
+}
+
+Suggest 3-5 common fields that would be useful for this model type. Use camelCase naming.`
+
+ const response = await window.spark.llm(promptText, 'gpt-4o', true)
+ const parsed = JSON.parse(response)
+ return parsed.fields
+ } catch (error) {
+ console.error('AI field suggestion failed:', error)
+ return null
+ }
+ }
+
+ static async explainCode(code: string): Promise {
+ try {
+ const promptText = `You are a code teacher. Explain what this code does in simple terms:
+
+${code}
+
+Provide a clear, concise explanation suitable for developers learning the codebase.`
+
+ const explanation = await window.spark.llm(promptText, 'gpt-4o', false)
+ return explanation.trim()
+ } catch (error) {
+ console.error('AI code explanation failed:', error)
+ return null
+ }
+ }
+
+ static async generateCompleteApp(description: string): Promise<{ files: ProjectFile[], models: PrismaModel[], theme: Partial } | null> {
+ try {
+ const promptText = `You are a full-stack application architect. Design a complete Next.js application based on: "${description}"
+
+Return a valid JSON object with properties "files", "models", and "theme":
+{
+ "files": [
+ {
+ "id": "unique-id",
+ "name": "page.tsx",
+ "path": "/src/app/page.tsx",
+ "content": "full code content here",
+ "language": "typescript"
+ }
+ ],
+ "models": [
+ {
+ "id": "unique-id",
+ "name": "User",
+ "fields": [
+ {
+ "id": "field-id",
+ "name": "id",
+ "type": "String",
+ "isRequired": true,
+ "isUnique": true,
+ "isArray": false,
+ "defaultValue": "uuid()"
+ }
+ ]
+ }
+ ],
+ "theme": {
+ "primaryColor": "#1976d2",
+ "secondaryColor": "#dc004e",
+ "errorColor": "#f44336",
+ "warningColor": "#ff9800",
+ "successColor": "#4caf50",
+ "fontFamily": "Roboto, Arial, sans-serif",
+ "fontSize": { "small": 12, "medium": 14, "large": 20 },
+ "spacing": 8,
+ "borderRadius": 4
+ }
+}
+
+Create 2-4 essential files for the app structure. Include appropriate Prisma models. Design a cohesive theme.`
+
+ const response = await window.spark.llm(promptText, 'gpt-4o', true)
+ const parsed = JSON.parse(response)
+ return parsed
+ } catch (error) {
+ console.error('AI app generation failed:', error)
+ return null
+ }
+ }
+}
diff --git a/src/vite-end.d.ts b/src/vite-end.d.ts
index beae9ae..877c007 100644
--- a/src/vite-end.d.ts
+++ b/src/vite-end.d.ts
@@ -1,3 +1,27 @@
///
declare const GITHUB_RUNTIME_PERMANENT_NAME: string
-declare const BASE_KV_SERVICE_URL: string
\ No newline at end of file
+declare const BASE_KV_SERVICE_URL: string
+
+declare global {
+ interface Window {
+ spark: {
+ llmPrompt(strings: TemplateStringsArray, ...values: any[]): string
+ llm(prompt: string, modelName?: string, jsonMode?: boolean): Promise
+ user(): Promise<{
+ avatarUrl: string
+ email: string
+ id: string
+ isOwner: boolean
+ login: string
+ }>
+ kv: {
+ keys(): Promise
+ get(key: string): Promise
+ set(key: string, value: T): Promise
+ delete(key: string): Promise
+ }
+ }
+ }
+
+ var spark: Window['spark']
+}
\ No newline at end of file