Files
headquarter/openspec/changes/archive/2026-05-19-repo-workspace/specs/spec.md
T
Fusion 875594d66d feat: add commit panel and file status indicators to repo workspace
- 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.
2026-05-19 15:00:25 +02:00

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