From 9bb843f82fff45e47d21eb63d67ae1bb4dce6113 Mon Sep 17 00:00:00 2001 From: Cody Seibert Date: Sun, 14 Dec 2025 10:59:52 -0500 Subject: [PATCH] chore: update dependencies and improve project structure - Added `morgan` for enhanced request logging in the server. - Updated `package-lock.json` to include new dependencies and their types. - Refactored the `NewProjectModal` component for improved readability and structure. - Enhanced the `FileBrowserDialog` to support initial path selection and improved error handling. - Updated various components to ensure consistent formatting and better user experience. - Introduced XML format specification for app specifications to maintain consistency across the application. --- .../dialogs/file-browser-dialog.tsx | 62 +- apps/app/src/components/layout/sidebar.tsx | 532 ++++++++++++++-- apps/app/src/components/new-project-modal.tsx | 103 +++- .../src/components/views/analysis-view.tsx | 1 + apps/app/src/components/views/board-view.tsx | 109 +++- .../src/components/views/interview-view.tsx | 1 + apps/app/src/components/views/spec-view.tsx | 569 +++++++++++------- .../app/src/components/views/welcome-view.tsx | 3 + .../app/src/contexts/file-browser-context.tsx | 81 ++- apps/app/src/lib/electron.ts | 25 +- apps/app/src/store/app-store.ts | 18 + apps/app/src/types/electron.d.ts | 4 + apps/server/.env.example | 2 + apps/server/package.json | 2 + apps/server/src/index.ts | 18 + apps/server/src/lib/app-spec-format.ts | 88 +++ apps/server/src/lib/security.ts | 35 +- apps/server/src/routes/fs.ts | 197 +----- apps/server/src/routes/spec-regeneration.ts | 389 ++++++++---- package-lock.json | 82 +++ 20 files changed, 1667 insertions(+), 654 deletions(-) create mode 100644 apps/server/src/lib/app-spec-format.ts diff --git a/apps/app/src/components/dialogs/file-browser-dialog.tsx b/apps/app/src/components/dialogs/file-browser-dialog.tsx index e16c2a43..ba17944e 100644 --- a/apps/app/src/components/dialogs/file-browser-dialog.tsx +++ b/apps/app/src/components/dialogs/file-browser-dialog.tsx @@ -1,7 +1,14 @@ "use client"; import { useState, useEffect } from "react"; -import { FolderOpen, Folder, ChevronRight, Home, ArrowLeft, HardDrive } from "lucide-react"; +import { + FolderOpen, + Folder, + ChevronRight, + Home, + ArrowLeft, + HardDrive, +} from "lucide-react"; import { Dialog, DialogContent, @@ -24,6 +31,7 @@ interface BrowseResult { directories: DirectoryEntry[]; drives?: string[]; error?: string; + warning?: string; } interface FileBrowserDialogProps { @@ -32,6 +40,7 @@ interface FileBrowserDialogProps { onSelect: (path: string) => void; title?: string; description?: string; + initialPath?: string; } export function FileBrowserDialog({ @@ -40,6 +49,7 @@ export function FileBrowserDialog({ onSelect, title = "Select Project Directory", description = "Navigate to your project folder", + initialPath, }: FileBrowserDialogProps) { const [currentPath, setCurrentPath] = useState(""); const [parentPath, setParentPath] = useState(null); @@ -47,14 +57,17 @@ export function FileBrowserDialog({ const [drives, setDrives] = useState([]); const [loading, setLoading] = useState(false); const [error, setError] = useState(""); + const [warning, setWarning] = useState(""); const browseDirectory = async (dirPath?: string) => { setLoading(true); setError(""); + setWarning(""); try { // Get server URL from environment or default - const serverUrl = process.env.NEXT_PUBLIC_SERVER_URL || "http://localhost:3008"; + const serverUrl = + process.env.NEXT_PUBLIC_SERVER_URL || "http://localhost:3008"; const response = await fetch(`${serverUrl}/api/fs/browse`, { method: "POST", @@ -69,23 +82,37 @@ export function FileBrowserDialog({ setParentPath(result.parentPath); setDirectories(result.directories); setDrives(result.drives || []); + setWarning(result.warning || ""); } else { setError(result.error || "Failed to browse directory"); } } catch (err) { - setError(err instanceof Error ? err.message : "Failed to load directories"); + setError( + err instanceof Error ? err.message : "Failed to load directories" + ); } finally { setLoading(false); } }; - // Load home directory on mount + // Reset current path when dialog closes useEffect(() => { - if (open && !currentPath) { - browseDirectory(); + if (!open) { + setCurrentPath(""); + setParentPath(null); + setDirectories([]); + setError(""); + setWarning(""); } }, [open]); + // Load initial path or home directory when dialog opens + useEffect(() => { + if (open && !currentPath) { + browseDirectory(initialPath); + } + }, [open, initialPath]); + const handleSelectDirectory = (dir: DirectoryEntry) => { browseDirectory(dir.path); }; @@ -135,7 +162,9 @@ export function FileBrowserDialog({ {drives.map((drive) => ( - )} + + + + + + + {/* Delete Project Confirmation Dialog */} + + {/* New Project Modal */} + ); } diff --git a/apps/app/src/components/new-project-modal.tsx b/apps/app/src/components/new-project-modal.tsx index 2e54c8f4..addefb73 100644 --- a/apps/app/src/components/new-project-modal.tsx +++ b/apps/app/src/components/new-project-modal.tsx @@ -41,7 +41,10 @@ interface ValidationErrors { interface NewProjectModalProps { open: boolean; onOpenChange: (open: boolean) => void; - onCreateBlankProject: (projectName: string, parentDir: string) => Promise; + onCreateBlankProject: ( + projectName: string, + parentDir: string + ) => Promise; onCreateFromTemplate: ( template: StarterTemplate, projectName: string, @@ -67,7 +70,8 @@ export function NewProjectModal({ const [projectName, setProjectName] = useState(""); const [workspaceDir, setWorkspaceDir] = useState(""); const [isLoadingWorkspace, setIsLoadingWorkspace] = useState(false); - const [selectedTemplate, setSelectedTemplate] = useState(null); + const [selectedTemplate, setSelectedTemplate] = + useState(null); const [useCustomUrl, setUseCustomUrl] = useState(false); const [customUrl, setCustomUrl] = useState(""); const [errors, setErrors] = useState({}); @@ -78,7 +82,8 @@ export function NewProjectModal({ if (open) { setIsLoadingWorkspace(true); const httpClient = getHttpApiClient(); - httpClient.workspace.getConfig() + httpClient.workspace + .getConfig() .then((result) => { if (result.success && result.workspaceDir) { setWorkspaceDir(result.workspaceDir); @@ -113,7 +118,10 @@ export function NewProjectModal({ }, [projectName, errors.projectName]); useEffect(() => { - if ((selectedTemplate || (useCustomUrl && customUrl)) && errors.templateSelection) { + if ( + (selectedTemplate || (useCustomUrl && customUrl)) && + errors.templateSelection + ) { setErrors((prev) => ({ ...prev, templateSelection: false })); } }, [selectedTemplate, useCustomUrl, customUrl, errors.templateSelection]); @@ -187,7 +195,9 @@ export function NewProjectModal({ const handleBrowseDirectory = async () => { const selectedPath = await openFileBrowser({ title: "Select Base Project Directory", - description: "Choose the parent directory where your project will be created", + description: + "Choose the parent directory where your project will be created", + initialPath: workspaceDir || undefined, }); if (selectedPath) { setWorkspaceDir(selectedPath); @@ -199,9 +209,16 @@ export function NewProjectModal({ }; // Use platform-specific path separator - const pathSep = typeof window !== 'undefined' && (window as any).electronAPI ? - (navigator.platform.indexOf('Win') !== -1 ? '\\' : '/') : '/'; - const projectPath = workspaceDir && projectName ? `${workspaceDir}${pathSep}${projectName}` : ""; + const pathSep = + typeof window !== "undefined" && (window as any).electronAPI + ? navigator.platform.indexOf("Win") !== -1 + ? "\\" + : "/" + : "/"; + const projectPath = + workspaceDir && projectName + ? `${workspaceDir}${pathSep}${projectName}` + : ""; return ( @@ -210,7 +227,9 @@ export function NewProjectModal({ data-testid="new-project-modal" > - Create New Project + + Create New Project + Start with a blank project or choose from a starter template. @@ -219,8 +238,15 @@ export function NewProjectModal({ {/* Project Name Input - Always visible at top */}
-