Skip to content

Commit 90dca79

Browse files
nullcoderClaude
andauthored
docs: update design for GitHub Gist-style multi-file editor (#51)
- Update SPEC.md to describe vertical file layout instead of tabs - Add detailed Multiple File Editor UX section - Update TODO.md with new component breakdown - Create comprehensive MULTI_FILE_EDITOR_DESIGN.md - Focus on inline editing with all files visible - Similar to GitHub Gist's UX pattern 🤖 Generated with Claude Code Co-authored-by: Claude <claude@ghostpaste.dev>
1 parent b100ca1 commit 90dca79

File tree

3 files changed

+256
-3
lines changed

3 files changed

+256
-3
lines changed

docs/MULTI_FILE_EDITOR_DESIGN.md

Lines changed: 214 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
# Multi-File Editor Design
2+
3+
## Overview
4+
5+
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.
6+
7+
## Design Principles
8+
9+
1. **Vertical Layout**: All files visible at once, no hidden content
10+
2. **Inline Editing**: Each file is a self-contained unit with all controls
11+
3. **Progressive Disclosure**: Start with one file, add more as needed
12+
4. **Mobile-First**: Works well on all screen sizes
13+
5. **Accessibility**: Keyboard navigable, screen reader friendly
14+
15+
## Component Architecture
16+
17+
### 1. MultiFileEditor (Container)
18+
19+
The main container that manages the state of all files.
20+
21+
**State Management:**
22+
23+
```typescript
24+
interface FileData {
25+
id: string; // Unique identifier (nanoid)
26+
name: string; // Filename with extension
27+
content: string; // File content
28+
language?: string; // Detected or selected language
29+
}
30+
31+
const [files, setFiles] = useState<FileData[]>([
32+
{ id: nanoid(), name: "", content: "", language: "text" },
33+
]);
34+
```
35+
36+
**Key Features:**
37+
38+
- Manages array of files
39+
- Handles add/remove operations
40+
- Validates constraints (1-20 files)
41+
- Prevents duplicate filenames
42+
- Generates default filenames
43+
44+
### 2. FileEditor (Individual File)
45+
46+
Each file is rendered as a FileEditor component.
47+
48+
**Layout:**
49+
50+
```
51+
┌─────────────────────────────────────────────────────────┐
52+
│ [filename.js ] [JavaScript ▼] [✕] │
53+
├─────────────────────────────────────────────────────────┤
54+
│ │
55+
│ // Your code here... │
56+
│ │
57+
│ │
58+
└─────────────────────────────────────────────────────────┘
59+
```
60+
61+
**Components:**
62+
63+
- Filename input (with validation)
64+
- Language selector dropdown
65+
- Remove button (conditional)
66+
- CodeMirror editor
67+
68+
### 3. AddFileButton
69+
70+
Simple button component at the bottom of all files.
71+
72+
```
73+
┌─────────────────────────────────────┐
74+
│ ➕ Add another file │
75+
└─────────────────────────────────────┘
76+
```
77+
78+
## User Interactions
79+
80+
### Adding Files
81+
82+
1. User clicks "Add another file"
83+
2. New FileEditor appended to bottom
84+
3. Page auto-scrolls to new file
85+
4. Focus placed on filename input
86+
5. Default filename generated (e.g., "file2.txt")
87+
88+
### Removing Files
89+
90+
1. User clicks ✕ button on file
91+
2. Confirmation if file has content
92+
3. File removed from list
93+
4. If last file, remove button hidden
94+
95+
### Filename Changes
96+
97+
1. User types in filename field
98+
2. Real-time validation:
99+
- No empty names
100+
- No duplicate names
101+
- Valid characters only
102+
- Max 255 characters
103+
3. Language auto-detected from extension
104+
4. Error shown below input if invalid
105+
106+
### Language Selection
107+
108+
1. Dropdown shows supported languages
109+
2. Selection updates syntax highlighting
110+
3. Manual selection overrides auto-detection
111+
112+
## State Flow
113+
114+
```
115+
User Action State Update UI Update
116+
─────────── ──────────── ─────────
117+
Add File → files.push(newFile) → Render new FileEditor
118+
Auto-scroll to bottom
119+
120+
Remove File → files.filter(...) → Remove FileEditor
121+
Update remove buttons
122+
123+
Edit Filename → file.name = value → Validate & show errors
124+
file.language = auto Update syntax highlight
125+
126+
Edit Content → file.content = value → Update editor content
127+
128+
Select Lang → file.language = sel → Update syntax highlight
129+
```
130+
131+
## Responsive Behavior
132+
133+
### Desktop (>768px)
134+
135+
- Full-width editors
136+
- Side-by-side filename and language inputs
137+
- Comfortable spacing
138+
139+
### Mobile (<768px)
140+
141+
- Stacked filename and language inputs
142+
- Full-width editors
143+
- Touch-friendly button sizes
144+
- Reduced vertical spacing
145+
146+
## Performance Considerations
147+
148+
1. **Lazy Loading**: CodeMirror instances created on-demand
149+
2. **Debouncing**: Filename validation debounced
150+
3. **Virtual Scrolling**: For many files (future enhancement)
151+
4. **Memoization**: FileEditor components memoized
152+
153+
## Accessibility
154+
155+
1. **Keyboard Navigation**:
156+
157+
- Tab through all inputs
158+
- Ctrl+Enter to add new file
159+
- Delete key to remove (with focus)
160+
161+
2. **Screen Readers**:
162+
163+
- Proper ARIA labels
164+
- Announce file operations
165+
- Describe editor state
166+
167+
3. **Focus Management**:
168+
- Focus new file on add
169+
- Focus previous file on remove
170+
- Trap focus in dialogs
171+
172+
## Error Handling
173+
174+
1. **Filename Errors**:
175+
176+
- "Filename is required"
177+
- "Filename already exists"
178+
- "Invalid characters in filename"
179+
180+
2. **File Limit**:
181+
182+
- Disable add button at 20 files
183+
- Show tooltip explaining limit
184+
185+
3. **Content Size**:
186+
- Warn at 400KB per file
187+
- Error at 500KB per file
188+
- Show total size indicator
189+
190+
## Future Enhancements
191+
192+
1. **Drag and Drop Reordering**
193+
2. **File Templates** (e.g., "Add HTML/CSS/JS set")
194+
3. **Import from URL**
195+
4. **Syntax Validation**
196+
5. **Collaborative Editing**
197+
198+
## Implementation Checklist
199+
200+
- [ ] Create FileEditor component
201+
- [ ] Create MultiFileEditor container
202+
- [ ] Implement file state management
203+
- [ ] Add filename validation
204+
- [ ] Implement language auto-detection
205+
- [ ] Add remove functionality with confirmation
206+
- [ ] Create AddFileButton component
207+
- [ ] Implement auto-scroll on add
208+
- [ ] Add keyboard shortcuts
209+
- [ ] Implement responsive design
210+
- [ ] Add accessibility features
211+
- [ ] Create unit tests
212+
- [ ] Add integration tests
213+
- [ ] Performance optimization
214+
- [ ] Documentation

docs/SPEC.md

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -311,7 +311,11 @@ img-src 'self' data: https:;
311311
### Creating a Gist
312312

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

331336
### Editing a Gist
332337

333338
1. Click "Edit" button
334339
2. Enter PIN if required
335-
3. Make changes
340+
3. Make changes:
341+
- Edit existing files inline
342+
- Add new files at the bottom
343+
- Remove files (minimum 1 required)
336344
4. Click "Update"
337345
5. New version created
338346

347+
### Multiple File Editor UX
348+
349+
The file editor follows GitHub Gist's design pattern:
350+
351+
- **Vertical Layout:** All files displayed vertically in a single scrollable page
352+
- **File Components:** Each file has:
353+
- Filename input field (with extension auto-detection)
354+
- Language selector dropdown
355+
- Remove button (✕) on the right (hidden when only 1 file)
356+
- Full-height code editor below
357+
- **Add File Button:** At the bottom of all files
358+
- Adds a new empty file editor
359+
- Auto-scrolls to the new file
360+
- Default filename: `file1.txt`, `file2.txt`, etc.
361+
- **File Management:**
362+
- Minimum 1 file required
363+
- Maximum 20 files allowed
364+
- Real-time validation of filenames
365+
- Duplicate filenames prevented
366+
- **Responsive Design:**
367+
- Stack vertically on all screen sizes
368+
- Consistent spacing between files
369+
- Touch-friendly controls on mobile
370+
339371
---
340372

341373
## 🚀 Implementation Notes

docs/TODO.md

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,8 +107,10 @@ This document tracks the implementation progress of GhostPaste. Check off tasks
107107

108108
### Form Components
109109

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

132139
## 🔌 Phase 5: API Development
133140

0 commit comments

Comments
 (0)