Closed
Description
Description
Implement the MultiFileEditor component that manages multiple FileEditor instances in a vertical layout, following GitHub Gist's UX pattern.
Requirements
- Vertical layout: All files displayed vertically on one page (NO TABS)
- Each file editor is stacked below the previous one
- Add new file button at the bottom of all files
- Auto-scroll to newly added files with smooth animation
- Delete file functionality with inline remove button
- Minimum 1 file, maximum 20 files
- Generate default filenames (file1.txt, file2.txt, etc.)
- Prevent duplicate filenames across all files
- Total size validation (5MB limit for entire gist)
- Individual file size validation (500KB per file)
- Responsive design for mobile
- Proper focus management when adding/removing files
Acceptance Criteria
- Files are displayed vertically, all visible at once
- Can add new files up to 20 file limit
- New files appear at the bottom with auto-scroll
- Focus moves to new file's filename input
- Can delete files (except when only 1 remains)
- Remove button hidden when only 1 file exists
- Confirmation dialog for non-empty file deletion
- Default filenames are auto-generated sequentially
- Duplicate filenames are prevented with error message
- Total size limit is enforced (5MB) with clear indicator
- Individual file warnings at 400KB, errors at 500KB
- Component is fully typed with TypeScript
- Works well on mobile devices
- Keyboard shortcuts work (Ctrl+Enter for new file)
- Accessible with screen readers
State Interface
interface FileData {
id: string; // nanoid for React keys
name: string;
content: string;
language?: string;
}
interface MultiFileEditorProps {
initialFiles?: FileData[];
onChange: (files: FileData[]) => void;
readOnly?: boolean;
maxFiles?: number; // Default 20
maxTotalSize?: number; // Default 5MB
maxFileSize?: number; // Default 500KB
}
UI Layout
┌─ MultiFileEditor Container ─────────────┐
│ │
│ Total: 3 files, 126 KB / 5 MB │
│ │
│ [FileEditor 1] │
│ filename.js | JavaScript ▼ | ✕ │
│ // code... │
│ │
│ [FileEditor 2] │
│ styles.css | CSS ▼ | ✕ │
│ /* code... */ │
│ │
│ [FileEditor 3] │
│ data.json | JSON ▼ │
│ { "data": "..." } │
│ │
│ ┌─────────────────────────────────────┐ │
│ │ ➕ Add another file │ │
│ └─────────────────────────────────────┘ │
└─────────────────────────────────────────┘
File Management Logic
// Generate unique default filename
function generateFilename(existingFiles: FileData[]): string {
let counter = 1;
let filename = `file${counter}.txt`;
while (existingFiles.some(f => f.name === filename)) {
counter++;
filename = `file${counter}.txt`;
}
return filename;
}
// Calculate total size
function calculateTotalSize(files: FileData[]): number {
return files.reduce((sum, file) =>
sum + new Blob([file.content]).size, 0
);
}
Technical Notes
- Follow the design in docs/MULTI_FILE_EDITOR_DESIGN.md
- Use array state management for files
- Each file needs unique ID (use nanoid)
- Implement proper memoization to prevent unnecessary re-renders
- Use React.memo for FileEditor components
- Consider virtualization for many files (future enhancement)
- File operations should update state immutably
- Use requestAnimationFrame for smooth scrolling
- Debounce size calculations for performance
Keyboard Shortcuts
- Ctrl/Cmd + Enter: Add new file
- Delete/Backspace: Remove file (when focused on remove button)
- Tab: Navigate between file editors
- Shift + Tab: Navigate backwards
Related
- Depends on Implement FileEditor component for single file editing #55 (FileEditor component)
- Uses design from docs/MULTI_FILE_EDITOR_DESIGN.md
- Part of Phase 4 UI Components
- Core component for create page functionality