Files
NomadArch/tasks/done/002-empty-state-ui.md
Gemini AI 157449a9ad restore: recover deleted documentation, CI/CD, and infrastructure files
Restored from origin/main (b4663fb):
- .github/ workflows and issue templates
- .gitignore (proper exclusions)
- .opencode/agent/web_developer.md
- AGENTS.md, BUILD.md, PROGRESS.md
- dev-docs/ (9 architecture/implementation docs)
- docs/screenshots/ (4 UI screenshots)
- images/ (CodeNomad icons)
- package-lock.json (dependency lockfile)
- tasks/ (25+ project task files)

Also restored original source files that were modified:
- packages/ui/src/App.tsx
- packages/ui/src/lib/logger.ts
- packages/ui/src/stores/instances.ts
- packages/server/src/server/routes/workspaces.ts
- packages/server/src/workspaces/manager.ts
- packages/server/src/workspaces/runtime.ts
- packages/server/package.json

Kept new additions:
- Install-*.bat/.sh (enhanced installers)
- Launch-*.bat/.sh (new launchers)
- README.md (SEO optimized with GLM 4.7)
2025-12-23 13:03:48 +04:00

281 lines
5.9 KiB
Markdown

# 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<string | null>
}
```
**Type definitions:**
```typescript
interface ElectronAPI {
selectFolder: () => Promise<string | null>
}
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