# Repository Workspace Specification ## Requirements ### Functional Requirements 1. **Default Project View**: Clicking a project opens the repository workspace 2. **File Browser**: Tree view of repository files and directories 3. **Branch Navigation**: Switch between branches to view different states 4. **File Viewer**: View file contents with syntax highlighting 5. **Quick Edit**: Make small changes and commit them 6. **Repository Switching**: Switch between repositories in a project 7. **Breadcrumb Navigation**: Show current file path with clickable segments ### Non-Functional Requirements 1. **Performance**: File tree loads in < 1 second 2. **Responsiveness**: UI remains responsive during git operations 3. **Usability**: Familiar interface similar to GitHub/GitLab 4. **Accessibility**: Keyboard navigation, screen reader support ## API Specification ### GET /projects/{project_id}/repositories/{repo_id}/files List files in a directory. **Query Parameters:** - `branch` (optional): Branch name, defaults to repository default branch - `path` (optional): Directory path, defaults to root **Response 200:** ```json { "path": "src", "branch": "main", "entries": [ { "name": "components", "type": "directory", "path": "src/components", "mode": "040000" }, { "name": "main.py", "type": "file", "path": "src/main.py", "size": 1234, "mode": "100644", "last_commit": { "hash": "abc123", "message": "Initial commit", "author": "John Doe", "date": "2024-01-01T00:00:00Z" } } ] } ``` **Response 404:** Branch or path not found ### GET /projects/{project_id}/repositories/{repo_id}/files/content Get file content. **Query Parameters:** - `branch` (required): Branch name - `path` (required): File path **Response 200:** ```json { "path": "src/main.py", "branch": "main", "content": "function hello() {\n return 'world';\n}", "size": 42, "encoding": "utf-8", "language": "python", "is_binary": false, "last_commit": { "hash": "abc123", "message": "Initial commit", "author": "John Doe", "date": "2024-01-01T00:00:00Z" } } ``` **Response 404:** File not found ### GET /projects/{project_id}/repositories/{repo_id}/branches List branches. **Response 200:** ```json { "branches": [ { "name": "main", "is_default": true, "last_commit": { "hash": "abc123", "message": "Initial commit", "date": "2024-01-01T00:00:00Z" } } ], "default_branch": "main" } ``` ### POST /projects/{project_id}/repositories/{repo_id}/files/content Update file content. **Request Body:** ```json { "path": "src/main.py", "branch": "main", "content": "new content", "commit_message": "Update file", "author_name": "User", "author_email": "user@example.com" } ``` **Response 200:** ```json { "commit_hash": "def789", "message": "Update file", "branch": "main" } ``` ## Data Model ### FileTreeEntry ```typescript interface FileTreeEntry { name: string; type: 'file' | 'directory'; path: string; size?: number; mode?: string; last_commit?: { hash: string; message: string; author: string; date: string; }; } ``` ### Branch ```typescript interface Branch { name: string; is_default: boolean; last_commit?: { hash: string; message: string; date: string; }; } ``` ### FileContent ```typescript interface FileContent { path: string; branch: string; content: string; size: number; encoding: string; language: string | null; is_binary: boolean; last_commit?: { hash: string; message: string; author: string; date: string; }; } ``` ## Frontend Specification ### URL Structure ``` /projects/:projectId → Default view (first repo, default branch) /projects/:projectId?repo=:repoId → Specific repo /projects/:projectId?repo=:repoId&branch=:branch&path=:path → Specific file ``` ### Components **RepoWorkspace** - Layout: Sidebar (250px) + Main Content (flex) - State: manages repo, branch, path, file selections - Effects: sync URL params, fetch data **FileTree** - Props: entries, activePath, onFileClick, onDirectoryToggle - Recursive rendering for nested directories - Expand/collapse state per directory **FileViewer** - Props: content, language, path, isEditing, onEdit - View mode: preformatted text with syntax highlighting - Edit mode: textarea with save/cancel **RepoSelector** - Props: repositories, selectedRepoId, onSelect - Dropdown with repo names **BranchSelector** - Props: branches, selectedBranch, onSelect - Dropdown with branch names, default branch marked ### State Management Use React state with URL synchronization: ```typescript const [searchParams, setSearchParams] = useSearchParams(); const repoId = searchParams.get('repo'); const branch = searchParams.get('branch'); const path = searchParams.get('path'); ``` ## Error Codes | Error Code | Description | User Message | |------------|-------------|--------------| | REPO_NOT_FOUND | Repository doesn't exist | "Repository not found" | | BRANCH_NOT_FOUND | Branch doesn't exist | "Branch not found, using default" | | FILE_NOT_FOUND | File path doesn't exist | "File not found" | | BINARY_FILE | File is binary | "Cannot display binary file" | | PERMISSION_DENIED | No access to file | "Permission denied" | ## Testing Strategy ### Backend Tests - Test file listing for various paths - Test file content retrieval - Test branch listing - Test file update/commit - Test error cases (missing files, invalid branches) ### Frontend Tests - Test file tree rendering - Test file viewer display - Test branch switching - Test repo switching - Test edit mode - Test URL state sync ### Integration Tests - End-to-end: Click project → browse files → view content → switch branch