diff --git a/PRD.md b/PRD.md index a540f71..44b2d51 100644 --- a/PRD.md +++ b/PRD.md @@ -40,6 +40,27 @@ This is a full-featured low-code IDE with multiple integrated tools (code editor - **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 +### Component Tree Manager +- **Functionality**: Manage multiple named component trees, each with its own hierarchy and component structure +- **Purpose**: Organize different parts of the application (Main App, Dashboard, Admin Panel) into separate, manageable trees +- **Trigger**: Opening the Component Trees tab +- **Progression**: Create tree → Name and describe purpose → Build component hierarchy → Switch between trees → Duplicate or delete trees → Export all trees +- **Success criteria**: Can create unlimited trees; each tree maintains independent state; trees can be duplicated; generates organized component structure per tree + +### n8n-Style Workflow Designer +- **Functionality**: Visual workflow builder with draggable nodes (triggers, actions, conditions, transforms, lambdas, APIs, database queries) connected by visual connections +- **Purpose**: Design automation workflows and business logic visually without coding complex orchestration +- **Trigger**: Opening the Workflows tab +- **Progression**: Create workflow → Add nodes (drag/position on canvas) → Connect nodes → Configure each node (API endpoints, conditions, lambda code) → Activate workflow → Export workflow definition +- **Success criteria**: Nodes can be positioned freely; connections show data flow; Monaco editor for lambda/transform nodes; supports HTTP methods, database queries, conditional branching; workflows persist and can be activated/deactivated + +### Lambda Function Designer +- **Functionality**: Full-featured serverless function editor with Monaco code editor, multiple runtime support (Node.js, Python), environment variables, and trigger configuration +- **Purpose**: Create and manage serverless functions with professional IDE features +- **Trigger**: Opening the Lambdas tab +- **Progression**: Create lambda → Select language (TypeScript/JavaScript/Python) → Write code in Monaco editor → Configure runtime, timeout, memory → Add triggers (HTTP, schedule, event, queue) → Set environment variables → Export lambda definitions +- **Success criteria**: Monaco editor with syntax highlighting; supports multiple languages; configuration matches AWS Lambda standards; triggers are configurable; environment variables are managed securely; generates deployment-ready functions + ### Style Designer - **Functionality**: Visual interface for Material UI theming with support for multiple theme variants (light/dark/custom), custom color management, and AI theme generation from descriptions - **Purpose**: Configure colors, typography, spacing with support for unlimited theme variants and custom color palettes beyond standard specifications, with AI design assistance diff --git a/src/App.tsx b/src/App.tsx index b73eed8..44c220c 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -6,11 +6,14 @@ import { Button } from '@/components/ui/button' import { Badge } from '@/components/ui/badge' import { Card } from '@/components/ui/card' import { ResizableHandle, ResizablePanel, ResizablePanelGroup } from '@/components/ui/resizable' -import { Code, Database, Tree, PaintBrush, Download, Sparkle, Flask, BookOpen, Play, Wrench, Gear, Cube, FileText, ChartBar, Keyboard } from '@phosphor-icons/react' -import { ProjectFile, PrismaModel, ComponentNode, ThemeConfig, PlaywrightTest, StorybookStory, UnitTest, FlaskConfig, NextJsConfig, NpmSettings } from '@/types/project' +import { Code, Database, Tree, PaintBrush, Download, Sparkle, Flask, BookOpen, Play, Wrench, Gear, Cube, FileText, ChartBar, Keyboard, FlowArrow } from '@phosphor-icons/react' +import { ProjectFile, PrismaModel, ComponentNode, ComponentTree, ThemeConfig, PlaywrightTest, StorybookStory, UnitTest, FlaskConfig, NextJsConfig, NpmSettings, Workflow, Lambda } from '@/types/project' import { CodeEditor } from '@/components/CodeEditor' import { ModelDesigner } from '@/components/ModelDesigner' import { ComponentTreeBuilder } from '@/components/ComponentTreeBuilder' +import { ComponentTreeManager } from '@/components/ComponentTreeManager' +import { WorkflowDesigner } from '@/components/WorkflowDesigner' +import { LambdaDesigner } from '@/components/LambdaDesigner' import { StyleDesigner } from '@/components/StyleDesigner' import { FileExplorer } from '@/components/FileExplorer' import { PlaywrightDesigner } from '@/components/PlaywrightDesigner' @@ -140,6 +143,18 @@ function App() { const [files, setFiles] = useKV('project-files', DEFAULT_FILES) const [models, setModels] = useKV('project-models', []) const [components, setComponents] = useKV('project-components', []) + const [componentTrees, setComponentTrees] = useKV('project-component-trees', [ + { + id: 'default-tree', + name: 'Main App', + description: 'Default component tree', + rootNodes: [], + createdAt: Date.now(), + updatedAt: Date.now(), + }, + ]) + const [workflows, setWorkflows] = useKV('project-workflows', []) + const [lambdas, setLambdas] = useKV('project-lambdas', []) const [theme, setTheme] = useKV('project-theme', DEFAULT_THEME) const [playwrightTests, setPlaywrightTests] = useKV('project-playwright-tests', []) const [storybookStories, setStorybookStories] = useKV('project-storybook-stories', []) @@ -156,6 +171,9 @@ function App() { const safeFiles = files || [] const safeModels = models || [] const safeComponents = components || [] + const safeComponentTrees = componentTrees || [] + const safeWorkflows = workflows || [] + const safeLambdas = lambdas || [] const safeTheme = (theme && theme.variants && theme.variants.length > 0) ? theme : DEFAULT_THEME const safePlaywrightTests = playwrightTests || [] const safeStorybookStories = storybookStories || [] @@ -200,6 +218,24 @@ function App() { { key: '5', ctrl: true, + description: 'Go to Component Trees', + action: () => setActiveTab('component-trees'), + }, + { + key: '6', + ctrl: true, + description: 'Go to Workflows', + action: () => setActiveTab('workflows'), + }, + { + key: '7', + ctrl: true, + description: 'Go to Lambdas', + action: () => setActiveTab('lambdas'), + }, + { + key: '8', + ctrl: true, description: 'Go to Styling', action: () => setActiveTab('styling'), }, @@ -457,6 +493,18 @@ Navigate to the backend directory and follow the setup instructions. Components + + + Component Trees + + + + Workflows + + + + Lambdas + Styling @@ -549,6 +597,27 @@ Navigate to the backend directory and follow the setup instructions. /> + + + + + + + + + + + + diff --git a/src/components/ComponentTreeManager.tsx b/src/components/ComponentTreeManager.tsx new file mode 100644 index 0000000..f997474 --- /dev/null +++ b/src/components/ComponentTreeManager.tsx @@ -0,0 +1,302 @@ +import { useState } from 'react' +import { ComponentTree, ComponentNode } from '@/types/project' +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 { Textarea } from '@/components/ui/textarea' +import { ScrollArea } from '@/components/ui/scroll-area' +import { Badge } from '@/components/ui/badge' +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, +} from '@/components/ui/dialog' +import { Plus, Tree, Trash, Pencil, Copy, FolderOpen } from '@phosphor-icons/react' +import { ComponentTreeBuilder } from '@/components/ComponentTreeBuilder' +import { toast } from 'sonner' +import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs' + +interface ComponentTreeManagerProps { + trees: ComponentTree[] + onTreesChange: (updater: (trees: ComponentTree[]) => ComponentTree[]) => void +} + +export function ComponentTreeManager({ trees, onTreesChange }: ComponentTreeManagerProps) { + const [selectedTreeId, setSelectedTreeId] = useState(trees[0]?.id || null) + const [createDialogOpen, setCreateDialogOpen] = useState(false) + const [editDialogOpen, setEditDialogOpen] = useState(false) + const [newTreeName, setNewTreeName] = useState('') + const [newTreeDescription, setNewTreeDescription] = useState('') + const [editingTree, setEditingTree] = useState(null) + + const selectedTree = trees.find(t => t.id === selectedTreeId) + + const handleCreateTree = () => { + if (!newTreeName.trim()) { + toast.error('Please enter a tree name') + return + } + + const newTree: ComponentTree = { + id: `tree-${Date.now()}`, + name: newTreeName, + description: newTreeDescription, + rootNodes: [], + createdAt: Date.now(), + updatedAt: Date.now(), + } + + onTreesChange((current) => [...current, newTree]) + setSelectedTreeId(newTree.id) + setNewTreeName('') + setNewTreeDescription('') + setCreateDialogOpen(false) + toast.success('Component tree created') + } + + const handleEditTree = () => { + if (!editingTree || !newTreeName.trim()) return + + onTreesChange((current) => + current.map((tree) => + tree.id === editingTree.id + ? { ...tree, name: newTreeName, description: newTreeDescription, updatedAt: Date.now() } + : tree + ) + ) + setEditDialogOpen(false) + setEditingTree(null) + setNewTreeName('') + setNewTreeDescription('') + toast.success('Component tree updated') + } + + const handleDeleteTree = (treeId: string) => { + if (trees.length === 1) { + toast.error('Cannot delete the last component tree') + return + } + + if (confirm('Are you sure you want to delete this component tree?')) { + onTreesChange((current) => current.filter((t) => t.id !== treeId)) + if (selectedTreeId === treeId) { + setSelectedTreeId(trees.find(t => t.id !== treeId)?.id || null) + } + toast.success('Component tree deleted') + } + } + + const handleDuplicateTree = (tree: ComponentTree) => { + const duplicatedTree: ComponentTree = { + ...tree, + id: `tree-${Date.now()}`, + name: `${tree.name} (Copy)`, + createdAt: Date.now(), + updatedAt: Date.now(), + } + + onTreesChange((current) => [...current, duplicatedTree]) + setSelectedTreeId(duplicatedTree.id) + toast.success('Component tree duplicated') + } + + const handleComponentsChange = (components: ComponentNode[]) => { + if (!selectedTreeId) return + + onTreesChange((current) => + current.map((tree) => + tree.id === selectedTreeId + ? { ...tree, rootNodes: components, updatedAt: Date.now() } + : tree + ) + ) + } + + const openEditDialog = (tree: ComponentTree) => { + setEditingTree(tree) + setNewTreeName(tree.name) + setNewTreeDescription(tree.description) + setEditDialogOpen(true) + } + + return ( +
+
+
+

+ + Component Trees +

+ +
+ + +
+ {trees.map((tree) => ( + setSelectedTreeId(tree.id)} + > + +
+
+ {tree.name} + {tree.description && ( + + {tree.description} + + )} +
+ + {tree.rootNodes.length} components + +
+
+
+
e.stopPropagation()}> + + + +
+
+
+ ))} +
+
+ + {trees.length === 0 && ( +
+
+ +

No component trees yet

+ +
+
+ )} +
+ +
+ {selectedTree ? ( + + ) : ( +
+
+ +

Select a component tree to edit

+
+
+ )} +
+ + + + + Create Component Tree + + Create a new component tree to organize your UI components + + +
+
+ + setNewTreeName(e.target.value)} + placeholder="e.g., Main App, Dashboard, Admin Panel" + /> +
+
+ +