Skip to content

Implement MultiFileEditor component for managing multiple files #56

Closed
@nullcoder

Description

@nullcoder

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    featureNew feature implementationpriority: criticalMust be done immediatelyreadyReady to be worked onuiUser interface and components

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions