# Task 002: Empty State UI & Folder Selection ## Goal Create the initial empty state interface that appears when no instances are running, with folder selection capability. ## Prerequisites - Task 001 completed (project setup) - Basic understanding of SolidJS components - Electron IPC understanding ## Acceptance Criteria - [ ] Empty state displays when no instances exist - [ ] "Select Folder" button visible and styled - [ ] Clicking button triggers Electron dialog - [ ] Selected folder path displays temporarily - [ ] UI matches design spec (centered, clean) - [ ] Keyboard shortcut Cmd/Ctrl+N works - [ ] Error handling for cancelled selection ## Steps ### 1. Create Empty State Component **src/components/empty-state.tsx:** **Structure:** - Centered container - Large folder icon (from lucide-solid) - Subheading: "Select a folder to start coding with AI" - Primary button: "Select Folder" - Helper text: "Keyboard shortcut: Cmd/Ctrl+N" **Styling:** - Use TailwindCSS utilities - Center vertically and horizontally - Max width: 500px - Padding: 32px - Icon size: 64px - Text sizes: Heading 24px, body 16px, helper 14px - Colors: Follow design spec (light/dark mode) **Props:** - `onSelectFolder: () => void` - Callback when button clicked ### 2. Create UI Store **src/stores/ui.ts:** **State:** ```typescript interface UIStore { hasInstances: boolean selectedFolder: string | null isSelectingFolder: boolean } ``` **Signals:** - `hasInstances` - Reactive boolean - `selectedFolder` - Reactive string or null - `isSelectingFolder` - Reactive boolean (loading state) **Actions:** - `setHasInstances(value: boolean)` - `setSelectedFolder(path: string | null)` - `setIsSelectingFolder(value: boolean)` ### 3. Implement IPC for Folder Selection **electron/main/main.ts additions:** **IPC Handler:** - Register handler for 'dialog:selectFolder' - Use `dialog.showOpenDialog()` with: - `properties: ['openDirectory']` - Title: "Select Project Folder" - Button label: "Select" - Return selected folder path or null if cancelled - Handle errors gracefully **electron/preload/index.ts additions:** **Expose method:** ```typescript electronAPI: { selectFolder: () => Promise } ``` **Type definitions:** ```typescript interface ElectronAPI { selectFolder: () => Promise } declare global { interface Window { electronAPI: ElectronAPI } } ``` ### 4. Update App Component **src/App.tsx:** **Logic:** - Import UI store - Import EmptyState component - Check if `hasInstances` is false - If false, render EmptyState - If true, render placeholder for instance UI (future) **Folder selection handler:** ```typescript async function handleSelectFolder() { setIsSelectingFolder(true) try { const folder = await window.electronAPI.selectFolder() if (folder) { setSelectedFolder(folder) // TODO: Will trigger instance creation in Task 003 console.log("Selected folder:", folder) } } catch (error) { console.error("Folder selection failed:", error) // TODO: Show error toast (Task 010) } finally { setIsSelectingFolder(false) } } ``` ### 5. Add Keyboard Shortcut **electron/main/menu.ts (new file):** **Create application menu:** - File menu: - New Instance (Cmd/Ctrl+N) - Click: Send 'menu:newInstance' to renderer - Separator - Quit (Cmd/Ctrl+Q) **Platform-specific menu:** - macOS: Include app menu with About, Hide, etc. - Windows/Linux: Standard File menu **Register menu in main.ts:** - Import Menu, buildFromTemplate - Create menu structure - Set as application menu **electron/preload/index.ts additions:** ```typescript electronAPI: { onNewInstance: (callback: () => void) => void } ``` **src/App.tsx additions:** - Listen for 'newInstance' event - Trigger handleSelectFolder when received ### 6. Add Loading State **Button states:** - Default: "Select Folder" - Loading: "Selecting..." with spinner icon - Disabled when isSelectingFolder is true **Spinner component:** - Use lucide-solid Loader2 icon - Add spin animation class - Size: 16px ### 7. Add Validation **Folder validation (in handler):** - Check if folder exists - Check if readable - Check if it's actually a directory - Show appropriate error if invalid **Error messages:** - "Folder does not exist" - "Cannot access folder (permission denied)" - "Please select a directory, not a file" ### 8. Style Refinements **Responsive behavior:** - Works at minimum window size (800x600) - Maintains centering - Text remains readable **Accessibility:** - Button has proper ARIA labels - Keyboard focus visible - Screen reader friendly text **Theme support:** - Test in light mode - Test in dark mode (use prefers-color-scheme) - Icons and text have proper contrast ### 9. Add Helpful Context **Additional helper text:** - "Examples: ~/projects/my-app" - "You can have multiple instances of the same folder" **Icon improvements:** - Use animated folder icon (optional) - Add subtle entrance animation (fade in) ## Testing Checklist **Manual Tests:** 1. Launch app → Empty state appears 2. Click "Select Folder" → Dialog opens 3. Select folder → Path logged to console 4. Cancel dialog → No error, back to empty state 5. Press Cmd/Ctrl+N → Dialog opens 6. Select non-directory → Error shown 7. Select restricted folder → Permission error shown 8. Resize window → Layout stays centered **Edge Cases:** - Very long folder paths (ellipsis) - Special characters in folder name - Folder on network drive - Folder that gets deleted while selected ## Dependencies - **Blocks:** Task 003 (needs folder path to create instance) - **Blocked by:** Task 001 (needs project setup) ## Estimated Time 2-3 hours ## Notes - Keep UI simple and clean - Focus on UX - clear messaging - Don't implement instance creation yet (that's Task 003) - Log selected folder to console for verification - Prepare for state management patterns used in later tasks