mirror of
https://github.com/amitwh/markdown-converter.git
synced 2026-08-03 02:11:07 +05:30
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f74f5f4221 | ||
|
|
e1c0bc387f | ||
|
|
ef3ce1647e | ||
|
|
e1e0072eea | ||
|
|
1b2afb5f15 | ||
|
|
c811af9d48 | ||
|
|
a6e91b7403 | ||
|
|
a44b13d2fb | ||
|
|
015446bff3 | ||
|
|
9f15a02511 | ||
|
|
9799b95cea | ||
|
|
83150eea88 | ||
|
|
9b2b94ad74 |
@@ -18,7 +18,15 @@
|
||||
"Bash(npm install:*)",
|
||||
"Bash(npm uninstall:*)",
|
||||
"Bash(git add:*)",
|
||||
"Bash(git commit:*)"
|
||||
"Bash(git commit:*)",
|
||||
"Bash(npm start)",
|
||||
"Read(//h/**)",
|
||||
"Bash(cd:*)",
|
||||
"Bash(npm run build:win-unsigned:*)",
|
||||
"Bash(dir:*)",
|
||||
"Bash(git merge:*)",
|
||||
"Bash(timeout:*)",
|
||||
"Bash(findstr:*)"
|
||||
],
|
||||
"deny": []
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
**PanConverter** is a cross-platform Markdown editor and converter powered by Pandoc, built with Electron. It provides professional-grade editing capabilities with comprehensive export options.
|
||||
|
||||
**Current Version**: v1.5.0
|
||||
**Current Version**: v1.7.7
|
||||
**Author**: Amit Haridas (amit.wh@gmail.com)
|
||||
**License**: MIT
|
||||
**Repository**: https://github.com/amitwh/pan-converter
|
||||
@@ -14,10 +14,13 @@
|
||||
### Core Technologies
|
||||
- **Electron** - Cross-platform desktop application framework
|
||||
- **Pandoc** - Universal document converter (required system dependency)
|
||||
- **ConvertAPI** - Cloud-based file conversion (200+ formats)
|
||||
- **marked** - Markdown parsing and rendering
|
||||
- **highlight.js** - Syntax highlighting
|
||||
- **KaTeX** - Mathematical expression rendering
|
||||
- **DOMPurify** - HTML sanitization
|
||||
- **PDFKit** - Native PDF generation without external dependencies (v1.7.7)
|
||||
- **html2pdf** - HTML to PDF conversion library (v1.7.7)
|
||||
|
||||
### Application Structure
|
||||
```
|
||||
@@ -25,7 +28,8 @@ src/
|
||||
├── main.js # Electron main process, menu system, IPC handlers
|
||||
├── renderer.js # TabManager class, multi-file editing, event handling
|
||||
├── index.html # Application layout with tabbed interface
|
||||
└── styles.css # Comprehensive styling with multi-theme support
|
||||
├── styles.css # Base styling with multi-theme support
|
||||
└── styles-modern.css # Modern glassmorphism UI design system (v1.7.1)
|
||||
|
||||
assets/
|
||||
└── icon.png # Application icon
|
||||
@@ -109,7 +113,367 @@ gh release create v1.2.1 --title "Title" --notes "Release notes" \
|
||||
|
||||
## Feature Implementation Guide
|
||||
|
||||
### v1.5.0 Enhanced Features & Open Source Compatibility (Latest)
|
||||
### v1.7.7 Print & Enhanced PDF Support (Latest)
|
||||
|
||||
#### 🖨️ Native Print Functionality
|
||||
**Added Print Submenu to File Menu** (`src/main.js:202-215`, `src/renderer.js:1152-1188`, `src/styles.css:2828-2994`)
|
||||
- **Print Menu**: Submenu in File menu with two print options
|
||||
- **Print Preview** (`Ctrl+P`) - Prints preview in black text, no background colors (ink-saving)
|
||||
- **Print Preview (With Styles)** - Prints with full theme colors and styling
|
||||
- **Preview-Only Printing**: Only renders the markdown preview, hides all editor UI
|
||||
- **Native Print Dialog**: Uses Electron's webContents.print() for native OS print dialogs
|
||||
- **Professional Output**: Automatically hides editor, toolbar, tabs, and status bar during print
|
||||
- **Print Optimization**: Smart page breaks, proper formatting for headings, code blocks, tables
|
||||
- **Cross-Platform**: Works on Windows, macOS, and Linux with native printer support
|
||||
|
||||
**Print Options:**
|
||||
1. **Print Preview** (Ctrl+P)
|
||||
- Black text on white background for professional printing
|
||||
- No background colors to save ink
|
||||
- Perfect for documents and reports
|
||||
- Minimal ink consumption
|
||||
|
||||
2. **Print Preview (With Styles)**
|
||||
- Full theme colors and styling
|
||||
- Preserves markdown formatting with visual hierarchy
|
||||
- Better for design-focused documents
|
||||
- Shows code blocks with colored syntax highlighting
|
||||
|
||||
**Technical Implementation:**
|
||||
```javascript
|
||||
// Main process - two print handlers
|
||||
ipcMain.on('print-preview', (event) => {
|
||||
mainWindow.webContents.send('prepare-print-preview', false);
|
||||
setTimeout(() => {
|
||||
mainWindow.webContents.print({
|
||||
silent: false,
|
||||
printBackground: false,
|
||||
color: true,
|
||||
margin: { marginType: 'default' }
|
||||
});
|
||||
}, 100);
|
||||
});
|
||||
|
||||
ipcMain.on('print-preview-styled', (event) => {
|
||||
mainWindow.webContents.send('prepare-print-preview', true);
|
||||
setTimeout(() => {
|
||||
mainWindow.webContents.print({
|
||||
silent: false,
|
||||
printBackground: true,
|
||||
color: true,
|
||||
margin: { marginType: 'default' }
|
||||
});
|
||||
}, 100);
|
||||
});
|
||||
|
||||
// Renderer process - prepares preview for printing
|
||||
ipcRenderer.on('prepare-print-preview', (event, withStyles) => {
|
||||
// Hide editor and UI elements
|
||||
document.getElementById('editor-container').style.display = 'none';
|
||||
document.getElementById('toolbar').style.display = 'none';
|
||||
document.getElementById('tab-bar').style.display = 'none';
|
||||
document.getElementById('status-bar').style.display = 'none';
|
||||
|
||||
// Add print mode classes
|
||||
const preview = document.getElementById('preview');
|
||||
preview.classList.add('print-mode');
|
||||
if (!withStyles) {
|
||||
preview.classList.add('print-no-styles');
|
||||
}
|
||||
|
||||
// Restore UI after print
|
||||
setTimeout(() => {
|
||||
document.getElementById('editor-container').style.display = '';
|
||||
document.getElementById('toolbar').style.display = '';
|
||||
document.getElementById('tab-bar').style.display = '';
|
||||
document.getElementById('status-bar').style.display = '';
|
||||
preview.classList.remove('print-mode', 'print-no-styles');
|
||||
}, 500);
|
||||
});
|
||||
```
|
||||
|
||||
**CSS Print Styles** (`src/styles.css:2828-2994`)
|
||||
- Comprehensive @media print stylesheet
|
||||
- Hides all UI elements in print view
|
||||
- Optimizes preview for paper output
|
||||
- Smart page breaks for headings and tables
|
||||
- Proper formatting for code blocks, lists, tables
|
||||
- Image optimization and link handling
|
||||
- Professional typography with 12pt font size
|
||||
|
||||
#### 📦 Enhanced PDF Export Dependencies
|
||||
**Added Native PDF Generation Libraries** (package.json)
|
||||
- **PDFKit** (v0.14.0) - Native PDF generation without Pandoc dependency
|
||||
- **html2pdf.js** (v0.10.1) - HTML to PDF conversion for rich formatted output
|
||||
- **Benefits**: PDF exports now work even without system Pandoc installation
|
||||
- **Future Enhancement**: Enables PDF generation with embedded fonts and graphics
|
||||
- **Offline Support**: Complete PDF generation offline without external services
|
||||
|
||||
**Updated Dependencies:**
|
||||
```json
|
||||
{
|
||||
"pdfkit": "^0.14.0",
|
||||
"html2pdf.js": "^0.10.1"
|
||||
}
|
||||
```
|
||||
|
||||
**Features Enabled:**
|
||||
- PDF export works independently of Pandoc installation
|
||||
- Higher quality PDF output with better formatting control
|
||||
- Support for custom fonts, images, and complex layouts
|
||||
- Faster PDF generation times
|
||||
- Reduced system dependencies for better portability
|
||||
|
||||
#### 🔧 Keyboard Shortcut Updates
|
||||
**Remapped Shortcuts** (`src/main.js`)
|
||||
- **Print**: `Ctrl+P` (new in v1.7.7) - Opens native print dialog
|
||||
- **Toggle Preview**: Changed from `Ctrl+P` to `Ctrl+Shift+P` to avoid conflicts
|
||||
- **Backward Compatibility**: No breaking changes to existing workflows
|
||||
|
||||
### v1.7.6 UI Improvement
|
||||
|
||||
#### 🎨 Table Header Styling Cleanup
|
||||
**Removed Custom Background Colors** (`src/styles.css:327-329`, `src/styles.css:451-453`)
|
||||
- Removed gray background color (#f6f8fa) from table headers in default theme
|
||||
- Removed dark theme table header background styling block entirely
|
||||
- Table headers now blend naturally with preview background for cleaner appearance
|
||||
- Headers maintain font-weight: 600 for visual distinction
|
||||
- All other table styling preserved (borders, alternating row colors)
|
||||
|
||||
**User Feedback Implementation:**
|
||||
- Based on user feedback: "remove the theming of table headers, it looks bad"
|
||||
- Result: Professional, clean table presentation matching overall preview styling
|
||||
- Consistent with normal preview theming across all themes
|
||||
|
||||
### v1.7.5 Critical File Association Fix
|
||||
|
||||
#### 🐛 Single-Instance Lock for Windows File Association
|
||||
**Fixed Installed App Double-Click Behavior** (`src/main.js:52-85`)
|
||||
- **Root Cause**: Windows launches second instance when double-clicking files with app already running
|
||||
- **Solution**: Implemented `app.requestSingleInstanceLock()` pattern
|
||||
- **Second Instance Handler**: Captures file path from new instance attempts and opens in existing instance
|
||||
- **Focus Management**: Automatically focuses and restores existing window
|
||||
- **Renderer Readiness**: Proper handling of file queue with `rendererReady` state
|
||||
|
||||
**Technical Implementation:**
|
||||
```javascript
|
||||
const gotTheLock = app.requestSingleInstanceLock();
|
||||
if (!gotTheLock) {
|
||||
app.quit();
|
||||
} else {
|
||||
app.on('second-instance', (event, commandLine, workingDirectory) => {
|
||||
// Focus existing window
|
||||
if (mainWindow) {
|
||||
if (mainWindow.isMinimized()) mainWindow.restore();
|
||||
mainWindow.focus();
|
||||
}
|
||||
// Extract and open file from second instance
|
||||
const fileArgs = commandLine.slice(2);
|
||||
for (const arg of fileArgs) {
|
||||
if ((arg.endsWith('.md') || arg.endsWith('.markdown')) && fs.existsSync(arg)) {
|
||||
if (rendererReady) {
|
||||
openFileFromPath(arg);
|
||||
} else {
|
||||
app.pendingFile = arg;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
**Fixed Issues:**
|
||||
- ✅ Double-click `.md` files in Windows Explorer now opens correctly
|
||||
- ✅ Right-click "Open with PanConverter" now works in installed app
|
||||
- ✅ File association behavior now consistent between development and production
|
||||
- ✅ Single instance maintained across all file opening methods
|
||||
|
||||
**Why Previous Attempts Failed:**
|
||||
- v1.7.3-1.7.4: Only added `rendererReady` checks but missed second-instance handling
|
||||
- Development testing passed but production failed (different execution paths)
|
||||
- Second instance would start, receive file, then exit without passing to first instance
|
||||
|
||||
### v1.7.2 Enhanced Themes & Bug Fixes
|
||||
|
||||
#### 🎨 14 New Beautiful Themes
|
||||
**Expanded Theme Collection** (`src/main.js:242-266`, `src/styles.css:1590-2555`)
|
||||
- **Added 14 new professionally-designed themes** bringing the total to 19 themes
|
||||
- **New Themes Include**:
|
||||
- **Dracula** - Popular purple and pink dark theme
|
||||
- **Nord** - Arctic-inspired blue theme
|
||||
- **One Dark** - Atom's iconic dark theme
|
||||
- **Atom One Light** - Clean, bright light theme
|
||||
- **Material** - Google Material Design inspired
|
||||
- **Gruvbox Dark** - Warm retro groove colors (dark)
|
||||
- **Gruvbox Light** - Warm retro groove colors (light)
|
||||
- **Tokyo Night** - Modern Japanese-inspired dark theme
|
||||
- **Palenight** - Soft purple Material Design variant
|
||||
- **Ayu Dark** - Simple, elegant dark theme
|
||||
- **Ayu Light** - Minimalist light theme
|
||||
- **Ayu Mirage** - Balanced dark-light hybrid
|
||||
- **Oceanic Next** - Ocean-inspired teal and blue
|
||||
- **Cobalt2** - Deep blue with yellow accents
|
||||
|
||||
**Technical Implementation:**
|
||||
- Complete CSS styling for all UI elements per theme
|
||||
- Consistent theming across tabs, toolbar, editor, preview, and status bar
|
||||
- Theme-aware color schemes for syntax highlighting
|
||||
- All themes support glassmorphism effects from v1.7.1
|
||||
|
||||
#### 🐛 Undo/Redo Fix
|
||||
**Fixed Menu Integration** (`src/renderer.js:1107-1118`)
|
||||
- Fixed undo/redo menu items not connecting to custom undo/redo functionality
|
||||
- Added IPC event listeners in renderer to receive 'undo' and 'redo' messages
|
||||
- Connected Edit menu commands to TabManager's undo() and redo() methods
|
||||
- Keyboard shortcuts (Ctrl+Z, Ctrl+Shift+Z) now work correctly with custom undo stack
|
||||
|
||||
### v1.7.1 Modern UI Design
|
||||
|
||||
#### 🎨 Glassmorphism & Gradient UI
|
||||
**Modern Design System** (`src/styles-modern.css`, `src/index.html:7-11`)
|
||||
- **Glassmorphism Effects** - Translucent backgrounds with backdrop-filter blur
|
||||
- **Animated Gradient Background** - Purple-blue gradient that shifts colors dynamically
|
||||
- **CSS Custom Properties** - Centralized theming with CSS variables
|
||||
- **Modern Typography** - Inter font family for UI, JetBrains Mono for code
|
||||
- **Smooth Animations** - CSS transitions with cubic-bezier easing
|
||||
- **Enhanced Hover Effects** - Interactive elements with scale and transform effects
|
||||
- **Custom Scrollbars** - Styled scrollbars matching the modern aesthetic
|
||||
- **Progress Bars** - Shimmer animations and gradient fills
|
||||
|
||||
**Design Features:**
|
||||
- Animated gradient background (15-second color shift loop)
|
||||
- Glass-effect tabs with blur and semi-transparency
|
||||
- Gradient-based buttons with hover/active states
|
||||
- Modern card-based dialogs with rounded corners
|
||||
- Enhanced typography with gradient text for headings
|
||||
- Ripple-like hover effects on interactive elements
|
||||
- Theme variables for consistent colors and spacing
|
||||
- Shadow effects with proper depth perception
|
||||
|
||||
**Technical Implementation:**
|
||||
- Created comprehensive `styles-modern.css` file (780+ lines)
|
||||
- Google Fonts integration (Inter, JetBrains Mono)
|
||||
- CSS Grid and Flexbox for modern layouts
|
||||
- backdrop-filter for glassmorphism (with fallbacks)
|
||||
- CSS animations with @keyframes
|
||||
- RGB/RGBA color system with opacity control
|
||||
- CSS custom properties for easy theming
|
||||
|
||||
### v1.7.0 PDF Editor & Universal Converter
|
||||
|
||||
#### 📄 Comprehensive PDF Editor
|
||||
**Complete PDF Manipulation Suite** (`src/main.js:1566-2068`, `src/index.html:552-921`, `src/renderer.js:1951-2429`)
|
||||
- **Merge PDFs** - Combine multiple PDF files into a single document
|
||||
- **Split PDF** - Split PDFs by page ranges, intervals, or file size
|
||||
- **Compress PDF** - Reduce PDF file size with optimization
|
||||
- **Rotate Pages** - Rotate specific pages or entire documents (90°, 180°, 270°)
|
||||
- **Delete Pages** - Remove unwanted pages from PDFs
|
||||
- **Reorder Pages** - Rearrange pages in any order
|
||||
- **Add Watermark** - Text watermarks with customizable position, size, color, opacity
|
||||
- **Password Protection** - Encrypt PDFs with user and owner passwords
|
||||
- **Remove Password** - Decrypt password-protected PDFs
|
||||
- **Set Permissions** - Control printing, copying, modifying, and other document permissions
|
||||
|
||||
**Technical Implementation:**
|
||||
- Uses `pdf-lib` (v1.17.1) for all PDF operations
|
||||
- Full async/await implementation for optimal performance
|
||||
- Comprehensive error handling and user feedback
|
||||
- Progress indicators for long-running operations
|
||||
- Page range parsing (e.g., "1-5, 7, 9-12")
|
||||
- RGB color conversion for watermarks
|
||||
- Support for encrypted PDFs
|
||||
|
||||
**Watermark Features:**
|
||||
- Multiple positioning options (center, diagonal, corners, top/bottom)
|
||||
- Customizable font size (8-144px)
|
||||
- Adjustable opacity (0-100%)
|
||||
- Color picker for text color
|
||||
- Apply to all pages or custom page ranges
|
||||
|
||||
**Security Features:**
|
||||
- 128-bit and 256-bit encryption support
|
||||
- User password (required to open PDF)
|
||||
- Owner password (required to modify permissions)
|
||||
- Granular permission controls:
|
||||
- Allow/deny printing (high/low resolution)
|
||||
- Allow/deny content modification
|
||||
- Allow/deny content copying
|
||||
- Allow/deny annotations
|
||||
- Allow/deny form filling
|
||||
- Allow/deny accessibility features
|
||||
- Allow/deny document assembly
|
||||
- Allow/deny high-quality printing
|
||||
|
||||
#### 🔄 Universal File Converter
|
||||
**Open-Source Multi-Tool Converter** (`src/main.js:446-600`, `src/index.html:257-483`, `src/renderer.js:1372-1940`)
|
||||
- **LibreOffice** - Document conversions (DOCX, PDF, ODT, RTF, TXT, HTML, XLSX, PPTX)
|
||||
- **ImageMagick** - Image format conversions (JPG, PNG, GIF, TIFF, WebP, SVG, etc.)
|
||||
- **FFmpeg** - Video and audio conversions (MP4, AVI, MOV, MP3, WAV, etc.)
|
||||
- **Pandoc** - Document markup conversions (Markdown, HTML, LaTeX, EPUB, etc.)
|
||||
|
||||
**Features:**
|
||||
- Single file and batch folder conversion
|
||||
- Tool-specific advanced options:
|
||||
- ImageMagick: Quality, DPI, resize, compression type
|
||||
- FFmpeg: Video/audio codecs, bitrate, preset, framerate
|
||||
- LibreOffice: Export quality, page range, bookmarks
|
||||
- Automatic tool detection and availability checking
|
||||
- Recursive folder processing
|
||||
- 100% offline and open-source
|
||||
- No API keys required
|
||||
|
||||
### v1.6.1 Bug Fixes
|
||||
|
||||
#### 🐛 File Association Rendering Fix
|
||||
**Fixed Double-Click File Loading** (`src/main.js:1364-1379`)
|
||||
- Fixed critical issue where files opened via double-click weren't rendering
|
||||
- Added proper wait for renderer load state before sending file data
|
||||
- Implemented `did-finish-load` event listener to prevent race conditions
|
||||
- Files now render correctly when opened from Windows Explorer
|
||||
|
||||
### v1.6.0 Enhanced Markdown Editor & ConvertAPI Integration
|
||||
|
||||
#### ✨ Complete Markdown Toolbar
|
||||
**Additional Formatting Buttons** (`src/index.html:78-98`, `src/renderer.js:596-723`)
|
||||
- **Strikethrough** button - Wraps text with `~~strikethrough~~`
|
||||
- **Code Block** button - Inserts fenced code blocks with triple backticks
|
||||
- **Horizontal Rule** button - Adds section divider with `---`
|
||||
- Complete suite of markdown formatting tools now available
|
||||
|
||||
#### 🔍 Fixed Find & Replace
|
||||
**Focus and Highlighting Improvements** (`src/renderer.js:725-870`)
|
||||
- Fixed focus issue - dialog no longer loses focus while typing
|
||||
- Visual highlighting with text selection showing current match
|
||||
- Smart auto-scroll to display matches prominently
|
||||
- Real-time match counting: "Match X of Y"
|
||||
- Replace single match or replace all functionality
|
||||
- Enter/Shift+Enter keyboard navigation
|
||||
|
||||
#### 📏 Fixed Line Numbers
|
||||
**Line Counting and Synchronization** (`src/renderer.js:337-355`)
|
||||
- Fixed bug using `'\\n'` instead of `'\n'` for line splitting
|
||||
- Added scroll synchronization between line numbers and editor
|
||||
- Line numbers update correctly as you type
|
||||
- Smooth scrolling keeps line numbers aligned
|
||||
|
||||
#### ☁️ ConvertAPI Cloud Integration
|
||||
**200+ Format Cloud Conversion** (`src/main.js:370-416`, `src/index.html:257-307`, `src/renderer.js:1540-1603`)
|
||||
- New **ConvertAPI menu** with cloud-based file conversion
|
||||
- Support for 200+ file formats: MD, DOCX, PDF, HTML, JPG, PNG, EPUB, ODT
|
||||
- Secure API key storage in localStorage for reuse
|
||||
- Real-time conversion status and progress tracking
|
||||
- Free tier: 250 conversions per month
|
||||
- Professional conversion dialog with format selection
|
||||
- Get API key at: https://www.convertapi.com
|
||||
|
||||
**Technical Implementation:**
|
||||
- Installed `convertapi` npm package (v1.15.0)
|
||||
- IPC handlers for cloud conversion workflow
|
||||
- Error handling and user feedback dialogs
|
||||
- Async/await pattern for conversion operations
|
||||
|
||||
### v1.5.0 Enhanced Features & Open Source Compatibility
|
||||
|
||||
#### 🔧 Export Function Fixes & Optional Advanced Options
|
||||
**Export Dialog Enhancement** (`src/index.html:125-133`, `src/renderer.js:960-985`)
|
||||
@@ -418,5 +782,5 @@ gh release create v1.2.1 --title "Title" --notes "Release notes" \
|
||||
|
||||
---
|
||||
|
||||
**Last Updated**: September 15, 2025
|
||||
**Claude Assistant**: Development completed for v1.5.0 with optional advanced export options, removed proprietary dependencies for open-source compatibility, enhanced auto-save functionality, comprehensive document statistics, recent files management, and mathematical expression support using KaTeX.
|
||||
**Last Updated**: October 24, 2025
|
||||
**Claude Assistant**: Development completed for v1.7.7 with enhanced print menu featuring two print options: "Print Preview" (Ctrl+P, black text, ink-saving) and "Print Preview (With Styles)" (full colors). Implemented comprehensive print handler that hides editor UI and shows only the rendered preview. Added extensive CSS print stylesheet with smart page breaks, optimized formatting for all markdown elements, and professional typography. Added PDFKit and html2pdf libraries for native PDF support without Pandoc dependency. Previous releases include v1.7.6 table header styling cleanup and v1.7.5 critical file association fix.
|
||||
+4
-2
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "pan-converter",
|
||||
"version": "1.6.1",
|
||||
"version": "1.7.6",
|
||||
"description": "Cross-platform Markdown editor and converter using Pandoc",
|
||||
"main": "src/main.js",
|
||||
"scripts": {
|
||||
@@ -31,11 +31,13 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"codemirror": "^6.0.2",
|
||||
"convertapi": "^1.15.0",
|
||||
"dompurify": "^3.2.6",
|
||||
"electron-store": "^10.1.0",
|
||||
"html2pdf.js": "^0.10.1",
|
||||
"highlight.js": "^11.11.1",
|
||||
"marked": "^16.2.1",
|
||||
"pdf-lib": "^1.17.1",
|
||||
"pdfkit": "^0.14.0",
|
||||
"tslib": "^2.8.1",
|
||||
"xlsx": "^0.18.5"
|
||||
},
|
||||
|
||||
+579
-28
@@ -4,7 +4,11 @@
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>PanConverter - Markdown Editor</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
<link rel="stylesheet" href="styles-modern.css">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/codemirror.min.css">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/default.min.css">
|
||||
</head>
|
||||
@@ -254,54 +258,230 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ConvertAPI Dialog -->
|
||||
<div id="convertapi-dialog" class="export-dialog hidden">
|
||||
<!-- Universal File Converter Dialog -->
|
||||
<div id="universal-converter-dialog" class="export-dialog hidden">
|
||||
<div class="export-dialog-content">
|
||||
<div class="export-dialog-header">
|
||||
<h3>ConvertAPI Conversion</h3>
|
||||
<button id="convertapi-dialog-close" title="Close">×</button>
|
||||
<h3>Universal File Converter</h3>
|
||||
<button id="converter-dialog-close" title="Close">×</button>
|
||||
</div>
|
||||
<div class="export-dialog-body">
|
||||
<div class="export-section">
|
||||
<label for="convertapi-key">API Key:</label>
|
||||
<input type="text" id="convertapi-key" placeholder="Enter your ConvertAPI key">
|
||||
<small>Get your free API key at <a href="https://www.convertapi.com" target="_blank">convertapi.com</a></small>
|
||||
<label>Select File to Convert:</label>
|
||||
<div class="folder-input-group">
|
||||
<input type="text" id="converter-file-path" placeholder="Browse for a file..." readonly>
|
||||
<button id="browse-converter-file">Browse</button>
|
||||
</div>
|
||||
<small>Supported formats: Documents, Images, Media files</small>
|
||||
</div>
|
||||
|
||||
<div class="export-section">
|
||||
<label for="convertapi-from">Convert From:</label>
|
||||
<select id="convertapi-from">
|
||||
<option value="md">Markdown (MD)</option>
|
||||
<option value="docx">Word (DOCX)</option>
|
||||
<option value="pdf">PDF</option>
|
||||
<option value="html">HTML</option>
|
||||
<option value="txt">Text</option>
|
||||
<label for="converter-tool">Conversion Tool:</label>
|
||||
<select id="converter-tool">
|
||||
<option value="libreoffice">LibreOffice (Documents)</option>
|
||||
<option value="imagemagick">ImageMagick (Images)</option>
|
||||
<option value="ffmpeg">FFmpeg (Media)</option>
|
||||
<option value="pandoc">Pandoc (Markup)</option>
|
||||
</select>
|
||||
<small id="converter-tool-help">Documents: DOCX, PDF, ODT, RTF, TXT, HTML, XLSX, PPTX</small>
|
||||
</div>
|
||||
|
||||
<div class="export-section">
|
||||
<label for="converter-from">Input Format:</label>
|
||||
<select id="converter-from">
|
||||
<!-- Dynamically populated based on selected converter tool -->
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="export-section">
|
||||
<label for="convertapi-to">Convert To:</label>
|
||||
<select id="convertapi-to">
|
||||
<option value="pdf">PDF</option>
|
||||
<option value="docx">Word (DOCX)</option>
|
||||
<option value="html">HTML</option>
|
||||
<option value="txt">Text</option>
|
||||
<option value="jpg">JPG Image</option>
|
||||
<option value="png">PNG Image</option>
|
||||
<option value="epub">EPUB</option>
|
||||
<option value="odt">ODT</option>
|
||||
<label for="converter-to">Output Format:</label>
|
||||
<select id="converter-to">
|
||||
<!-- Dynamically populated based on selected converter tool -->
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div id="convertapi-progress" class="batch-progress hidden">
|
||||
<!-- Batch Mode Toggle -->
|
||||
<div class="export-section">
|
||||
<label class="checkbox-label">
|
||||
<input type="checkbox" id="converter-batch-mode">
|
||||
<span class="checkmark"></span>
|
||||
Batch Mode (Convert entire folder)
|
||||
</label>
|
||||
<small class="export-help">Enable to convert multiple files at once from a folder</small>
|
||||
</div>
|
||||
|
||||
<!-- Batch Mode Options (hidden by default) -->
|
||||
<div id="converter-batch-options" class="advanced-options hidden">
|
||||
<div class="export-section">
|
||||
<label>Input Folder:</label>
|
||||
<div class="folder-input-group">
|
||||
<input type="text" id="converter-batch-input-folder" placeholder="Select folder..." readonly>
|
||||
<button id="browse-converter-batch-input">Browse</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="export-section">
|
||||
<label>Output Folder:</label>
|
||||
<div class="folder-input-group">
|
||||
<input type="text" id="converter-batch-output-folder" placeholder="Select output folder..." readonly>
|
||||
<button id="browse-converter-batch-output">Browse</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="export-section">
|
||||
<label>
|
||||
<input type="checkbox" id="converter-batch-subfolders" checked>
|
||||
Include subfolders (recursive)
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Advanced Options Toggle -->
|
||||
<div class="export-section export-mode-toggle">
|
||||
<label class="checkbox-label">
|
||||
<input type="checkbox" id="converter-advanced-toggle">
|
||||
<span class="checkmark"></span>
|
||||
Show Advanced Options
|
||||
</label>
|
||||
<small class="export-help">Tool-specific quality, compression, and conversion settings</small>
|
||||
</div>
|
||||
|
||||
<!-- Advanced Converter Options (hidden by default) -->
|
||||
<div id="converter-advanced-options" class="advanced-options hidden">
|
||||
<!-- ImageMagick Advanced Options -->
|
||||
<div class="converter-options imagemagick-options hidden">
|
||||
<div class="export-section">
|
||||
<label for="imagemagick-quality">Quality (1-100):</label>
|
||||
<input type="range" id="imagemagick-quality" min="1" max="100" value="85">
|
||||
<span id="imagemagick-quality-value">85</span>
|
||||
</div>
|
||||
|
||||
<div class="export-section">
|
||||
<label for="imagemagick-dpi">Resolution (DPI):</label>
|
||||
<input type="number" id="imagemagick-dpi" min="72" max="600" value="300" placeholder="300">
|
||||
<small>Higher DPI = better quality, larger file size</small>
|
||||
</div>
|
||||
|
||||
<div class="export-section">
|
||||
<label for="imagemagick-resize">Resize (WxH):</label>
|
||||
<input type="text" id="imagemagick-resize" placeholder="e.g., 1920x1080 or 50%">
|
||||
<small>Examples: 1920x1080, 800x600, 50% (leave empty for no resize)</small>
|
||||
</div>
|
||||
|
||||
<div class="export-section">
|
||||
<label for="imagemagick-compression">Compression Type:</label>
|
||||
<select id="imagemagick-compression">
|
||||
<option value="">None (Default)</option>
|
||||
<option value="JPEG">JPEG</option>
|
||||
<option value="LZW">LZW</option>
|
||||
<option value="Zip">Zip</option>
|
||||
<option value="RLE">RLE</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- FFmpeg Advanced Options -->
|
||||
<div class="converter-options ffmpeg-options hidden">
|
||||
<div class="export-section">
|
||||
<label for="ffmpeg-video-codec">Video Codec:</label>
|
||||
<select id="ffmpeg-video-codec">
|
||||
<option value="">Default</option>
|
||||
<option value="libx264">H.264 (libx264)</option>
|
||||
<option value="libx265">H.265/HEVC (libx265)</option>
|
||||
<option value="libvpx-vp9">VP9 (libvpx-vp9)</option>
|
||||
<option value="copy">Copy (no re-encode)</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="export-section">
|
||||
<label for="ffmpeg-audio-codec">Audio Codec:</label>
|
||||
<select id="ffmpeg-audio-codec">
|
||||
<option value="">Default</option>
|
||||
<option value="aac">AAC</option>
|
||||
<option value="libmp3lame">MP3 (libmp3lame)</option>
|
||||
<option value="libopus">Opus (libopus)</option>
|
||||
<option value="libvorbis">Vorbis (libvorbis)</option>
|
||||
<option value="copy">Copy (no re-encode)</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="export-section">
|
||||
<label for="ffmpeg-bitrate">Video Bitrate (kbps):</label>
|
||||
<input type="number" id="ffmpeg-bitrate" min="100" max="50000" placeholder="e.g., 2000">
|
||||
<small>Higher bitrate = better quality, larger file size</small>
|
||||
</div>
|
||||
|
||||
<div class="export-section">
|
||||
<label for="ffmpeg-preset">Encoding Preset:</label>
|
||||
<select id="ffmpeg-preset">
|
||||
<option value="">Default</option>
|
||||
<option value="ultrafast">Ultra Fast</option>
|
||||
<option value="fast">Fast</option>
|
||||
<option value="medium" selected>Medium</option>
|
||||
<option value="slow">Slow (Better Quality)</option>
|
||||
<option value="veryslow">Very Slow (Best Quality)</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="export-section">
|
||||
<label for="ffmpeg-framerate">Frame Rate (fps):</label>
|
||||
<input type="number" id="ffmpeg-framerate" min="1" max="120" placeholder="e.g., 30">
|
||||
<small>Common: 24, 30, 60</small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- LibreOffice Advanced Options -->
|
||||
<div class="converter-options libreoffice-options hidden">
|
||||
<div class="export-section">
|
||||
<label for="libreoffice-quality">Export Quality:</label>
|
||||
<select id="libreoffice-quality">
|
||||
<option value="">Default</option>
|
||||
<option value="high">High</option>
|
||||
<option value="medium" selected>Medium</option>
|
||||
<option value="low">Low (Smaller file size)</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="export-section">
|
||||
<label for="libreoffice-page-range">Page Range:</label>
|
||||
<input type="text" id="libreoffice-page-range" placeholder="e.g., 1-5, 7, 9-12">
|
||||
<small>Leave empty to convert all pages</small>
|
||||
</div>
|
||||
|
||||
<div class="export-section">
|
||||
<label>
|
||||
<input type="checkbox" id="libreoffice-export-bookmarks">
|
||||
Export Bookmarks (PDF only)
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Pandoc Advanced Options -->
|
||||
<div class="converter-options pandoc-options hidden">
|
||||
<div class="export-section">
|
||||
<small>Pandoc conversions can use advanced export options from the Export menu.</small>
|
||||
<button id="pandoc-show-export-options" type="button">Open Export Options Dialog...</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="converter-progress" class="batch-progress hidden">
|
||||
<div class="progress-text">
|
||||
<span id="convertapi-status">Converting...</span>
|
||||
<span id="converter-status">Converting...</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="export-section">
|
||||
<small><strong>Installation Notes:</strong></small>
|
||||
<small>• LibreOffice: Download from <a href="https://www.libreoffice.org/download" target="_blank">libreoffice.org</a></small>
|
||||
<small>• ImageMagick: Download from <a href="https://imagemagick.org/script/download.php" target="_blank">imagemagick.org</a></small>
|
||||
<small>• FFmpeg: Download from <a href="https://ffmpeg.org/download.html" target="_blank">ffmpeg.org</a></small>
|
||||
<small>• Pandoc: Download from <a href="https://pandoc.org/installing.html" target="_blank">pandoc.org</a></small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="export-dialog-footer">
|
||||
<button id="convertapi-cancel">Cancel</button>
|
||||
<button id="convertapi-convert" class="primary">Convert</button>
|
||||
<button id="converter-cancel">Cancel</button>
|
||||
<button id="converter-convert" class="primary">Convert</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -373,6 +553,377 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- PDF Editor Dialog -->
|
||||
<div id="pdf-editor-dialog" class="export-dialog hidden">
|
||||
<div class="export-dialog-content">
|
||||
<div class="export-dialog-header">
|
||||
<h3 id="pdf-editor-title">PDF Editor</h3>
|
||||
<button id="pdf-editor-dialog-close" title="Close">×</button>
|
||||
</div>
|
||||
<div class="export-dialog-body">
|
||||
<!-- Merge PDFs Section -->
|
||||
<div id="pdf-merge-section" class="pdf-operation-section hidden">
|
||||
<div class="export-section">
|
||||
<label>PDF Files to Merge:</label>
|
||||
<div id="merge-files-list" class="file-list">
|
||||
<!-- Dynamically added file entries -->
|
||||
</div>
|
||||
<button id="add-merge-file" type="button">+ Add PDF File</button>
|
||||
<small>Files will be merged in the order shown. Drag to reorder.</small>
|
||||
</div>
|
||||
<div class="export-section">
|
||||
<label>Output File:</label>
|
||||
<div class="folder-input-group">
|
||||
<input type="text" id="merge-output-path" placeholder="Choose output location..." readonly>
|
||||
<button id="browse-merge-output">Browse</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Split PDF Section -->
|
||||
<div id="pdf-split-section" class="pdf-operation-section hidden">
|
||||
<div class="export-section">
|
||||
<label>PDF File to Split:</label>
|
||||
<div class="folder-input-group">
|
||||
<input type="text" id="split-input-path" placeholder="Select PDF file..." readonly>
|
||||
<button id="browse-split-input">Browse</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="export-section">
|
||||
<label>Split Mode:</label>
|
||||
<select id="split-mode">
|
||||
<option value="pages">By Page Range</option>
|
||||
<option value="interval">Every N Pages</option>
|
||||
<option value="size">By File Size</option>
|
||||
</select>
|
||||
</div>
|
||||
<div id="split-pages-options" class="export-section">
|
||||
<label>Page Ranges:</label>
|
||||
<input type="text" id="split-page-ranges" placeholder="e.g., 1-5, 6-10, 11-15">
|
||||
<small>Separate ranges with commas. Each range will create a new PDF.</small>
|
||||
</div>
|
||||
<div id="split-interval-options" class="export-section hidden">
|
||||
<label>Split Every:</label>
|
||||
<input type="number" id="split-interval" min="1" value="5" placeholder="5">
|
||||
<small>pages (e.g., 5 = split every 5 pages)</small>
|
||||
</div>
|
||||
<div id="split-size-options" class="export-section hidden">
|
||||
<label>Max File Size (MB):</label>
|
||||
<input type="number" id="split-size" min="1" value="5" placeholder="5">
|
||||
<small>Split when file size exceeds this limit</small>
|
||||
</div>
|
||||
<div class="export-section">
|
||||
<label>Output Folder:</label>
|
||||
<div class="folder-input-group">
|
||||
<input type="text" id="split-output-folder" placeholder="Select output folder..." readonly>
|
||||
<button id="browse-split-output">Browse</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Compress PDF Section -->
|
||||
<div id="pdf-compress-section" class="pdf-operation-section hidden">
|
||||
<div class="export-section">
|
||||
<label>PDF File to Compress:</label>
|
||||
<div class="folder-input-group">
|
||||
<input type="text" id="compress-input-path" placeholder="Select PDF file..." readonly>
|
||||
<button id="browse-compress-input">Browse</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="export-section">
|
||||
<label>Compression Level:</label>
|
||||
<select id="compress-level">
|
||||
<option value="low">Low (Best Quality)</option>
|
||||
<option value="medium" selected>Medium (Balanced)</option>
|
||||
<option value="high">High (Smallest Size)</option>
|
||||
</select>
|
||||
<small>Higher compression = smaller file size, lower quality</small>
|
||||
</div>
|
||||
<div class="export-section">
|
||||
<label>Compression Options:</label>
|
||||
<div class="checkbox-group">
|
||||
<label><input type="checkbox" id="compress-images" checked> Compress Images</label>
|
||||
<label><input type="checkbox" id="compress-remove-duplicates" checked> Remove Duplicates</label>
|
||||
<label><input type="checkbox" id="compress-optimize-fonts"> Optimize Fonts</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="export-section">
|
||||
<label>Output File:</label>
|
||||
<div class="folder-input-group">
|
||||
<input type="text" id="compress-output-path" placeholder="Choose output location..." readonly>
|
||||
<button id="browse-compress-output">Browse</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Rotate Pages Section -->
|
||||
<div id="pdf-rotate-section" class="pdf-operation-section hidden">
|
||||
<div class="export-section">
|
||||
<label>PDF File:</label>
|
||||
<div class="folder-input-group">
|
||||
<input type="text" id="rotate-input-path" placeholder="Select PDF file..." readonly>
|
||||
<button id="browse-rotate-input">Browse</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="export-section">
|
||||
<label>Pages to Rotate:</label>
|
||||
<input type="text" id="rotate-pages" placeholder="e.g., 1-5, 7, 9-12 (leave empty for all pages)">
|
||||
<small>Separate page numbers/ranges with commas, or leave empty to rotate all pages</small>
|
||||
</div>
|
||||
<div class="export-section">
|
||||
<label>Rotation Angle:</label>
|
||||
<select id="rotate-angle">
|
||||
<option value="90">90° Clockwise</option>
|
||||
<option value="180">180°</option>
|
||||
<option value="270">270° Clockwise (90° Counter-clockwise)</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="export-section">
|
||||
<label>Output File:</label>
|
||||
<div class="folder-input-group">
|
||||
<input type="text" id="rotate-output-path" placeholder="Choose output location..." readonly>
|
||||
<button id="browse-rotate-output">Browse</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Delete Pages Section -->
|
||||
<div id="pdf-delete-section" class="pdf-operation-section hidden">
|
||||
<div class="export-section">
|
||||
<label>PDF File:</label>
|
||||
<div class="folder-input-group">
|
||||
<input type="text" id="delete-input-path" placeholder="Select PDF file..." readonly>
|
||||
<button id="browse-delete-input">Browse</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="export-section">
|
||||
<label>Pages to Delete:</label>
|
||||
<input type="text" id="delete-pages" placeholder="e.g., 1-5, 7, 9-12">
|
||||
<small>Separate page numbers/ranges with commas</small>
|
||||
</div>
|
||||
<div class="export-section">
|
||||
<label>Output File:</label>
|
||||
<div class="folder-input-group">
|
||||
<input type="text" id="delete-output-path" placeholder="Choose output location..." readonly>
|
||||
<button id="browse-delete-output">Browse</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Reorder Pages Section -->
|
||||
<div id="pdf-reorder-section" class="pdf-operation-section hidden">
|
||||
<div class="export-section">
|
||||
<label>PDF File:</label>
|
||||
<div class="folder-input-group">
|
||||
<input type="text" id="reorder-input-path" placeholder="Select PDF file..." readonly>
|
||||
<button id="browse-reorder-input">Browse</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="export-section">
|
||||
<label>New Page Order:</label>
|
||||
<input type="text" id="reorder-pages" placeholder="e.g., 3, 1, 2, 5, 4">
|
||||
<small>Enter page numbers in desired order, separated by commas</small>
|
||||
</div>
|
||||
<div class="export-section">
|
||||
<button id="load-current-order" type="button">Load Current Page Order</button>
|
||||
<div id="current-page-order" class="info-display hidden">
|
||||
Current order: <span id="current-order-display"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="export-section">
|
||||
<label>Output File:</label>
|
||||
<div class="folder-input-group">
|
||||
<input type="text" id="reorder-output-path" placeholder="Choose output location..." readonly>
|
||||
<button id="browse-reorder-output">Browse</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Watermark Section -->
|
||||
<div id="pdf-watermark-section" class="pdf-operation-section hidden">
|
||||
<div class="export-section">
|
||||
<label>PDF File:</label>
|
||||
<div class="folder-input-group">
|
||||
<input type="text" id="watermark-input-path" placeholder="Select PDF file..." readonly>
|
||||
<button id="browse-watermark-input">Browse</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="export-section">
|
||||
<label>Watermark Text:</label>
|
||||
<input type="text" id="watermark-text" placeholder="Enter watermark text...">
|
||||
</div>
|
||||
<div class="export-section">
|
||||
<label>Font Size:</label>
|
||||
<input type="number" id="watermark-font-size" min="8" max="144" value="48" placeholder="48">
|
||||
</div>
|
||||
<div class="export-section">
|
||||
<label>Opacity (0-100):</label>
|
||||
<input type="range" id="watermark-opacity" min="0" max="100" value="30">
|
||||
<span id="watermark-opacity-value">30</span>
|
||||
</div>
|
||||
<div class="export-section">
|
||||
<label>Position:</label>
|
||||
<select id="watermark-position">
|
||||
<option value="center">Center</option>
|
||||
<option value="diagonal">Diagonal</option>
|
||||
<option value="top-left">Top Left</option>
|
||||
<option value="top-center">Top Center</option>
|
||||
<option value="top-right">Top Right</option>
|
||||
<option value="bottom-left">Bottom Left</option>
|
||||
<option value="bottom-center">Bottom Center</option>
|
||||
<option value="bottom-right">Bottom Right</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="export-section">
|
||||
<label>Color:</label>
|
||||
<input type="color" id="watermark-color" value="#000000">
|
||||
</div>
|
||||
<div class="export-section">
|
||||
<label>Apply to:</label>
|
||||
<select id="watermark-pages">
|
||||
<option value="all">All Pages</option>
|
||||
<option value="custom">Custom Pages</option>
|
||||
</select>
|
||||
<input type="text" id="watermark-custom-pages" placeholder="e.g., 1-5, 7, 9-12" class="hidden">
|
||||
</div>
|
||||
<div class="export-section">
|
||||
<label>Output File:</label>
|
||||
<div class="folder-input-group">
|
||||
<input type="text" id="watermark-output-path" placeholder="Choose output location..." readonly>
|
||||
<button id="browse-watermark-output">Browse</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Encrypt PDF Section (Password Protection) -->
|
||||
<div id="pdf-encrypt-section" class="pdf-operation-section hidden">
|
||||
<div class="export-section">
|
||||
<label>PDF File to Protect:</label>
|
||||
<div class="folder-input-group">
|
||||
<input type="text" id="encrypt-input-path" placeholder="Select PDF file..." readonly>
|
||||
<button id="browse-encrypt-input">Browse</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="export-section">
|
||||
<label>User Password:</label>
|
||||
<input type="password" id="encrypt-user-password" placeholder="Password to open the PDF">
|
||||
<small>Required to open and view the PDF</small>
|
||||
</div>
|
||||
<div class="export-section">
|
||||
<label>Owner Password:</label>
|
||||
<input type="password" id="encrypt-owner-password" placeholder="Password to modify permissions">
|
||||
<small>Optional: Required to change document permissions</small>
|
||||
</div>
|
||||
<div class="export-section">
|
||||
<label>Encryption Level:</label>
|
||||
<select id="encrypt-level">
|
||||
<option value="128">128-bit (Standard)</option>
|
||||
<option value="256" selected>256-bit (High Security)</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="export-section">
|
||||
<label>Document Permissions:</label>
|
||||
<div class="checkbox-group">
|
||||
<label><input type="checkbox" id="encrypt-allow-printing" checked> Allow Printing</label>
|
||||
<label><input type="checkbox" id="encrypt-allow-modify" checked> Allow Content Modification</label>
|
||||
<label><input type="checkbox" id="encrypt-allow-copy" checked> Allow Content Copying</label>
|
||||
<label><input type="checkbox" id="encrypt-allow-annotate" checked> Allow Annotations</label>
|
||||
<label><input type="checkbox" id="encrypt-allow-forms" checked> Allow Form Filling</label>
|
||||
<label><input type="checkbox" id="encrypt-allow-extract" checked> Allow Accessibility (Screen Readers)</label>
|
||||
<label><input type="checkbox" id="encrypt-allow-assemble" checked> Allow Document Assembly</label>
|
||||
<label><input type="checkbox" id="encrypt-allow-print-high" checked> Allow High-Quality Printing</label>
|
||||
</div>
|
||||
<small>These permissions apply when owner password is set</small>
|
||||
</div>
|
||||
<div class="export-section">
|
||||
<label>Output File:</label>
|
||||
<div class="folder-input-group">
|
||||
<input type="text" id="encrypt-output-path" placeholder="Choose output location..." readonly>
|
||||
<button id="browse-encrypt-output">Browse</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Decrypt PDF Section (Remove Password) -->
|
||||
<div id="pdf-decrypt-section" class="pdf-operation-section hidden">
|
||||
<div class="export-section">
|
||||
<label>Protected PDF File:</label>
|
||||
<div class="folder-input-group">
|
||||
<input type="text" id="decrypt-input-path" placeholder="Select protected PDF file..." readonly>
|
||||
<button id="browse-decrypt-input">Browse</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="export-section">
|
||||
<label>Current Password:</label>
|
||||
<input type="password" id="decrypt-password" placeholder="Enter PDF password">
|
||||
<small>Enter the password to unlock and remove protection</small>
|
||||
</div>
|
||||
<div class="export-section">
|
||||
<label>Output File:</label>
|
||||
<div class="folder-input-group">
|
||||
<input type="text" id="decrypt-output-path" placeholder="Choose output location..." readonly>
|
||||
<button id="browse-decrypt-output">Browse</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Permissions Section -->
|
||||
<div id="pdf-permissions-section" class="pdf-operation-section hidden">
|
||||
<div class="export-section">
|
||||
<label>PDF File:</label>
|
||||
<div class="folder-input-group">
|
||||
<input type="text" id="permissions-input-path" placeholder="Select PDF file..." readonly>
|
||||
<button id="browse-permissions-input">Browse</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="export-section">
|
||||
<label>Current Password (if protected):</label>
|
||||
<input type="password" id="permissions-current-password" placeholder="Leave empty if not protected">
|
||||
</div>
|
||||
<div class="export-section">
|
||||
<label>New Owner Password:</label>
|
||||
<input type="password" id="permissions-owner-password" placeholder="Password to modify permissions">
|
||||
<small>Set a password required to change document permissions</small>
|
||||
</div>
|
||||
<div class="export-section">
|
||||
<label>Document Permissions:</label>
|
||||
<div class="checkbox-group">
|
||||
<label><input type="checkbox" id="permissions-allow-printing" checked> Allow Printing</label>
|
||||
<label><input type="checkbox" id="permissions-allow-modify" checked> Allow Content Modification</label>
|
||||
<label><input type="checkbox" id="permissions-allow-copy" checked> Allow Content Copying</label>
|
||||
<label><input type="checkbox" id="permissions-allow-annotate" checked> Allow Annotations</label>
|
||||
<label><input type="checkbox" id="permissions-allow-forms" checked> Allow Form Filling</label>
|
||||
<label><input type="checkbox" id="permissions-allow-extract" checked> Allow Accessibility (Screen Readers)</label>
|
||||
<label><input type="checkbox" id="permissions-allow-assemble" checked> Allow Document Assembly</label>
|
||||
<label><input type="checkbox" id="permissions-allow-print-high" checked> Allow High-Quality Printing</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="export-section">
|
||||
<label>Output File:</label>
|
||||
<div class="folder-input-group">
|
||||
<input type="text" id="permissions-output-path" placeholder="Choose output location..." readonly>
|
||||
<button id="browse-permissions-output">Browse</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Progress indicator -->
|
||||
<div id="pdf-progress" class="batch-progress hidden">
|
||||
<div class="progress-bar">
|
||||
<div class="progress-fill" id="pdf-progress-fill"></div>
|
||||
</div>
|
||||
<div class="progress-text">
|
||||
<span id="pdf-progress-text">Processing...</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="export-dialog-footer">
|
||||
<button id="pdf-editor-cancel">Cancel</button>
|
||||
<button id="pdf-editor-process" class="primary">Process</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="editor-container">
|
||||
<div class="tab-content active" id="tab-content-1" data-tab-id="1">
|
||||
<div id="editor-pane-1" class="pane">
|
||||
|
||||
+850
-48
File diff suppressed because it is too large
Load Diff
+944
-50
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,825 @@
|
||||
/* ========================================
|
||||
PanConverter v1.7.1 - Modern UI
|
||||
Glassmorphism, Gradients & Animations
|
||||
======================================== */
|
||||
|
||||
/* Modern Color Palette */
|
||||
:root {
|
||||
/* Primary Brand Colors - Purple-Blue Gradient */
|
||||
--primary-gradient: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
--primary-light: #8b9aff;
|
||||
--primary-dark: #5661b3;
|
||||
|
||||
/* Accent Colors */
|
||||
--accent-green: #10b981;
|
||||
--accent-blue: #3b82f6;
|
||||
--accent-purple: #8b5cf6;
|
||||
--accent-pink: #ec4899;
|
||||
|
||||
/* Neutral Colors */
|
||||
--gray-50: #f9fafb;
|
||||
--gray-100: #f3f4f6;
|
||||
--gray-200: #e5e7eb;
|
||||
--gray-300: #d1d5db;
|
||||
--gray-400: #9ca3af;
|
||||
--gray-500: #6b7280;
|
||||
--gray-600: #4b5563;
|
||||
--gray-700: #374151;
|
||||
--gray-800: #1f2937;
|
||||
--gray-900: #111827;
|
||||
|
||||
/* Glassmorphism */
|
||||
--glass-bg: rgba(255, 255, 255, 0.7);
|
||||
--glass-border: rgba(255, 255, 255, 0.18);
|
||||
--glass-shadow: 0 8px 32px 0 rgba(31, 38, 135, 0.15);
|
||||
|
||||
/* Effects */
|
||||
--blur-amount: 10px;
|
||||
--transition-speed: 0.3s;
|
||||
--transition-ease: cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
/* Reset & Base */
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
|
||||
overflow: hidden;
|
||||
height: 100vh;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 50%, #f093fb 100%);
|
||||
background-size: 200% 200%;
|
||||
animation: gradientShift 15s ease infinite;
|
||||
color: var(--gray-900);
|
||||
}
|
||||
|
||||
@keyframes gradientShift {
|
||||
0% { background-position: 0% 50%; }
|
||||
50% { background-position: 100% 50%; }
|
||||
100% { background-position: 0% 50%; }
|
||||
}
|
||||
|
||||
.container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100vh;
|
||||
padding: 12px;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
/* Glass Container for main content */
|
||||
.main-content-glass {
|
||||
background: var(--glass-bg);
|
||||
backdrop-filter: blur(var(--blur-amount));
|
||||
-webkit-backdrop-filter: blur(var(--blur-amount));
|
||||
border: 1px solid var(--glass-border);
|
||||
border-radius: 20px;
|
||||
box-shadow: var(--glass-shadow);
|
||||
overflow: hidden;
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
/* Tab Bar - Modern Glassmorphism */
|
||||
.tab-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background: rgba(255, 255, 255, 0.5);
|
||||
backdrop-filter: blur(10px);
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.2);
|
||||
padding: 8px 12px;
|
||||
min-height: 48px;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.tab {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 8px 16px;
|
||||
background: rgba(255, 255, 255, 0.3);
|
||||
backdrop-filter: blur(8px);
|
||||
border: 1px solid rgba(255, 255, 255, 0.3);
|
||||
border-radius: 12px;
|
||||
margin-right: 4px;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
max-width: 200px;
|
||||
min-width: 120px;
|
||||
transition: all var(--transition-speed) var(--transition-ease);
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.tab:hover {
|
||||
background: rgba(255, 255, 255, 0.5);
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.tab.active {
|
||||
background: linear-gradient(135deg, rgba(102, 126, 234, 0.9), rgba(118, 75, 162, 0.9));
|
||||
color: white;
|
||||
border-color: rgba(255, 255, 255, 0.5);
|
||||
box-shadow: 0 6px 20px rgba(102, 126, 234, 0.4);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.tab-title {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.tab-close {
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
border: none;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
cursor: pointer;
|
||||
padding: 4px;
|
||||
margin-left: 8px;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 6px;
|
||||
color: currentColor;
|
||||
transition: all var(--transition-speed) var(--transition-ease);
|
||||
}
|
||||
|
||||
.tab-close:hover {
|
||||
background: rgba(255, 255, 255, 0.4);
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
.new-tab-button {
|
||||
background: rgba(255, 255, 255, 0.3);
|
||||
backdrop-filter: blur(8px);
|
||||
border: 1px solid rgba(255, 255, 255, 0.3);
|
||||
border-radius: 10px;
|
||||
padding: 8px 16px;
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: var(--gray-700);
|
||||
transition: all var(--transition-speed) var(--transition-ease);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.new-tab-button:hover {
|
||||
background: rgba(255, 255, 255, 0.5);
|
||||
transform: scale(1.05);
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
/* Toolbar - Modern Gradient Buttons */
|
||||
.toolbar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 10px 16px;
|
||||
background: rgba(255, 255, 255, 0.4);
|
||||
backdrop-filter: blur(10px);
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.2);
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.toolbar button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.3);
|
||||
background: rgba(255, 255, 255, 0.3);
|
||||
backdrop-filter: blur(8px);
|
||||
border-radius: 10px;
|
||||
cursor: pointer;
|
||||
transition: all var(--transition-speed) var(--transition-ease);
|
||||
color: var(--gray-700);
|
||||
}
|
||||
|
||||
.toolbar button:hover {
|
||||
background: var(--primary-gradient);
|
||||
border-color: transparent;
|
||||
color: white;
|
||||
transform: translateY(-2px) scale(1.05);
|
||||
box-shadow: 0 6px 16px rgba(102, 126, 234, 0.3);
|
||||
}
|
||||
|
||||
.toolbar button:active {
|
||||
transform: translateY(0) scale(0.98);
|
||||
}
|
||||
|
||||
.toolbar-separator {
|
||||
width: 1px;
|
||||
height: 24px;
|
||||
background: linear-gradient(180deg, transparent, rgba(255, 255, 255, 0.4), transparent);
|
||||
margin: 0 8px;
|
||||
}
|
||||
|
||||
/* Tab Content */
|
||||
.tab-content {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.tab-content:not(.active) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Editor Container */
|
||||
.editor-container {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
background: rgba(255, 255, 255, 0.3);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 0 0 20px 20px;
|
||||
}
|
||||
|
||||
.pane {
|
||||
flex: 1;
|
||||
overflow: auto;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.pane:first-child {
|
||||
border-right: 1px solid rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
|
||||
/* Custom Scrollbar */
|
||||
.pane::-webkit-scrollbar,
|
||||
.editor-textarea::-webkit-scrollbar {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
}
|
||||
|
||||
.pane::-webkit-scrollbar-track,
|
||||
.editor-textarea::-webkit-scrollbar-track {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.pane::-webkit-scrollbar-thumb,
|
||||
.editor-textarea::-webkit-scrollbar-thumb {
|
||||
background: var(--primary-gradient);
|
||||
border-radius: 10px;
|
||||
border: 2px solid rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
.pane::-webkit-scrollbar-thumb:hover,
|
||||
.editor-textarea::-webkit-scrollbar-thumb:hover {
|
||||
background: linear-gradient(135deg, #8b9aff, #9d6fd8);
|
||||
}
|
||||
|
||||
.editor-textarea {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 24px;
|
||||
font-family: 'JetBrains Mono', 'Fira Code', 'SF Mono', Monaco, 'Courier New', monospace;
|
||||
font-size: 15px;
|
||||
line-height: 1.7;
|
||||
border: none;
|
||||
outline: none;
|
||||
resize: none;
|
||||
background: transparent;
|
||||
color: var(--gray-900);
|
||||
}
|
||||
|
||||
.pane:last-child {
|
||||
padding: 0;
|
||||
background: rgba(255, 255, 255, 0.5);
|
||||
backdrop-filter: blur(15px);
|
||||
}
|
||||
|
||||
.preview-content {
|
||||
max-width: none;
|
||||
margin: 0;
|
||||
padding: 24px 32px;
|
||||
line-height: 1.8;
|
||||
font-size: 15px;
|
||||
color: var(--gray-900);
|
||||
}
|
||||
|
||||
/* Status Bar - Glassmorphism */
|
||||
.status-bar {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 8px 20px;
|
||||
background: rgba(255, 255, 255, 0.5);
|
||||
backdrop-filter: blur(10px);
|
||||
border-top: 1px solid rgba(255, 255, 255, 0.2);
|
||||
border-radius: 0 0 20px 20px;
|
||||
font-size: 12px;
|
||||
color: var(--gray-600);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* Preview Styles - Modern Typography */
|
||||
.preview-content h1, .preview-content h2, .preview-content h3 {
|
||||
background: var(--primary-gradient);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
background-clip: text;
|
||||
font-weight: 700;
|
||||
margin-top: 28px;
|
||||
margin-bottom: 16px;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
.preview-content h1 {
|
||||
font-size: 2.5em;
|
||||
border-bottom: 3px solid;
|
||||
border-image: var(--primary-gradient) 1;
|
||||
padding-bottom: 12px;
|
||||
}
|
||||
|
||||
.preview-content h2 {
|
||||
font-size: 2em;
|
||||
border-bottom: 2px solid rgba(102, 126, 234, 0.3);
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
.preview-content p {
|
||||
margin-bottom: 18px;
|
||||
line-height: 1.8;
|
||||
}
|
||||
|
||||
.preview-content code {
|
||||
padding: 3px 8px;
|
||||
margin: 0 2px;
|
||||
font-size: 90%;
|
||||
background: linear-gradient(135deg, rgba(102, 126, 234, 0.1), rgba(118, 75, 162, 0.1));
|
||||
border: 1px solid rgba(102, 126, 234, 0.2);
|
||||
border-radius: 6px;
|
||||
font-family: 'JetBrains Mono', 'Fira Code', monospace;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.preview-content pre {
|
||||
padding: 20px;
|
||||
overflow-x: auto;
|
||||
font-size: 14px;
|
||||
line-height: 1.6;
|
||||
background: rgba(255, 255, 255, 0.6);
|
||||
backdrop-filter: blur(10px);
|
||||
border: 1px solid rgba(102, 126, 234, 0.2);
|
||||
border-radius: 12px;
|
||||
margin-bottom: 20px;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.preview-content pre code {
|
||||
background: transparent;
|
||||
border: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.preview-content blockquote {
|
||||
padding: 16px 20px;
|
||||
color: var(--gray-700);
|
||||
border-left: 4px solid;
|
||||
border-image: var(--primary-gradient) 1;
|
||||
margin-bottom: 20px;
|
||||
background: rgba(255, 255, 255, 0.4);
|
||||
backdrop-filter: blur(8px);
|
||||
border-radius: 0 8px 8px 0;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.preview-content a {
|
||||
color: #667eea;
|
||||
text-decoration: none;
|
||||
font-weight: 600;
|
||||
position: relative;
|
||||
transition: color var(--transition-speed);
|
||||
}
|
||||
|
||||
.preview-content a::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 0;
|
||||
bottom: -2px;
|
||||
width: 0;
|
||||
height: 2px;
|
||||
background: var(--primary-gradient);
|
||||
transition: width var(--transition-speed) var(--transition-ease);
|
||||
}
|
||||
|
||||
.preview-content a:hover {
|
||||
color: #764ba2;
|
||||
}
|
||||
|
||||
.preview-content a:hover::after {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.preview-content table {
|
||||
border-collapse: separate;
|
||||
border-spacing: 0;
|
||||
margin-bottom: 20px;
|
||||
width: 100%;
|
||||
border-radius: 12px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.preview-content table th,
|
||||
.preview-content table td {
|
||||
padding: 12px 16px;
|
||||
border: 1px solid rgba(102, 126, 234, 0.2);
|
||||
}
|
||||
|
||||
.preview-content table th {
|
||||
font-weight: 600;
|
||||
background: var(--primary-gradient);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.preview-content table tr:nth-child(even) {
|
||||
background: rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
|
||||
/* Modern Dialogs */
|
||||
.export-dialog,
|
||||
.batch-dialog,
|
||||
.find-dialog-overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba(0, 0, 0, 0.4);
|
||||
backdrop-filter: blur(8px);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 1000;
|
||||
animation: fadeIn 0.3s var(--transition-ease);
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.export-dialog.hidden,
|
||||
.batch-dialog.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.export-dialog-content,
|
||||
.batch-dialog-content {
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
backdrop-filter: blur(20px);
|
||||
border: 1px solid rgba(255, 255, 255, 0.3);
|
||||
border-radius: 20px;
|
||||
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
|
||||
width: 90%;
|
||||
max-width: 650px;
|
||||
max-height: 90vh;
|
||||
overflow-y: auto;
|
||||
animation: slideUpFade 0.4s var(--transition-ease);
|
||||
}
|
||||
|
||||
@keyframes slideUpFade {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(30px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
.export-dialog-header,
|
||||
.batch-dialog-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 20px 24px;
|
||||
border-bottom: 1px solid rgba(102, 126, 234, 0.2);
|
||||
background: var(--primary-gradient);
|
||||
border-radius: 20px 20px 0 0;
|
||||
}
|
||||
|
||||
.export-dialog-header h3,
|
||||
.batch-dialog-header h3 {
|
||||
margin: 0;
|
||||
font-size: 20px;
|
||||
font-weight: 700;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.export-dialog-body,
|
||||
.batch-dialog-body {
|
||||
padding: 24px;
|
||||
}
|
||||
|
||||
.export-section,
|
||||
.batch-section {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.export-section label,
|
||||
.batch-section label {
|
||||
display: block;
|
||||
font-weight: 600;
|
||||
margin-bottom: 8px;
|
||||
color: var(--gray-700);
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.export-section select,
|
||||
.export-section input,
|
||||
.batch-section select,
|
||||
.batch-section input {
|
||||
width: 100%;
|
||||
padding: 10px 14px;
|
||||
border: 2px solid rgba(102, 126, 234, 0.2);
|
||||
border-radius: 10px;
|
||||
font-size: 14px;
|
||||
background: rgba(255, 255, 255, 0.8);
|
||||
backdrop-filter: blur(5px);
|
||||
transition: all var(--transition-speed) var(--transition-ease);
|
||||
}
|
||||
|
||||
.export-section select:focus,
|
||||
.export-section input:focus,
|
||||
.batch-section select:focus,
|
||||
.batch-section input:focus {
|
||||
outline: none;
|
||||
border-color: #667eea;
|
||||
box-shadow: 0 0 0 4px rgba(102, 126, 234, 0.1);
|
||||
background: white;
|
||||
}
|
||||
|
||||
/* Modern Buttons */
|
||||
.export-dialog-footer,
|
||||
.batch-dialog-footer {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 12px;
|
||||
padding: 20px 24px;
|
||||
border-top: 1px solid rgba(102, 126, 234, 0.2);
|
||||
background: rgba(255, 255, 255, 0.5);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 0 0 20px 20px;
|
||||
}
|
||||
|
||||
.export-dialog-footer button,
|
||||
.batch-dialog-footer button,
|
||||
.folder-input-group button {
|
||||
padding: 10px 24px;
|
||||
border: none;
|
||||
border-radius: 10px;
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
transition: all var(--transition-speed) var(--transition-ease);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.export-dialog-footer button.primary,
|
||||
.batch-dialog-footer button.primary {
|
||||
background: var(--primary-gradient);
|
||||
color: white;
|
||||
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4);
|
||||
}
|
||||
|
||||
.export-dialog-footer button.primary:hover,
|
||||
.batch-dialog-footer button.primary:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 6px 20px rgba(102, 126, 234, 0.5);
|
||||
}
|
||||
|
||||
.export-dialog-footer button:not(.primary),
|
||||
.batch-dialog-footer button:not(.primary),
|
||||
.folder-input-group button {
|
||||
background: rgba(255, 255, 255, 0.8);
|
||||
color: var(--gray-700);
|
||||
border: 2px solid rgba(102, 126, 234, 0.2);
|
||||
}
|
||||
|
||||
.export-dialog-footer button:not(.primary):hover,
|
||||
.batch-dialog-footer button:not(.primary):hover,
|
||||
.folder-input-group button:hover {
|
||||
background: white;
|
||||
border-color: #667eea;
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
/* Find Dialog - Floating Modern */
|
||||
.find-dialog {
|
||||
position: absolute;
|
||||
top: 70px;
|
||||
right: 24px;
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
backdrop-filter: blur(20px);
|
||||
border: 1px solid rgba(255, 255, 255, 0.3);
|
||||
border-radius: 16px;
|
||||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2);
|
||||
z-index: 1000;
|
||||
min-width: 420px;
|
||||
padding: 16px;
|
||||
animation: slideInRight 0.3s var(--transition-ease);
|
||||
}
|
||||
|
||||
@keyframes slideInRight {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateX(30px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateX(0);
|
||||
}
|
||||
}
|
||||
|
||||
.find-dialog.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Progress Bars - Modern Gradient */
|
||||
.progress-bar {
|
||||
width: 100%;
|
||||
height: 8px;
|
||||
background: rgba(102, 126, 234, 0.1);
|
||||
border-radius: 10px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.progress-fill {
|
||||
height: 100%;
|
||||
background: var(--primary-gradient);
|
||||
transition: width 0.4s var(--transition-ease);
|
||||
width: 0%;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.progress-fill::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: linear-gradient(90deg,
|
||||
transparent,
|
||||
rgba(255, 255, 255, 0.3),
|
||||
transparent
|
||||
);
|
||||
animation: shimmer 2s infinite;
|
||||
}
|
||||
|
||||
@keyframes shimmer {
|
||||
0% {
|
||||
transform: translateX(-100%);
|
||||
}
|
||||
100% {
|
||||
transform: translateX(100%);
|
||||
}
|
||||
}
|
||||
|
||||
/* Auto-save Indicator - Modern */
|
||||
.auto-save-indicator {
|
||||
position: fixed;
|
||||
top: 24px;
|
||||
right: 24px;
|
||||
background: var(--primary-gradient);
|
||||
color: white;
|
||||
padding: 10px 20px;
|
||||
border-radius: 12px;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
z-index: 10000;
|
||||
box-shadow: 0 4px 20px rgba(102, 126, 234, 0.4);
|
||||
animation: slideInRight 0.3s ease-out;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.auto-save-indicator::before {
|
||||
content: '✓';
|
||||
display: inline-block;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
background: white;
|
||||
color: #667eea;
|
||||
border-radius: 50%;
|
||||
font-size: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* Enhanced Statistics - Modern */
|
||||
.enhanced-stats {
|
||||
font-size: 11px;
|
||||
color: var(--gray-600);
|
||||
padding: 2px 0;
|
||||
margin-left: 16px;
|
||||
font-family: 'JetBrains Mono', monospace;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* Line Numbers - Modern */
|
||||
.line-numbers {
|
||||
min-width: 50px;
|
||||
padding: 12px 8px;
|
||||
background: rgba(255, 255, 255, 0.3);
|
||||
backdrop-filter: blur(8px);
|
||||
border-right: 1px solid rgba(102, 126, 234, 0.2);
|
||||
font-family: 'JetBrains Mono', monospace;
|
||||
font-size: 13px;
|
||||
line-height: 1.7;
|
||||
color: var(--gray-500);
|
||||
text-align: right;
|
||||
user-select: none;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.line-numbers.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Animations & Transitions */
|
||||
.toolbar button,
|
||||
.tab,
|
||||
.new-tab-button,
|
||||
.export-dialog-footer button,
|
||||
.batch-dialog-footer button {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.toolbar button::before,
|
||||
.tab::before,
|
||||
.new-tab-button::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-radius: 50%;
|
||||
background: rgba(255, 255, 255, 0.4);
|
||||
transform: translate(-50%, -50%);
|
||||
transition: width 0.6s, height 0.6s;
|
||||
}
|
||||
|
||||
.toolbar button:hover::before,
|
||||
.tab:hover::before,
|
||||
.new-tab-button:hover::before {
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
}
|
||||
|
||||
/* Responsive Design */
|
||||
@media (max-width: 768px) {
|
||||
.container {
|
||||
padding: 8px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.export-dialog-content,
|
||||
.batch-dialog-content {
|
||||
max-width: 95%;
|
||||
}
|
||||
|
||||
.find-dialog {
|
||||
right: 12px;
|
||||
min-width: 320px;
|
||||
}
|
||||
}
|
||||
|
||||
/* Hide default elements */
|
||||
.pane.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.pane.full-width {
|
||||
border-right: none;
|
||||
}
|
||||
+1409
-4
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user