Skip to content

docs: design multi-file editor with GitHub Gist-style UX #51

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 6, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
214 changes: 214 additions & 0 deletions docs/MULTI_FILE_EDITOR_DESIGN.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
# Multi-File Editor Design

## Overview

The GhostPaste multi-file editor follows GitHub Gist's UX pattern where files are displayed vertically in a single page rather than using tabs. This provides better visibility of all files and a more intuitive editing experience.

## Design Principles

1. **Vertical Layout**: All files visible at once, no hidden content
2. **Inline Editing**: Each file is a self-contained unit with all controls
3. **Progressive Disclosure**: Start with one file, add more as needed
4. **Mobile-First**: Works well on all screen sizes
5. **Accessibility**: Keyboard navigable, screen reader friendly

## Component Architecture

### 1. MultiFileEditor (Container)

The main container that manages the state of all files.

**State Management:**

```typescript
interface FileData {
id: string; // Unique identifier (nanoid)
name: string; // Filename with extension
content: string; // File content
language?: string; // Detected or selected language
}

const [files, setFiles] = useState<FileData[]>([
{ id: nanoid(), name: "", content: "", language: "text" },
]);
```

**Key Features:**

- Manages array of files
- Handles add/remove operations
- Validates constraints (1-20 files)
- Prevents duplicate filenames
- Generates default filenames

### 2. FileEditor (Individual File)

Each file is rendered as a FileEditor component.

**Layout:**

```
┌─────────────────────────────────────────────────────────┐
│ [filename.js ] [JavaScript ▼] [✕] │
├─────────────────────────────────────────────────────────┤
│ │
│ // Your code here... │
│ │
│ │
└─────────────────────────────────────────────────────────┘
```

**Components:**

- Filename input (with validation)
- Language selector dropdown
- Remove button (conditional)
- CodeMirror editor

### 3. AddFileButton

Simple button component at the bottom of all files.

```
┌─────────────────────────────────────┐
│ ➕ Add another file │
└─────────────────────────────────────┘
```

## User Interactions

### Adding Files

1. User clicks "Add another file"
2. New FileEditor appended to bottom
3. Page auto-scrolls to new file
4. Focus placed on filename input
5. Default filename generated (e.g., "file2.txt")

### Removing Files

1. User clicks ✕ button on file
2. Confirmation if file has content
3. File removed from list
4. If last file, remove button hidden

### Filename Changes

1. User types in filename field
2. Real-time validation:
- No empty names
- No duplicate names
- Valid characters only
- Max 255 characters
3. Language auto-detected from extension
4. Error shown below input if invalid

### Language Selection

1. Dropdown shows supported languages
2. Selection updates syntax highlighting
3. Manual selection overrides auto-detection

## State Flow

```
User Action State Update UI Update
─────────── ──────────── ─────────
Add File → files.push(newFile) → Render new FileEditor
Auto-scroll to bottom

Remove File → files.filter(...) → Remove FileEditor
Update remove buttons

Edit Filename → file.name = value → Validate & show errors
file.language = auto Update syntax highlight

Edit Content → file.content = value → Update editor content

Select Lang → file.language = sel → Update syntax highlight
```

## Responsive Behavior

### Desktop (>768px)

- Full-width editors
- Side-by-side filename and language inputs
- Comfortable spacing

### Mobile (<768px)

- Stacked filename and language inputs
- Full-width editors
- Touch-friendly button sizes
- Reduced vertical spacing

## Performance Considerations

1. **Lazy Loading**: CodeMirror instances created on-demand
2. **Debouncing**: Filename validation debounced
3. **Virtual Scrolling**: For many files (future enhancement)
4. **Memoization**: FileEditor components memoized

## Accessibility

1. **Keyboard Navigation**:

- Tab through all inputs
- Ctrl+Enter to add new file
- Delete key to remove (with focus)

2. **Screen Readers**:

- Proper ARIA labels
- Announce file operations
- Describe editor state

3. **Focus Management**:
- Focus new file on add
- Focus previous file on remove
- Trap focus in dialogs

## Error Handling

1. **Filename Errors**:

- "Filename is required"
- "Filename already exists"
- "Invalid characters in filename"

2. **File Limit**:

- Disable add button at 20 files
- Show tooltip explaining limit

3. **Content Size**:
- Warn at 400KB per file
- Error at 500KB per file
- Show total size indicator

## Future Enhancements

1. **Drag and Drop Reordering**
2. **File Templates** (e.g., "Add HTML/CSS/JS set")
3. **Import from URL**
4. **Syntax Validation**
5. **Collaborative Editing**

## Implementation Checklist

- [ ] Create FileEditor component
- [ ] Create MultiFileEditor container
- [ ] Implement file state management
- [ ] Add filename validation
- [ ] Implement language auto-detection
- [ ] Add remove functionality with confirmation
- [ ] Create AddFileButton component
- [ ] Implement auto-scroll on add
- [ ] Add keyboard shortcuts
- [ ] Implement responsive design
- [ ] Add accessibility features
- [ ] Create unit tests
- [ ] Add integration tests
- [ ] Performance optimization
- [ ] Documentation
36 changes: 34 additions & 2 deletions docs/SPEC.md
Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,11 @@ img-src 'self' data: https:;
### Creating a Gist

1. User enters code in editor
2. Adds files via tabs
2. Adds multiple files (GitHub Gist-style):
- Initial file shown by default
- Click "Add file" button to append new file editor below
- Each file has inline remove button (except when only one file)
- No tabs - all files visible vertically
3. Sets preferences:
- Description (optional)
- Expiration (optional)
Expand All @@ -327,15 +331,43 @@ img-src 'self' data: https:;
3. Fetch and decrypt metadata
4. Fetch and decrypt content
5. Display in read-only editor
6. Multiple files shown vertically (same as creation)

### Editing a Gist

1. Click "Edit" button
2. Enter PIN if required
3. Make changes
3. Make changes:
- Edit existing files inline
- Add new files at the bottom
- Remove files (minimum 1 required)
4. Click "Update"
5. New version created

### Multiple File Editor UX

The file editor follows GitHub Gist's design pattern:

- **Vertical Layout:** All files displayed vertically in a single scrollable page
- **File Components:** Each file has:
- Filename input field (with extension auto-detection)
- Language selector dropdown
- Remove button (✕) on the right (hidden when only 1 file)
- Full-height code editor below
- **Add File Button:** At the bottom of all files
- Adds a new empty file editor
- Auto-scrolls to the new file
- Default filename: `file1.txt`, `file2.txt`, etc.
- **File Management:**
- Minimum 1 file required
- Maximum 20 files allowed
- Real-time validation of filenames
- Duplicate filenames prevented
- **Responsive Design:**
- Stack vertically on all screen sizes
- Consistent spacing between files
- Touch-friendly controls on mobile

---

## 🚀 Implementation Notes
Expand Down
9 changes: 8 additions & 1 deletion docs/TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,8 +107,10 @@ This document tracks the implementation progress of GhostPaste. Check off tasks

### Form Components

- [ ] Create FileTab component for multi-file support
- [ ] Create FileEditor component (single file with name, language, editor)
- [ ] Create MultiFileEditor component (manages multiple FileEditor instances)
- [ ] Create CodeEditor component (CodeMirror wrapper)
- [ ] Create AddFileButton component
- [ ] Create ExpirySelector component
- [ ] Create PINInput component
- [ ] Create ShareDialog component with copy functionality
Expand All @@ -128,6 +130,11 @@ This document tracks the implementation progress of GhostPaste. Check off tasks
- [ ] Create keyboard shortcuts
- [ ] Add copy-to-clipboard functionality
- [ ] Implement responsive design
- [ ] Add file editor auto-scroll on add
- [ ] Implement filename auto-generation
- [ ] Add language auto-detection from filename
- [ ] Prevent duplicate filenames
- [ ] Add file reordering (drag and drop - stretch goal)

## 🔌 Phase 5: API Development

Expand Down