875594d66d
- Add CommitPanel component for viewing changed files and committing - Show file status indicators (M/A/D/?) in file tree - Integrate git status with workspace for real-time updates - Add CSS styles for commit panel and status badges Part of git-control change implementation.
258 lines
5.8 KiB
Markdown
258 lines
5.8 KiB
Markdown
# 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
|