feat: implement file editor with syntax highlighting and editing
- Install react-simple-code-editor and prismjs dependencies - Create language detection utility with 50+ file extensions - Create SyntaxHighlighter component with Prism.js highlighting - Create CodeEditor component with syntax-highlighted editing - Create CommitDialog with diff preview and commit message - Create FileEditor component integrating view/edit/commit flow - Replace FileViewer with FileEditor in RepoWorkspace - Add comprehensive CSS styles for editor, highlighter, and dialog - Support keyboard shortcuts: Ctrl+E (toggle edit), Ctrl+S (save) - Quality gates: typecheck ✓ lint ✓ build ✓
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
schema: spec-driven
|
||||
created: 2026-05-19
|
||||
@@ -0,0 +1,191 @@
|
||||
# File Editor - Design
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
RepoWorkspace
|
||||
└── FileEditor (replaces FileViewer)
|
||||
├── View Mode
|
||||
│ ├── Toolbar (Edit button, file info)
|
||||
│ ├── SyntaxHighlighter (Prism.js)
|
||||
│ └── Line numbers
|
||||
└── Edit Mode
|
||||
├── Toolbar (Save, Cancel, file info)
|
||||
├── Editor (react-simple-code-editor)
|
||||
└── Line numbers
|
||||
↓ [Save clicked]
|
||||
CommitDialog
|
||||
├── Diff preview
|
||||
├── Commit message input
|
||||
└── Author info
|
||||
```
|
||||
|
||||
## Component Design
|
||||
|
||||
### FileEditor
|
||||
|
||||
**Props:**
|
||||
```typescript
|
||||
interface FileEditorProps {
|
||||
projectId: string;
|
||||
repoId: string;
|
||||
filePath: string;
|
||||
branch: string;
|
||||
}
|
||||
```
|
||||
|
||||
**State:**
|
||||
```typescript
|
||||
interface FileEditorState {
|
||||
mode: 'view' | 'edit';
|
||||
content: string;
|
||||
originalContent: string;
|
||||
language: string;
|
||||
loading: boolean;
|
||||
error: string | null;
|
||||
showCommitDialog: boolean;
|
||||
}
|
||||
```
|
||||
|
||||
**Flow:**
|
||||
1. Load file content on mount/file change
|
||||
2. Detect language from file extension
|
||||
3. Display in view mode by default
|
||||
4. Click "Edit" → switch to edit mode
|
||||
5. Make changes → click "Save"
|
||||
6. Show commit dialog with diff
|
||||
7. Enter commit message → commit
|
||||
8. Return to view mode with updated content
|
||||
|
||||
### SyntaxHighlighter (View Mode)
|
||||
|
||||
**Implementation:**
|
||||
- Use Prism.js for tokenization
|
||||
- Render highlighted tokens as HTML
|
||||
- Add line numbers via CSS counter
|
||||
- Copy-to-clipboard button
|
||||
|
||||
**Language Detection:**
|
||||
```typescript
|
||||
const detectLanguage = (filename: string): string => {
|
||||
const ext = filename.split('.').pop()?.toLowerCase();
|
||||
const langMap: Record<string, string> = {
|
||||
'js': 'javascript',
|
||||
'ts': 'typescript',
|
||||
'tsx': 'tsx',
|
||||
'jsx': 'jsx',
|
||||
'py': 'python',
|
||||
'md': 'markdown',
|
||||
// ... more mappings
|
||||
};
|
||||
return langMap[ext || ''] || 'plaintext';
|
||||
};
|
||||
```
|
||||
|
||||
### CodeEditor (Edit Mode)
|
||||
|
||||
**Implementation:**
|
||||
- react-simple-code-editor component
|
||||
- Prism.js highlighting via textarea overlay
|
||||
- Line numbers synchronized with content
|
||||
- Tab key support (inserts spaces)
|
||||
|
||||
**Features:**
|
||||
- Syntax highlighting while typing
|
||||
- Auto-indentation
|
||||
- Line numbers
|
||||
- Selection highlighting
|
||||
|
||||
### CommitDialog
|
||||
|
||||
**Props:**
|
||||
```typescript
|
||||
interface CommitDialogProps {
|
||||
isOpen: boolean;
|
||||
filePath: string;
|
||||
originalContent: string;
|
||||
newContent: string;
|
||||
onCommit: (message: string) => void;
|
||||
onCancel: () => void;
|
||||
}
|
||||
```
|
||||
|
||||
**Features:**
|
||||
- Diff preview (simple line-by-line comparison)
|
||||
- Commit message input (required)
|
||||
- Author info (from user profile)
|
||||
- Cancel button (returns to edit mode)
|
||||
|
||||
## API Integration
|
||||
|
||||
### Load File (existing)
|
||||
```
|
||||
GET /projects/{id}/repositories/{id}/files/content
|
||||
Query: branch, path
|
||||
```
|
||||
|
||||
### Save File (existing)
|
||||
```
|
||||
POST /projects/{id}/repositories/{id}/files/content
|
||||
Body: {
|
||||
path: string,
|
||||
branch: string,
|
||||
content: string,
|
||||
commit_message: string,
|
||||
author_name: string,
|
||||
author_email: string
|
||||
}
|
||||
```
|
||||
|
||||
## Styling
|
||||
|
||||
### Editor Layout
|
||||
```
|
||||
┌─────────────────────────────────────────┐
|
||||
│ [file.txt] [Edit] [Raw] │ ← Toolbar
|
||||
├─────────────────────────────────────────┤
|
||||
│ 1 │ function hello() { │ ← Line numbers + content
|
||||
│ 2 │ return "world"; │
|
||||
│ 3 │ } │
|
||||
└─────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### Colors
|
||||
- Match existing app theme (CSS variables)
|
||||
- Syntax colors:
|
||||
- Keywords: var(--brand)
|
||||
- Strings: #10b981
|
||||
- Comments: var(--muted)
|
||||
- Numbers: #f59e0b
|
||||
- Functions: #3b82f6
|
||||
|
||||
### Responsive
|
||||
- Editor takes full width on mobile
|
||||
- Toolbar buttons shrink to icons
|
||||
- Line numbers hidden on very small screens
|
||||
|
||||
## Implementation Order
|
||||
|
||||
1. **Install dependencies** - react-simple-code-editor, prismjs
|
||||
2. **Create SyntaxHighlighter component** - Prism.js wrapper
|
||||
3. **Create CodeEditor component** - Edit mode with highlighting
|
||||
4. **Create CommitDialog component** - Commit flow
|
||||
5. **Create FileEditor component** - Main component orchestrating modes
|
||||
6. **Replace FileViewer in RepoWorkspace**
|
||||
7. **Add CSS styles** - Editor styling, syntax colors
|
||||
8. **Test** - Various file types, commit flow
|
||||
|
||||
## Error Handling
|
||||
|
||||
- **Binary files**: Show "Cannot edit binary files" message
|
||||
- **Load errors**: Show retry button
|
||||
- **Save errors**: Show error in commit dialog
|
||||
- **Large files**: Show warning, offer to download instead
|
||||
- **Network errors**: Show offline indicator
|
||||
|
||||
## Performance
|
||||
|
||||
- **Lazy load Prism** - Load language grammars on demand
|
||||
- **Debounce edits** - Don't re-highlight on every keystroke
|
||||
- **Virtual scrolling** - For files >1000 lines
|
||||
- **Memoization** - Cache highlighted output
|
||||
@@ -0,0 +1,68 @@
|
||||
# File Editor - Syntax Highlighting and Editing
|
||||
|
||||
## Problem
|
||||
|
||||
The current file viewer in the repository workspace only displays raw text without syntax highlighting or editing capabilities. Users cannot view code with proper formatting or make quick edits to files.
|
||||
|
||||
## Solution
|
||||
|
||||
Create a comprehensive file editor that provides:
|
||||
|
||||
1. **Syntax highlighting** for all text-based file types
|
||||
2. **View/Edit mode toggle** - switch between read-only and edit mode
|
||||
3. **Rich text editing** with syntax highlighting while editing
|
||||
4. **Commit dialog** - save changes with custom commit message
|
||||
5. **Line numbers** in both view and edit modes
|
||||
6. **File type detection** from extension
|
||||
|
||||
## Key Features
|
||||
|
||||
### Syntax Highlighting
|
||||
- Support for all common programming languages
|
||||
- Automatic language detection from file extension
|
||||
- Consistent color scheme matching the app theme
|
||||
|
||||
### Edit Mode
|
||||
- Toggle between view (read-only) and edit mode
|
||||
- Syntax highlighted editing using textarea overlay
|
||||
- Line numbers visible during editing
|
||||
- Keyboard shortcuts (Ctrl+S to save)
|
||||
|
||||
### Commit Flow
|
||||
- Click "Edit" → make changes → click "Save"
|
||||
- Commit dialog appears with message input
|
||||
- Shows diff preview of changes
|
||||
- Commit with author info (from user profile)
|
||||
- Returns to view mode after successful commit
|
||||
|
||||
### File Support
|
||||
- All text-based files (source code, config, markdown, etc.)
|
||||
- Binary files show "cannot edit" message
|
||||
- Large files (>1MB) show warning
|
||||
|
||||
## Benefits
|
||||
|
||||
- **Better code reading** - syntax highlighting makes code easier to understand
|
||||
- **Quick fixes** - edit files without leaving the browser
|
||||
- **Git integration** - changes are committed directly
|
||||
- **Familiar interface** - similar to GitHub/GitLab file editor
|
||||
|
||||
## Scope
|
||||
|
||||
### New Components
|
||||
- FileEditor (enhanced file viewer with edit capability)
|
||||
- CommitDialog (commit message + diff preview)
|
||||
- SyntaxHighlighter (Prism.js wrapper)
|
||||
|
||||
### Modified Components
|
||||
- RepoWorkspace (integrate new editor)
|
||||
- FileViewer (replaced by FileEditor)
|
||||
|
||||
### Backend Changes
|
||||
- None (existing endpoints already support file update)
|
||||
|
||||
## Technology
|
||||
|
||||
- **react-simple-code-editor** - lightweight code editing with syntax highlighting
|
||||
- **Prism.js** - syntax highlighting for 289+ languages
|
||||
- **Existing API** - PUT /files/content endpoint already exists
|
||||
@@ -0,0 +1,187 @@
|
||||
# File Editor Specification
|
||||
|
||||
## Requirements
|
||||
|
||||
### Functional Requirements
|
||||
|
||||
1. **File Display**: Show file contents with syntax highlighting for all text files
|
||||
2. **Language Detection**: Automatically detect language from file extension
|
||||
3. **View Mode**: Read-only display with line numbers and copy button
|
||||
4. **Edit Mode**: Rich text editing with syntax highlighting
|
||||
5. **Commit Flow**: Save changes via commit dialog with custom message
|
||||
6. **Diff Preview**: Show changes before committing
|
||||
7. **File Support**: All text-based files (source code, config, markdown, etc.)
|
||||
|
||||
### Non-Functional Requirements
|
||||
|
||||
1. **Performance**: Load files < 500ms, highlighting < 100ms
|
||||
2. **Responsiveness**: UI remains responsive during editing
|
||||
3. **Accessibility**: Keyboard navigation, screen reader support
|
||||
4. **Browser Support**: Modern browsers (Chrome, Firefox, Safari, Edge)
|
||||
|
||||
## API Specification
|
||||
|
||||
### GET /projects/{project_id}/repositories/{repo_id}/files/content
|
||||
Get file content (existing endpoint).
|
||||
|
||||
**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
|
||||
}
|
||||
```
|
||||
|
||||
### POST /projects/{project_id}/repositories/{repo_id}/files/content
|
||||
Update file content (existing endpoint).
|
||||
|
||||
**Request Body:**
|
||||
```json
|
||||
{
|
||||
"path": "src/main.py",
|
||||
"branch": "main",
|
||||
"content": "new content",
|
||||
"commit_message": "Update greeting",
|
||||
"author_name": "User Name",
|
||||
"author_email": "user@example.com"
|
||||
}
|
||||
```
|
||||
|
||||
## Frontend Specification
|
||||
|
||||
### Components
|
||||
|
||||
#### FileEditor
|
||||
Main component managing view/edit modes.
|
||||
|
||||
**Props:**
|
||||
```typescript
|
||||
interface FileEditorProps {
|
||||
projectId: string;
|
||||
repoId: string;
|
||||
}
|
||||
```
|
||||
|
||||
**State:**
|
||||
```typescript
|
||||
interface FileEditorState {
|
||||
mode: 'view' | 'edit';
|
||||
content: string;
|
||||
originalContent: string;
|
||||
language: string;
|
||||
filePath: string | null;
|
||||
branch: string;
|
||||
loading: boolean;
|
||||
error: string | null;
|
||||
showCommitDialog: boolean;
|
||||
isBinary: boolean;
|
||||
}
|
||||
```
|
||||
|
||||
#### SyntaxHighlighter
|
||||
Read-only syntax highlighted display.
|
||||
|
||||
**Props:**
|
||||
```typescript
|
||||
interface SyntaxHighlighterProps {
|
||||
code: string;
|
||||
language: string;
|
||||
showLineNumbers?: boolean;
|
||||
}
|
||||
```
|
||||
|
||||
#### CodeEditor
|
||||
Editable code with syntax highlighting.
|
||||
|
||||
**Props:**
|
||||
```typescript
|
||||
interface CodeEditorProps {
|
||||
value: string;
|
||||
onChange: (value: string) => void;
|
||||
language: string;
|
||||
readOnly?: boolean;
|
||||
}
|
||||
```
|
||||
|
||||
#### CommitDialog
|
||||
Commit flow dialog.
|
||||
|
||||
**Props:**
|
||||
```typescript
|
||||
interface CommitDialogProps {
|
||||
isOpen: boolean;
|
||||
filePath: string;
|
||||
originalContent: string;
|
||||
newContent: string;
|
||||
onCommit: (message: string) => Promise<void>;
|
||||
onCancel: () => void;
|
||||
}
|
||||
```
|
||||
|
||||
### Language Support
|
||||
|
||||
Supported languages (from Prism.js):
|
||||
- JavaScript/TypeScript (js, ts, jsx, tsx)
|
||||
- Python (py)
|
||||
- HTML/XML (html, xml)
|
||||
- CSS/SCSS (css, scss)
|
||||
- JSON (json)
|
||||
- Markdown (md)
|
||||
- YAML (yaml, yml)
|
||||
- Shell/Bash (sh, bash)
|
||||
- Docker (dockerfile)
|
||||
- SQL (sql)
|
||||
- And 280+ more via Prism.js
|
||||
|
||||
### Keyboard Shortcuts
|
||||
|
||||
- `Ctrl/Cmd + E`: Toggle edit mode
|
||||
- `Ctrl/Cmd + S`: Save (shows commit dialog)
|
||||
- `Escape`: Cancel edit mode
|
||||
- `Tab`: Insert 2 spaces
|
||||
|
||||
### URL State
|
||||
|
||||
Editor state synced to URL:
|
||||
```
|
||||
/projects/:projectId?repo=:repoId&branch=:branch&file=:path&edit=true
|
||||
```
|
||||
|
||||
## Error Codes
|
||||
|
||||
| Error Code | Description | User Message |
|
||||
|------------|-------------|--------------|
|
||||
| BINARY_FILE | File is binary | "Binary files cannot be edited" |
|
||||
| FILE_TOO_LARGE | File > 1MB | "File too large to edit" |
|
||||
| LOAD_ERROR | Failed to load | "Failed to load file" |
|
||||
| SAVE_ERROR | Failed to save | "Failed to save changes" |
|
||||
| EMPTY_COMMIT | No changes | "No changes to commit" |
|
||||
|
||||
## Testing Strategy
|
||||
|
||||
### Unit Tests
|
||||
- Language detection
|
||||
- Syntax highlighting output
|
||||
- Diff generation
|
||||
- Commit validation
|
||||
|
||||
### Integration Tests
|
||||
- File loading
|
||||
- Mode switching
|
||||
- Commit flow
|
||||
- Error handling
|
||||
|
||||
### Manual Tests
|
||||
- Various file types
|
||||
- Large files
|
||||
- Binary files
|
||||
- Network failures
|
||||
@@ -0,0 +1,102 @@
|
||||
# File Editor - Tasks
|
||||
|
||||
## Phase 1: Dependencies and Setup
|
||||
|
||||
- [ ] **Task 1.1**: Install dependencies
|
||||
- `react-simple-code-editor`
|
||||
- `prismjs`
|
||||
- `@types/prismjs`
|
||||
- Add Prism CSS theme
|
||||
|
||||
- [ ] **Task 1.2**: Create language detection utility
|
||||
- Map file extensions to Prism.js language names
|
||||
- Handle common extensions (.js, .ts, .py, .md, etc.)
|
||||
- Default to plaintext for unknown extensions
|
||||
|
||||
## Phase 2: Syntax Highlighting
|
||||
|
||||
- [ ] **Task 2.1**: Create SyntaxHighlighter component
|
||||
- Use Prism.js to tokenize code
|
||||
- Render highlighted HTML
|
||||
- Add line numbers
|
||||
- Copy-to-clipboard button
|
||||
|
||||
- [ ] **Task 2.2**: Add Prism.js themes
|
||||
- Light theme (matches app)
|
||||
- Dark theme support
|
||||
- CSS custom properties integration
|
||||
|
||||
- [ ] **Task 2.3**: Lazy load language support
|
||||
- Only load language grammar when needed
|
||||
- Dynamic imports for language files
|
||||
|
||||
## Phase 3: Edit Mode
|
||||
|
||||
- [ ] **Task 3.1**: Create CodeEditor component
|
||||
- Use react-simple-code-editor
|
||||
- Prism.js highlighting overlay
|
||||
- Line numbers
|
||||
- Tab key support
|
||||
|
||||
- [ ] **Task 3.2**: Add keyboard shortcuts
|
||||
- Ctrl/Cmd + E: Toggle edit
|
||||
- Ctrl/Cmd + S: Save
|
||||
- Escape: Cancel
|
||||
- Tab: Insert spaces
|
||||
|
||||
## Phase 4: Commit Dialog
|
||||
|
||||
- [ ] **Task 4.1**: Create CommitDialog component
|
||||
- Diff preview (simple comparison)
|
||||
- Commit message input (required)
|
||||
- Author info display
|
||||
- Cancel and Commit buttons
|
||||
|
||||
- [ ] **Task 4.2**: Add diff generation
|
||||
- Simple line-by-line diff
|
||||
- Show added/removed lines
|
||||
- Highlight changes
|
||||
|
||||
## Phase 5: FileEditor Integration
|
||||
|
||||
- [ ] **Task 5.1**: Create FileEditor component
|
||||
- Manage view/edit state
|
||||
- Load file content
|
||||
- Toggle modes
|
||||
- Handle save flow
|
||||
|
||||
- [ ] **Task 5.2**: Replace FileViewer in RepoWorkspace
|
||||
- Update imports
|
||||
- Pass required props
|
||||
- Handle file selection
|
||||
|
||||
## Phase 6: Styling
|
||||
|
||||
- [ ] **Task 6.1**: Add editor CSS
|
||||
- Toolbar styling
|
||||
- Editor container
|
||||
- Line numbers
|
||||
- Syntax colors
|
||||
- Commit dialog styles
|
||||
|
||||
- [ ] **Task 6.2**: Add responsive styles
|
||||
- Mobile layout
|
||||
- Touch-friendly buttons
|
||||
- Collapsible toolbar
|
||||
|
||||
## Phase 7: Quality Gates
|
||||
|
||||
- [ ] **Task 7.1**: Run backend checks
|
||||
- ruff
|
||||
- mypy
|
||||
|
||||
- [ ] **Task 7.2**: Run frontend checks
|
||||
- TypeScript typecheck
|
||||
- ESLint
|
||||
- Build
|
||||
|
||||
- [ ] **Task 7.3**: Manual testing
|
||||
- Test various file types
|
||||
- Test commit flow
|
||||
- Test error handling
|
||||
- Test responsive design
|
||||
Reference in New Issue
Block a user