Merge pull request 'feature/sprint1-dragdrop-presets-shortcuts' (#10) from feature/sprint1-dragdrop-presets-shortcuts into main
Reviewed-on: #10
This commit was merged in pull request #10.
This commit is contained in:
211
INSTRUCTIONS.md
211
INSTRUCTIONS.md
@@ -6,35 +6,41 @@ PNGer is a single-container web application for PNG editing and resizing, design
|
|||||||
|
|
||||||
## Development Roadmap
|
## Development Roadmap
|
||||||
|
|
||||||
### Phase 1: MVP Foundation (Current)
|
### Phase 1: MVP Foundation (Completed ✅)
|
||||||
- [x] Repository setup
|
- [x] Repository setup
|
||||||
- [ ] Project structure initialization
|
- [x] Project structure initialization
|
||||||
- [ ] Backend API scaffold
|
- [x] Backend API scaffold
|
||||||
- [ ] Frontend scaffold
|
- [x] Frontend scaffold
|
||||||
- [ ] Basic upload/download flow
|
- [x] Basic upload/download flow
|
||||||
- [ ] Dockerfile configuration
|
- [x] Dockerfile configuration
|
||||||
|
- [x] Live preview implementation
|
||||||
|
- [x] Dark/light mode theming
|
||||||
|
- [x] Image resize and compression
|
||||||
|
- [x] Format conversion (PNG, WebP, JPEG)
|
||||||
|
|
||||||
### Phase 2: Core Features
|
### Phase 2: Sprint 1 - Enhanced UX (Completed ✅)
|
||||||
- [ ] Image resize functionality
|
- [x] Drag & drop file upload
|
||||||
- [ ] PNG compression
|
- [x] Clipboard paste (Ctrl+V)
|
||||||
- [ ] Real-time preview
|
- [x] Smart presets (8 configurations)
|
||||||
- [ ] Responsive UI design
|
- [x] Keyboard shortcuts (Enter, Ctrl+Enter, ?, Esc)
|
||||||
- [ ] Error handling
|
- [x] Enhanced visual feedback
|
||||||
|
- [x] Responsive UI improvements
|
||||||
|
- [x] Theme contrast fixes
|
||||||
|
|
||||||
### Phase 3: Polish & Deployment
|
### Phase 3: Advanced Features (Upcoming)
|
||||||
- [ ] Docker Compose for Unraid
|
- [ ] Batch processing
|
||||||
- [ ] Environment configuration
|
- [ ] Advanced crop tool with visual selector
|
||||||
- [ ] Documentation
|
- [ ] Watermarking
|
||||||
- [ ] Testing
|
- [ ] Image filters and effects
|
||||||
- [ ] Production build optimization
|
- [ ] Undo/redo functionality
|
||||||
|
- [ ] History/recent files
|
||||||
|
|
||||||
## Technical Architecture
|
## Technical Architecture
|
||||||
|
|
||||||
### Backend (Express + Sharp)
|
### Backend (Express + Sharp)
|
||||||
|
|
||||||
**Endpoints:**
|
**Endpoints:**
|
||||||
- `POST /api/upload` - Accept PNG file
|
- `POST /api/transform` - Transform image (resize, crop, compress, convert)
|
||||||
- `POST /api/process` - Resize/compress image
|
|
||||||
- `GET /api/health` - Health check
|
- `GET /api/health` - Health check
|
||||||
|
|
||||||
**Key Dependencies:**
|
**Key Dependencies:**
|
||||||
@@ -46,11 +52,21 @@ PNGer is a single-container web application for PNG editing and resizing, design
|
|||||||
### Frontend (Svelte + Vite)
|
### Frontend (Svelte + Vite)
|
||||||
|
|
||||||
**Components:**
|
**Components:**
|
||||||
- `App.svelte` - Main container
|
- `App.svelte` - Main container with all features
|
||||||
- `Uploader.svelte` - Drag & drop interface
|
- `lib/api.ts` - API client
|
||||||
- `Editor.svelte` - Resize controls
|
- `lib/preview.ts` - Live preview logic using Canvas API
|
||||||
- `Preview.svelte` - Real-time image preview
|
- `lib/theme.ts` - Dark/light theme management
|
||||||
- `Download.svelte` - Download button
|
- `lib/presets.ts` - Smart presets configuration
|
||||||
|
|
||||||
|
**Key Features:**
|
||||||
|
- Drag & drop upload
|
||||||
|
- Clipboard paste support
|
||||||
|
- Real-time preview (debounced 300ms)
|
||||||
|
- Side-by-side comparison
|
||||||
|
- File size analysis
|
||||||
|
- 8 smart presets
|
||||||
|
- Keyboard shortcuts
|
||||||
|
- Persistent theme preference
|
||||||
|
|
||||||
**Key Dependencies:**
|
**Key Dependencies:**
|
||||||
- svelte: Reactive framework
|
- svelte: Reactive framework
|
||||||
@@ -80,9 +96,6 @@ PNGer is a single-container web application for PNG editing and resizing, design
|
|||||||
git clone https://git.alwisp.com/jason/pnger.git
|
git clone https://git.alwisp.com/jason/pnger.git
|
||||||
cd pnger
|
cd pnger
|
||||||
|
|
||||||
# Install root dependencies (if monorepo)
|
|
||||||
npm install
|
|
||||||
|
|
||||||
# Setup backend
|
# Setup backend
|
||||||
cd backend
|
cd backend
|
||||||
npm install
|
npm install
|
||||||
@@ -110,7 +123,7 @@ npm run dev
|
|||||||
```
|
```
|
||||||
PORT=3000
|
PORT=3000
|
||||||
NODE_ENV=development
|
NODE_ENV=development
|
||||||
MAX_FILE_SIZE=10
|
MAX_FILE_SIZE=10485760
|
||||||
CORS_ORIGIN=http://localhost:5173
|
CORS_ORIGIN=http://localhost:5173
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -167,21 +180,65 @@ docker-compose up -d
|
|||||||
|
|
||||||
## Code Standards
|
## Code Standards
|
||||||
|
|
||||||
### JavaScript/Svelte
|
### JavaScript/TypeScript
|
||||||
- Use ES6+ features
|
- Use ES6+ features
|
||||||
- Async/await for asynchronous operations
|
- Async/await for asynchronous operations
|
||||||
- Descriptive variable names
|
- Descriptive variable names
|
||||||
- Comments for complex logic only
|
- Comments for complex logic only
|
||||||
|
- Type safety with TypeScript
|
||||||
|
|
||||||
### File Organization
|
### File Organization
|
||||||
- One component per file
|
- One component per file
|
||||||
- Group related utilities
|
- Group related utilities
|
||||||
- Keep components under 200 lines
|
- Keep components under 300 lines (split if larger)
|
||||||
|
- Organize by feature when possible
|
||||||
|
|
||||||
### Commit Messages
|
### Commit Messages
|
||||||
- Format: `type: description`
|
- Format: `type: description`
|
||||||
- Types: feat, fix, docs, style, refactor, test
|
- Types: feat, fix, docs, style, refactor, test, chore
|
||||||
- Example: `feat: add drag and drop upload`
|
- Example: `feat: add drag and drop upload`
|
||||||
|
- Be specific and descriptive
|
||||||
|
|
||||||
|
## Feature Implementation Notes
|
||||||
|
|
||||||
|
### Drag & Drop Upload
|
||||||
|
- Uses HTML5 Drag and Drop API
|
||||||
|
- Visual feedback on dragover/dragleave
|
||||||
|
- File type validation on drop
|
||||||
|
- Falls back to file input for browser support
|
||||||
|
|
||||||
|
### Clipboard Paste
|
||||||
|
- Listens for paste events on document
|
||||||
|
- Extracts image from clipboard items
|
||||||
|
- Supports screenshots and copied images
|
||||||
|
- Works across all major browsers
|
||||||
|
|
||||||
|
### Smart Presets
|
||||||
|
- 8 predefined configurations:
|
||||||
|
1. Web Optimized (1920x1080, 80%, WebP)
|
||||||
|
2. Social Media (1080x1080, 85%, JPEG)
|
||||||
|
3. Profile Picture (400x400, 90%, PNG)
|
||||||
|
4. Email Friendly (800x600, 75%, JPEG)
|
||||||
|
5. Tiny Icon (64x64, 100%, PNG)
|
||||||
|
6. Retina 2x (2x size, 90%)
|
||||||
|
7. Icon Small (256x256, 95%, PNG)
|
||||||
|
8. Icon Large (512x512, 95%, PNG)
|
||||||
|
- Applies all settings with one click
|
||||||
|
- Visual icons for easy identification
|
||||||
|
|
||||||
|
### Keyboard Shortcuts
|
||||||
|
- Global document listeners
|
||||||
|
- Context-aware Enter key (checks focus)
|
||||||
|
- Help dialog with ? key
|
||||||
|
- Esc to close dialogs
|
||||||
|
- Cross-platform support (Ctrl/Cmd)
|
||||||
|
|
||||||
|
### Live Preview
|
||||||
|
- Client-side Canvas API rendering
|
||||||
|
- Debounced updates (300ms)
|
||||||
|
- Reactive state management
|
||||||
|
- Automatic size estimation
|
||||||
|
- Side-by-side comparison
|
||||||
|
|
||||||
## Troubleshooting
|
## Troubleshooting
|
||||||
|
|
||||||
@@ -202,27 +259,101 @@ npm rebuild sharp
|
|||||||
- Verify Dockerfile syntax
|
- Verify Dockerfile syntax
|
||||||
- Clear Docker cache: `docker builder prune`
|
- Clear Docker cache: `docker builder prune`
|
||||||
|
|
||||||
|
**Preview not updating:**
|
||||||
|
- Check browser console for errors
|
||||||
|
- Verify Canvas API support
|
||||||
|
- Clear browser cache
|
||||||
|
|
||||||
|
**Drag & drop not working:**
|
||||||
|
- Check file type validation
|
||||||
|
- Verify event handlers are attached
|
||||||
|
- Test with different browsers
|
||||||
|
|
||||||
## Performance Targets
|
## Performance Targets
|
||||||
|
|
||||||
- Upload handling: < 100ms (for 5MB file)
|
- Upload handling: < 100ms (for 5MB file)
|
||||||
- Image processing: < 2s (for 10MP image)
|
- Image processing: < 2s (for 10MP image)
|
||||||
- Download generation: < 500ms
|
- Download generation: < 500ms
|
||||||
- UI response time: < 100ms
|
- UI response time: < 100ms
|
||||||
|
- Preview generation: < 500ms (client-side)
|
||||||
- Docker image size: < 150MB
|
- Docker image size: < 150MB
|
||||||
|
|
||||||
## Security Considerations
|
## Security Considerations
|
||||||
|
|
||||||
- File type validation (PNG only)
|
- File type validation (image/* only)
|
||||||
- File size limits (10MB default)
|
- File size limits (10MB default, configurable)
|
||||||
- No persistent storage of user files
|
- No persistent storage of user files
|
||||||
- Memory cleanup after processing
|
- Memory cleanup after processing
|
||||||
- CORS configuration
|
- CORS configuration
|
||||||
|
- Input sanitization
|
||||||
|
- Error message sanitization (no path disclosure)
|
||||||
|
|
||||||
|
## Testing Checklist
|
||||||
|
|
||||||
|
### Upload Methods
|
||||||
|
- [ ] File input (click to browse)
|
||||||
|
- [ ] Drag & drop
|
||||||
|
- [ ] Clipboard paste (Ctrl+V)
|
||||||
|
- [ ] File validation errors
|
||||||
|
- [ ] Size limit handling
|
||||||
|
|
||||||
|
### Image Operations
|
||||||
|
- [ ] Resize with width only
|
||||||
|
- [ ] Resize with height only
|
||||||
|
- [ ] Resize with both dimensions
|
||||||
|
- [ ] Crop to fit (cover mode)
|
||||||
|
- [ ] All 9 crop positions
|
||||||
|
- [ ] Format conversion (PNG, WebP, JPEG)
|
||||||
|
- [ ] Quality adjustment
|
||||||
|
|
||||||
|
### Presets
|
||||||
|
- [ ] All 8 presets apply correctly
|
||||||
|
- [ ] Preset values match specifications
|
||||||
|
- [ ] Visual feedback on selection
|
||||||
|
|
||||||
|
### Keyboard Shortcuts
|
||||||
|
- [ ] Ctrl+V pastes image
|
||||||
|
- [ ] Enter downloads (not in input)
|
||||||
|
- [ ] Ctrl+Enter downloads (anywhere)
|
||||||
|
- [ ] ? shows help dialog
|
||||||
|
- [ ] Esc closes dialog
|
||||||
|
|
||||||
|
### UI/UX
|
||||||
|
- [ ] Theme toggle works
|
||||||
|
- [ ] Theme persists on reload
|
||||||
|
- [ ] Live preview updates
|
||||||
|
- [ ] File size analysis accurate
|
||||||
|
- [ ] Error messages clear
|
||||||
|
- [ ] Loading states visible
|
||||||
|
- [ ] Responsive on mobile
|
||||||
|
|
||||||
|
### Browser Compatibility
|
||||||
|
- [ ] Chrome/Edge
|
||||||
|
- [ ] Firefox
|
||||||
|
- [ ] Safari
|
||||||
|
- [ ] Mobile browsers
|
||||||
|
|
||||||
## Next Steps
|
## Next Steps
|
||||||
|
|
||||||
1. Create backend folder structure
|
1. ✅ Create backend folder structure
|
||||||
2. Create frontend folder structure
|
2. ✅ Create frontend folder structure
|
||||||
3. Initialize package.json files
|
3. ✅ Initialize package.json files
|
||||||
4. Create Dockerfile
|
4. ✅ Create Dockerfile
|
||||||
5. Create docker-compose.yml
|
5. ✅ Create docker-compose.yml
|
||||||
6. Begin MVP development
|
6. ✅ Implement MVP features
|
||||||
|
7. ✅ Add drag & drop upload
|
||||||
|
8. ✅ Add clipboard paste
|
||||||
|
9. ✅ Add smart presets
|
||||||
|
10. ✅ Add keyboard shortcuts
|
||||||
|
11. [ ] Implement batch processing
|
||||||
|
12. [ ] Add advanced crop tool
|
||||||
|
13. [ ] Add watermarking
|
||||||
|
14. [ ] Add filters/effects
|
||||||
|
|
||||||
|
## Resources
|
||||||
|
|
||||||
|
- [Sharp Documentation](https://sharp.pixelplumbing.com/)
|
||||||
|
- [Svelte Documentation](https://svelte.dev/docs)
|
||||||
|
- [Canvas API Reference](https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API)
|
||||||
|
- [Drag and Drop API](https://developer.mozilla.org/en-US/docs/Web/API/HTML_Drag_and_Drop_API)
|
||||||
|
- [Clipboard API](https://developer.mozilla.org/en-US/docs/Web/API/Clipboard_API)
|
||||||
71
README.md
71
README.md
@@ -1,6 +1,6 @@
|
|||||||
# PNGer - Modern PNG Editor & Resizer
|
# PNGer - Modern PNG Editor & Resizer
|
||||||
|
|
||||||
A sleek, modern PNG editor and resizer with **live preview**, **dark/light mode theming**, and direct upload/download features. Built with TypeScript and deployed as a single Docker container on Unraid.
|
A sleek, modern PNG editor and resizer with **live preview**, **drag & drop**, **smart presets**, **keyboard shortcuts**, and **dark/light mode theming**. Built with TypeScript and deployed as a single Docker container on Unraid.
|
||||||
|
|
||||||
## ✨ Features
|
## ✨ Features
|
||||||
|
|
||||||
@@ -26,12 +26,31 @@ A sleek, modern PNG editor and resizer with **live preview**, **dark/light mode
|
|||||||
- **Fit Modes**: Inside (resize only) or Cover (crop to fill)
|
- **Fit Modes**: Inside (resize only) or Cover (crop to fill)
|
||||||
|
|
||||||
### 🚀 Performance & Usability
|
### 🚀 Performance & Usability
|
||||||
|
- **Drag & Drop**: Drag images directly into the app or use file browser
|
||||||
|
- **Clipboard Paste**: Paste images with Ctrl+V (Cmd+V on Mac)
|
||||||
|
- **Smart Presets**: Quick access to common configurations:
|
||||||
|
- 📷 Web Optimized (1920x1080, 80% quality, WebP)
|
||||||
|
- 📱 Social Media (1080x1080, 85% quality, JPEG)
|
||||||
|
- 👤 Profile Picture (400x400, 90% quality, PNG)
|
||||||
|
- 📧 Email Friendly (800x600, 75% quality, JPEG)
|
||||||
|
- ⭐ Tiny Icon (64x64, 100% quality, PNG)
|
||||||
|
- 🔍 Retina 2x (2x dimensions, 90% quality)
|
||||||
|
- 💎 Icon Small (256x256, 95% quality, PNG)
|
||||||
|
- 🔶 Icon Large (512x512, 95% quality, PNG)
|
||||||
|
- **Keyboard Shortcuts**: Fast workflow with keyboard controls (see below)
|
||||||
- **Direct Download**: No server-side storage, immediate download
|
- **Direct Download**: No server-side storage, immediate download
|
||||||
- **Modern UI**: Sleek, responsive TypeScript/Svelte design
|
- **Modern UI**: Sleek, responsive TypeScript/Svelte design
|
||||||
- **File Analysis**: Original size, transformed size, savings percentage
|
- **File Analysis**: Original size, transformed size, savings percentage
|
||||||
- **Debounced Updates**: Smooth preview generation (300ms delay)
|
- **Debounced Updates**: Smooth preview generation (300ms delay)
|
||||||
- **Visual Feedback**: Loading states, error messages, success indicators
|
- **Visual Feedback**: Loading states, error messages, success indicators
|
||||||
|
|
||||||
|
### ⌨️ Keyboard Shortcuts
|
||||||
|
- **Ctrl+V** (Cmd+V): Paste image from clipboard
|
||||||
|
- **Enter**: Transform & Download (works when input not focused)
|
||||||
|
- **Ctrl+Enter** (Cmd+Enter): Transform & Download (works anywhere)
|
||||||
|
- **?**: Show/hide keyboard shortcuts help
|
||||||
|
- **Esc**: Close shortcuts dialog
|
||||||
|
|
||||||
## Tech Stack
|
## Tech Stack
|
||||||
|
|
||||||
- **Frontend**: Svelte 4 + Vite + TypeScript
|
- **Frontend**: Svelte 4 + Vite + TypeScript
|
||||||
@@ -170,7 +189,8 @@ pnger/
|
|||||||
│ │ └── lib/
|
│ │ └── lib/
|
||||||
│ │ ├── api.ts # API client
|
│ │ ├── api.ts # API client
|
||||||
│ │ ├── preview.ts # Live preview logic
|
│ │ ├── preview.ts # Live preview logic
|
||||||
│ │ └── theme.ts # Theme management store
|
│ │ ├── theme.ts # Theme management store
|
||||||
|
│ │ └── presets.ts # Smart presets configuration
|
||||||
│ ├── package.json
|
│ ├── package.json
|
||||||
│ ├── tsconfig.json
|
│ ├── tsconfig.json
|
||||||
│ └── vite.config.ts
|
│ └── vite.config.ts
|
||||||
@@ -186,17 +206,18 @@ pnger/
|
|||||||
├── Dockerfile # Multi-stage build (frontend + backend)
|
├── Dockerfile # Multi-stage build (frontend + backend)
|
||||||
├── docker-compose.yml # Unraid deployment config
|
├── docker-compose.yml # Unraid deployment config
|
||||||
├── ROADMAP.md # Feature roadmap
|
├── ROADMAP.md # Feature roadmap
|
||||||
|
├── SPRINT1_CHANGES.md # Sprint 1 implementation details
|
||||||
└── UI_UPGRADE_NOTES.md # UI upgrade documentation
|
└── UI_UPGRADE_NOTES.md # UI upgrade documentation
|
||||||
```
|
```
|
||||||
|
|
||||||
## How It Works
|
## How It Works
|
||||||
|
|
||||||
1. User uploads an image via the web interface
|
1. User uploads an image via drag & drop, file browser, or clipboard paste
|
||||||
2. **Live preview** generates instantly using Canvas API
|
2. **Live preview** generates instantly using Canvas API
|
||||||
3. User adjusts parameters (width, height, quality, format, etc.)
|
3. User adjusts parameters (width, height, quality, format, etc.) or selects a preset
|
||||||
4. Preview updates in real-time (debounced 300ms)
|
4. Preview updates in real-time (debounced 300ms)
|
||||||
5. User sees file size comparison and savings
|
5. User sees file size comparison and savings
|
||||||
6. When satisfied, user clicks "Transform & Download"
|
6. When satisfied, user clicks "Transform & Download" or presses Enter
|
||||||
7. Frontend sends image + parameters to backend API
|
7. Frontend sends image + parameters to backend API
|
||||||
8. Backend processes using Sharp (resize, crop, compress, convert)
|
8. Backend processes using Sharp (resize, crop, compress, convert)
|
||||||
9. Processed image returned directly to browser
|
9. Processed image returned directly to browser
|
||||||
@@ -250,20 +271,48 @@ All configuration is handled via environment variables passed through Docker/Unr
|
|||||||
- **Instant Updates**: Debounced at 300ms for smooth performance
|
- **Instant Updates**: Debounced at 300ms for smooth performance
|
||||||
- **Canvas-Based**: No server load, runs in browser
|
- **Canvas-Based**: No server load, runs in browser
|
||||||
|
|
||||||
|
### Drag & Drop Upload
|
||||||
|
- **Drop Zone**: Large, responsive drop target with visual feedback
|
||||||
|
- **Multiple Methods**: Drag & drop, click to browse, or paste from clipboard
|
||||||
|
- **File Validation**: Instant validation with clear error messages
|
||||||
|
- **Visual States**: Hover and dragging states for better UX
|
||||||
|
|
||||||
|
### Smart Presets
|
||||||
|
- **8 Pre-configured Options**: Common use cases ready to go
|
||||||
|
- **One-Click Apply**: Instantly sets all parameters
|
||||||
|
- **Visual Icons**: Easy identification of each preset
|
||||||
|
- **Responsive Grid**: Adapts to screen size
|
||||||
|
|
||||||
|
### Keyboard Shortcuts
|
||||||
|
- **Productivity Focused**: Common operations accessible via keyboard
|
||||||
|
- **Help Dialog**: Press ? to see all shortcuts
|
||||||
|
- **Context Aware**: Enter works differently based on focus
|
||||||
|
- **Cross-Platform**: Supports both Ctrl and Cmd modifiers
|
||||||
|
|
||||||
### Image Analysis
|
### Image Analysis
|
||||||
- Original file size displayed
|
- Original file size displayed
|
||||||
- Preview size estimation
|
- Preview size estimation
|
||||||
- Savings/increase percentage
|
- Savings/increase percentage
|
||||||
- Visual feedback with color coding
|
- Visual feedback with color coding
|
||||||
|
|
||||||
|
## Recent Updates (Sprint 1)
|
||||||
|
|
||||||
|
✅ **Drag & Drop Upload** - Drag images directly into the app
|
||||||
|
✅ **Clipboard Paste** - Paste images with Ctrl+V
|
||||||
|
✅ **Smart Presets** - 8 quick-access presets for common scenarios
|
||||||
|
✅ **Keyboard Shortcuts** - Fast workflow with keyboard controls
|
||||||
|
✅ **Enhanced UX** - Better visual feedback and error handling
|
||||||
|
✅ **Theme Contrast Fixes** - Improved text visibility in both themes
|
||||||
|
|
||||||
|
See [SPRINT1_CHANGES.md](./SPRINT1_CHANGES.md) for detailed implementation notes.
|
||||||
|
|
||||||
## Roadmap
|
## Roadmap
|
||||||
|
|
||||||
See [ROADMAP.md](./ROADMAP.md) for planned features including:
|
See [ROADMAP.md](./ROADMAP.md) for planned features including:
|
||||||
- Drag & drop upload
|
|
||||||
- Batch processing
|
- Batch processing
|
||||||
- Smart presets
|
- Advanced crop tool with visual selector
|
||||||
- Watermarking
|
- Watermarking
|
||||||
- Advanced crop tool
|
- Image filters and effects
|
||||||
- And more!
|
- And more!
|
||||||
|
|
||||||
## License
|
## License
|
||||||
@@ -284,3 +333,9 @@ Sleek black interface with light gold accents, ideal for viewing light/transpare
|
|||||||
|
|
||||||
### Live Preview
|
### Live Preview
|
||||||
Side-by-side comparison showing original and transformed image with file size analysis
|
Side-by-side comparison showing original and transformed image with file size analysis
|
||||||
|
|
||||||
|
### Smart Presets
|
||||||
|
Quick-access buttons for common image optimization scenarios
|
||||||
|
|
||||||
|
### Keyboard Shortcuts
|
||||||
|
Built-in help dialog showing all available shortcuts
|
||||||
Reference in New Issue
Block a user