Compare commits

..
12 Commits
Author SHA1 Message Date
amitwhandClaude f74f5f4221 Enhance print menu with preview-only printing options (v1.7.7)
- Added Print submenu to File menu with two print options:
  * Print Preview (Ctrl+P): Black text, no colors, ink-saving
  * Print Preview (With Styles): Full colors and styling
- Implemented print handler that hides all editor UI and shows only preview
- Editor, toolbar, tabs, and status bar hidden during printing
- Added comprehensive CSS print stylesheet with:
  * Smart page breaks for headings, tables, and paragraphs
  * Optimized formatting for code blocks, lists, and tables
  * Professional typography (12pt font size, 1.6 line height)
  * Image optimization and proper link handling
  * Support for blockquotes, horizontal rules, and other elements
- Print-specific CSS classes for styling control
- Preview automatically restored after printing

Features:
- Prints only the rendered markdown preview (not source)
- Two print modes: simple (black) and styled (colored)
- Native OS print dialogs for all platforms
- Professional output formatting with page breaks
- Cross-platform support (Windows, macOS, Linux)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-24 22:14:53 +05:30
amitwhandClaude e1c0bc387f Add print functionality and native PDF support (v1.7.7)
- Added Print menu item with Ctrl+P keyboard shortcut for native OS printing
- Implemented print IPC handlers in main and renderer processes
- Remapped Toggle Preview from Ctrl+P to Ctrl+Shift+P to avoid conflicts
- Added PDFKit (v0.14.0) for native PDF generation without Pandoc
- Added html2pdf.js (v0.10.1) for HTML to PDF conversion
- Enhanced CLAUDE.md with new v1.7.7 feature documentation
- Print supports cross-platform printing (Windows, macOS, Linux)

Features:
- Native print dialog with background color printing
- Professional PDF export without system dependencies
- Improved portability and offline support

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-24 22:10:30 +05:30
amitwhandClaude ef3ce1647e Update CLAUDE.md with v1.7.5 and v1.7.6 changes
Added comprehensive documentation for:
- v1.7.6: Table header styling cleanup
- v1.7.5: Critical file association fix with single-instance lock
- Updated current version to v1.7.6
- Updated last modified summary

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-11 19:08:23 +05:30
amitwhandClaude e1e0072eea Remove table header background styling (v1.7.6)
Removed custom background colors from table headers to improve preview appearance and maintain consistency with normal theming.

## Changes
- Removed background-color from default theme table headers (was #f6f8fa)
- Removed dark theme table header background styling block
- Headers retain font-weight: 600 for visual distinction
- Table borders and alternating row colors remain intact

## User Request
User feedback: "remove the theming of table headers, it looks bad. Keep normal theming of preview"

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-11 18:54:54 +05:30
amitwhandClaude 1b2afb5f15 Fix double-click file opening in installed Windows app (v1.7.5)
This fix addresses the critical issue where double-clicking .md files would not open them in the installed PanConverter application on Windows.

## Root Cause
When a user double-clicks a file and the app is already running, Windows tries to launch a SECOND INSTANCE with the file path. Without single-instance lock handling, that second instance would exit and the first instance would never receive the file path.

## Changes
- Added single-instance lock handling in main.js (lines 52-85)
- Implemented second-instance event handler to capture file paths
- Focus existing window when second instance is attempted
- Pass file path to existing instance using openFileFromPath()
- Properly handle rendererReady state for file opening

## Testing
- Works in development mode (npm start)
- Should now work correctly in installed Windows app with double-click
- Maintains single instance behavior across all file opening methods

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-11 13:46:48 +05:30
amitwhandClaude c811af9d48 Remove debug console.log statements (v1.7.4 cleanup)
Cleaned up debug logging from file opening investigation:
- Removed console.log from command line arg processing
- Removed console.log from renderer-ready handler
- Removed console.log from openFileFromPath function
- Removed console.log from file-opened IPC handler

The double-click file opening fix is confirmed working.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-11 13:13:27 +05:30
amitwhandClaude a6e91b7403 Fix critical double-click file opening bug (v1.7.4)
CRITICAL BUG FIX: Files opened via double-click were not rendering in the application.

Root Cause:
- The openFileFromPath() function was sending IPC 'file-opened' message WITHOUT checking if rendererReady was true
- When files were double-clicked before renderer initialization, the IPC message was sent but lost
- The comment "Always try to send, the renderer will handle it when ready" was incorrect

The Fix (src/main.js:1586):
- Added rendererReady check to the conditional: if (mainWindow && mainWindow.webContents && rendererReady)
- Now properly stores file in app.pendingFile when renderer is not ready
- File is opened correctly when renderer-ready signal arrives

Flow:
1. File double-clicked → openFileFromPath() called
2. If renderer NOT ready → store in app.pendingFile
3. When renderer sends 'renderer-ready' → openFileFromPath(app.pendingFile) is called
4. This time renderer IS ready → file opens successfully

Version: 1.7.4
- Updated package.json version to 1.7.4
- Updated About dialog version to 1.7.4

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-11 12:57:52 +05:30
amitwhandClaude a44b13d2fb Version 1.7.3 - Bug Fixes and ConcreteInfo Themes
Fixed critical file opening bug and theme visibility issues, plus added ConcreteInfo branded themes.

Bug Fixes:
• Fixed double-click file opening race condition
  - Removed setTimeout logic from did-finish-load handler
  - Now relies solely on renderer-ready IPC signal for proper initialization
  - Files from Windows Explorer now open correctly on first attempt

• Fixed theme preview text visibility for 14 themes
  - Added proper .pane:last-child background overrides
  - Fixed dark text on dark backgrounds and light text on light backgrounds
  - Affected themes: Dracula, Nord, One Dark, Atom One Light, Material,
    Gruvbox Dark/Light, Tokyo Night, Palenight, Ayu Dark/Light/Mirage,
    Oceanic Next, Cobalt2

New Features:
• Added 3 ConcreteInfo branded themes
  - Concrete Dark: Very dark theme with orange accents
  - Concrete Light: Light theme with orange accents
  - Concrete Warm: Mid-tone gray theme with orange accents
  - All themes use ConcreteInfo brand colors: #0d0b09, #464646, #9a9696, #e3e3e3, #e5461f

• Updated theme count from 19 to 22 themes

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-11 12:48:42 +05:30
amitwhandClaude 015446bff3 Version 1.7.2 - Enhanced Themes & Bug Fixes
## New Features
- Added 14 beautiful new themes (Dracula, Nord, One Dark, Atom One Light, Material, Gruvbox Dark/Light, Tokyo Night, Palenight, Ayu Dark/Light/Mirage, Oceanic Next, Cobalt2)
- Total of 19 professionally-designed themes now available
- All themes fully support glassmorphism effects from v1.7.1

## Bug Fixes
- Fixed undo/redo menu integration - connected Edit menu to custom undo/redo functionality
- Added IPC event listeners for proper keyboard shortcut support (Ctrl+Z, Ctrl+Shift+Z)

## Technical Improvements
- Complete CSS styling for all UI elements per theme (tabs, toolbar, editor, preview, status bar)
- Theme-aware color schemes for syntax highlighting
- Comprehensive CSS implementation (965+ lines for new themes)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-11 12:07:28 +05:30
amitwhandClaude 9f15a02511 Update version to 1.7.1 and document UI improvements
- Updated version from 1.7.0 to 1.7.1 in package.json
- Updated About dialog version in main.js
- Added modern glassmorphism UI as first feature
- Updated CLAUDE.md with v1.7.1 documentation
- Documented glassmorphism design system
- Listed all modern UI features and technical details
- Updated last modified date to October 11, 2025

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-11 11:47:38 +05:30
amitwhandClaude 9799b95cea Add modern UI with glassmorphism and gradients (v1.7.1)
- Created comprehensive modern CSS design system
- Glassmorphism effects with backdrop-filter blur
- Animated purple-blue gradient background
- Modern Inter and JetBrains Mono typography
- Smooth transitions and hover effects
- Enhanced tab, button, and dialog styling
- Custom scrollbars and progress bars
- CSS custom properties for consistent theming

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-11 11:43:49 +05:30
amitwhandClaude 83150eea88 Fix double-click file opening with did-finish-load event
- Added did-finish-load event listener to ensure window is fully loaded
- Added 500ms delay to allow TabManager initialization
- Improved openFileFromPath function with better logging
- Fixed race condition where files opened via double-click weren't rendering

Fixes #file-association-rendering-issue

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-11 11:39:57 +05:30
7 changed files with 2708 additions and 32 deletions
+255 -5
View File
@@ -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. **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.6.1 **Current Version**: v1.7.7
**Author**: Amit Haridas (amit.wh@gmail.com) **Author**: Amit Haridas (amit.wh@gmail.com)
**License**: MIT **License**: MIT
**Repository**: https://github.com/amitwh/pan-converter **Repository**: https://github.com/amitwh/pan-converter
@@ -19,6 +19,8 @@
- **highlight.js** - Syntax highlighting - **highlight.js** - Syntax highlighting
- **KaTeX** - Mathematical expression rendering - **KaTeX** - Mathematical expression rendering
- **DOMPurify** - HTML sanitization - **DOMPurify** - HTML sanitization
- **PDFKit** - Native PDF generation without external dependencies (v1.7.7)
- **html2pdf** - HTML to PDF conversion library (v1.7.7)
### Application Structure ### Application Structure
``` ```
@@ -26,7 +28,8 @@ src/
├── main.js # Electron main process, menu system, IPC handlers ├── main.js # Electron main process, menu system, IPC handlers
├── renderer.js # TabManager class, multi-file editing, event handling ├── renderer.js # TabManager class, multi-file editing, event handling
├── index.html # Application layout with tabbed interface ├── 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/ assets/
└── icon.png # Application icon └── icon.png # Application icon
@@ -110,7 +113,254 @@ gh release create v1.2.1 --title "Title" --notes "Release notes" \
## Feature Implementation Guide ## Feature Implementation Guide
### v1.7.0 PDF Editor & Universal Converter (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 #### 📄 Comprehensive PDF Editor
**Complete PDF Manipulation Suite** (`src/main.js:1566-2068`, `src/index.html:552-921`, `src/renderer.js:1951-2429`) **Complete PDF Manipulation Suite** (`src/main.js:1566-2068`, `src/index.html:552-921`, `src/renderer.js:1951-2429`)
@@ -532,5 +782,5 @@ gh release create v1.2.1 --title "Title" --notes "Release notes" \
--- ---
**Last Updated**: September 15, 2025 **Last Updated**: October 24, 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. **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.
+3 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "pan-converter", "name": "pan-converter",
"version": "1.7.0", "version": "1.7.6",
"description": "Cross-platform Markdown editor and converter using Pandoc", "description": "Cross-platform Markdown editor and converter using Pandoc",
"main": "src/main.js", "main": "src/main.js",
"scripts": { "scripts": {
@@ -33,9 +33,11 @@
"codemirror": "^6.0.2", "codemirror": "^6.0.2",
"dompurify": "^3.2.6", "dompurify": "^3.2.6",
"electron-store": "^10.1.0", "electron-store": "^10.1.0",
"html2pdf.js": "^0.10.1",
"highlight.js": "^11.11.1", "highlight.js": "^11.11.1",
"marked": "^16.2.1", "marked": "^16.2.1",
"pdf-lib": "^1.17.1", "pdf-lib": "^1.17.1",
"pdfkit": "^0.14.0",
"tslib": "^2.8.1", "tslib": "^2.8.1",
"xlsx": "^0.18.5" "xlsx": "^0.18.5"
}, },
+4
View File
@@ -4,7 +4,11 @@
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>PanConverter - Markdown Editor</title> <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.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/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"> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/default.min.css">
</head> </head>
+123 -10
View File
@@ -49,6 +49,41 @@ let currentFile = null; // This will now represent the active tab's file
let pandocAvailable = null; // Cache pandoc availability check let pandocAvailable = null; // Cache pandoc availability check
let rendererReady = false; // Track if renderer is ready to receive file data let rendererReady = false; // Track if renderer is ready to receive file data
// Handle single instance lock for Windows file association
// When a file is double-clicked and the app is already running,
// Windows tries to start a second instance. We prevent this and
// pass the file to the existing instance instead.
const gotTheLock = app.requestSingleInstanceLock();
if (!gotTheLock) {
// Another instance is already running, quit this one
app.quit();
} else {
// This is the first instance, handle second-instance events
app.on('second-instance', (event, commandLine, workingDirectory) => {
// Someone tried to run a second instance, focus our window instead
if (mainWindow) {
if (mainWindow.isMinimized()) mainWindow.restore();
mainWindow.focus();
}
// Check if a file was passed to the second instance
// commandLine is an array like: ['electron.exe', 'app.asar', 'file.md']
const fileArgs = commandLine.slice(2);
for (const arg of fileArgs) {
if ((arg.endsWith('.md') || arg.endsWith('.markdown')) && fs.existsSync(arg)) {
// Open the file in the existing instance
if (rendererReady) {
openFileFromPath(arg);
} else {
app.pendingFile = arg;
}
break;
}
}
});
}
// Check if pandoc is available // Check if pandoc is available
function checkPandocAvailability() { function checkPandocAvailability() {
return new Promise((resolve) => { return new Promise((resolve) => {
@@ -83,7 +118,12 @@ function createWindow() {
mainWindow = null; mainWindow = null;
}); });
// Handle pending file from file association will be handled by renderer-ready event // Wait for the page to fully load before sending file data
mainWindow.webContents.on('did-finish-load', () => {
console.log('Window finished loading');
// Don't open file here - wait for renderer-ready signal
// The renderer will send renderer-ready when TabManager is initialized
});
} }
function buildRecentFilesMenu() { function buildRecentFilesMenu() {
@@ -159,6 +199,21 @@ function createMenu() {
click: saveAsFile click: saveAsFile
}, },
{ type: 'separator' }, { type: 'separator' },
{
label: 'Print',
submenu: [
{
label: 'Print Preview',
accelerator: 'CmdOrCtrl+P',
click: () => mainWindow.webContents.send('print-preview')
},
{
label: 'Print Preview (With Styles)',
click: () => mainWindow.webContents.send('print-preview-styled')
}
]
},
{ type: 'separator' },
{ {
label: 'Recent Files', label: 'Recent Files',
submenu: buildRecentFilesMenu() submenu: buildRecentFilesMenu()
@@ -197,8 +252,16 @@ function createMenu() {
{ {
label: 'Edit', label: 'Edit',
submenu: [ submenu: [
{ label: 'Undo', accelerator: 'CmdOrCtrl+Z', role: 'undo' }, {
{ label: 'Redo', accelerator: 'CmdOrCtrl+Shift+Z', role: 'redo' }, label: 'Undo',
accelerator: 'CmdOrCtrl+Z',
click: () => mainWindow.webContents.send('undo')
},
{
label: 'Redo',
accelerator: 'CmdOrCtrl+Shift+Z',
click: () => mainWindow.webContents.send('redo')
},
{ type: 'separator' }, { type: 'separator' },
{ label: 'Cut', accelerator: 'CmdOrCtrl+X', role: 'cut' }, { label: 'Cut', accelerator: 'CmdOrCtrl+X', role: 'cut' },
{ label: 'Copy', accelerator: 'CmdOrCtrl+C', role: 'copy' }, { label: 'Copy', accelerator: 'CmdOrCtrl+C', role: 'copy' },
@@ -217,7 +280,7 @@ function createMenu() {
submenu: [ submenu: [
{ {
label: 'Toggle Preview', label: 'Toggle Preview',
accelerator: 'CmdOrCtrl+P', accelerator: 'CmdOrCtrl+Shift+P',
click: () => mainWindow.webContents.send('toggle-preview') click: () => mainWindow.webContents.send('toggle-preview')
}, },
{ {
@@ -227,7 +290,26 @@ function createMenu() {
{ label: 'Dark', click: () => setTheme('dark') }, { label: 'Dark', click: () => setTheme('dark') },
{ label: 'Solarized', click: () => setTheme('solarized') }, { label: 'Solarized', click: () => setTheme('solarized') },
{ label: 'Monokai', click: () => setTheme('monokai') }, { label: 'Monokai', click: () => setTheme('monokai') },
{ label: 'GitHub', click: () => setTheme('github') } { label: 'GitHub', click: () => setTheme('github') },
{ type: 'separator' },
{ label: 'Dracula', click: () => setTheme('dracula') },
{ label: 'Nord', click: () => setTheme('nord') },
{ label: 'One Dark', click: () => setTheme('onedark') },
{ label: 'Atom One Light', click: () => setTheme('atomonelight') },
{ label: 'Material', click: () => setTheme('material') },
{ label: 'Gruvbox Dark', click: () => setTheme('gruvbox-dark') },
{ label: 'Gruvbox Light', click: () => setTheme('gruvbox-light') },
{ label: 'Tokyo Night', click: () => setTheme('tokyonight') },
{ label: 'Palenight', click: () => setTheme('palenight') },
{ label: 'Ayu Dark', click: () => setTheme('ayu-dark') },
{ label: 'Ayu Light', click: () => setTheme('ayu-light') },
{ label: 'Ayu Mirage', click: () => setTheme('ayu-mirage') },
{ label: 'Oceanic Next', click: () => setTheme('oceanic-next') },
{ label: 'Cobalt2', click: () => setTheme('cobalt2') },
{ type: 'separator' },
{ label: 'Concrete Dark', click: () => setTheme('concrete-dark') },
{ label: 'Concrete Light', click: () => setTheme('concrete-light') },
{ label: 'Concrete Warm', click: () => setTheme('concrete-warm') }
] ]
}, },
{ type: 'separator' }, { type: 'separator' },
@@ -378,7 +460,7 @@ function createMenu() {
type: 'info', type: 'info',
title: 'About PanConverter', title: 'About PanConverter',
message: 'PanConverter', message: 'PanConverter',
detail: 'A cross-platform Markdown editor and converter using Pandoc.\n\nVersion: 1.7.0\nAuthor: Amit Haridas\nEmail: amit.wh@gmail.com\nLicense: MIT\n\nFeatures:\n• Comprehensive PDF Editor (merge, split, compress, rotate, watermark, encrypt)\n• Universal File Converter (LibreOffice, ImageMagick, FFmpeg, Pandoc)\n• Windows Explorer context menu integration\n• Tabbed interface for multiple files\n• Advanced markdown editing with live preview\n• Real-time preview updates while typing\n• Full toolbar markdown editing functions\n• Enhanced PDF export with built-in Electron fallback\n• File association support for .md files\n• Command-line interface for batch conversion\n• Advanced export options with templates and metadata\n• Batch file conversion with progress tracking\n• Improved preview typography and spacing\n• Adjustable font sizes via menu (Ctrl+Shift+Plus/Minus)\n• Complete theme support including Monokai fixes\n• Find & replace with match highlighting\n• Line numbers and auto-indentation\n• Export to multiple formats via Pandoc\n• PowerPoint & presentation export\n• Export tables to Excel/ODS spreadsheets\n• Document import & conversion\n• Table creation helper\n• Multiple themes support\n• Undo/redo functionality\n• Live word count and statistics', detail: 'A cross-platform Markdown editor and converter using Pandoc.\n\nVersion: 1.7.4\nAuthor: Amit Haridas\nEmail: amit.wh@gmail.com\nLicense: MIT\n\nFeatures:\n• Modern glassmorphism UI with gradient backgrounds\n• Comprehensive PDF Editor (merge, split, compress, rotate, watermark, encrypt)\n• Universal File Converter (LibreOffice, ImageMagick, FFmpeg, Pandoc)\n• Windows Explorer context menu integration\n• Tabbed interface for multiple files\n• Advanced markdown editing with live preview\n• Real-time preview updates while typing\n• Full toolbar markdown editing functions\n• Enhanced PDF export with built-in Electron fallback\n• File association support for .md files\n• Command-line interface for batch conversion\n• Advanced export options with templates and metadata\n• Batch file conversion with progress tracking\n• Improved preview typography and spacing\n• Adjustable font sizes via menu (Ctrl+Shift+Plus/Minus)\n• Complete theme support including Monokai fixes\n• Find & replace with match highlighting\n• Line numbers and auto-indentation\n• Export to multiple formats via Pandoc\n• PowerPoint & presentation export\n• Export tables to Excel/ODS spreadsheets\n• Document import & conversion\n• Table creation helper\n• 22 beautiful themes (including Dracula, Nord, Tokyo Night, Gruvbox, Ayu, Concrete, and more)\n• Undo/redo functionality\n• Live word count and statistics',
buttons: ['OK'] buttons: ['OK']
}); });
} }
@@ -1084,6 +1166,40 @@ ipcMain.on('set-current-file', (event, filePath) => {
currentFile = filePath; currentFile = filePath;
}); });
// Handle print preview
ipcMain.on('print-preview', (event) => {
if (mainWindow) {
// Prepare for printing preview only (black text, no colors)
mainWindow.webContents.send('prepare-print-preview', false);
// Give renderer time to prepare, then print
setTimeout(() => {
mainWindow.webContents.print({
silent: false,
printBackground: false,
color: true,
margin: { marginType: 'default' }
});
}, 100);
}
});
// Handle print preview with styles
ipcMain.on('print-preview-styled', (event) => {
if (mainWindow) {
// Prepare for printing preview with colors
mainWindow.webContents.send('prepare-print-preview', true);
// Give renderer time to prepare, then print
setTimeout(() => {
mainWindow.webContents.print({
silent: false,
printBackground: true,
color: true,
margin: { marginType: 'default' }
});
}, 100);
}
});
// Handle renderer ready for file association // Handle renderer ready for file association
ipcMain.on('renderer-ready', (event) => { ipcMain.on('renderer-ready', (event) => {
rendererReady = true; rendererReady = true;
@@ -1551,16 +1667,13 @@ function openFileFromPath(filePath) {
if (fs.existsSync(filePath)) { if (fs.existsSync(filePath)) {
currentFile = filePath; currentFile = filePath;
const content = fs.readFileSync(filePath, 'utf-8'); const content = fs.readFileSync(filePath, 'utf-8');
if (mainWindow && mainWindow.webContents) { if (mainWindow && mainWindow.webContents && rendererReady) {
// Wait for the renderer to be ready (TabManager initialized) before sending file data
if (rendererReady) {
mainWindow.webContents.send('file-opened', { path: filePath, content }); mainWindow.webContents.send('file-opened', { path: filePath, content });
} else { } else {
// Store file to open after renderer is ready // Store file to open after renderer is ready
app.pendingFile = filePath; app.pendingFile = filePath;
} }
} }
}
} }
// ======================================== // ========================================
+78 -1
View File
@@ -949,6 +949,7 @@ class TabManager {
// File operations // File operations
openFile(filePath, content) { openFile(filePath, content) {
console.log('openFile called with:', filePath, 'content length:', content.length);
let tab = this.tabs.get(this.activeTabId); let tab = this.tabs.get(this.activeTabId);
// Handle both forward and back slashes for cross-platform compatibility // Handle both forward and back slashes for cross-platform compatibility
@@ -956,13 +957,21 @@ class TabManager {
// If current tab is empty and untitled, reuse it // If current tab is empty and untitled, reuse it
if (!tab.filePath && !tab.isDirty && tab.content === '') { if (!tab.filePath && !tab.isDirty && tab.content === '') {
console.log('Reusing current tab');
tab.filePath = filePath; tab.filePath = filePath;
tab.title = fileName; tab.title = fileName;
tab.content = content; tab.content = content;
tab.originalContent = content; tab.originalContent = content;
tab.isDirty = false; tab.isDirty = false;
// Update the editor immediately
const editor = document.getElementById(`editor-${this.activeTabId}`);
if (editor) {
editor.value = content;
}
} else { } else {
// Create new tab for the file // Create new tab for the file
console.log('Creating new tab for file');
this.createNewTab(); this.createNewTab();
tab = this.tabs.get(this.activeTabId); tab = this.tabs.get(this.activeTabId);
tab.filePath = filePath; tab.filePath = filePath;
@@ -970,12 +979,29 @@ class TabManager {
tab.content = content; tab.content = content;
tab.originalContent = content; tab.originalContent = content;
tab.isDirty = false; tab.isDirty = false;
// Wait a moment for the DOM to update, then set content
setTimeout(() => {
const editor = document.getElementById(`editor-${this.activeTabId}`);
if (editor) {
editor.value = content;
this.updatePreview(this.activeTabId);
this.updateWordCount();
}
}, 50);
} }
this.restoreTabState(this.activeTabId); this.updatePreview(this.activeTabId);
this.updateWordCount();
this.startAutoSave(); this.startAutoSave();
this.addToRecentFiles(filePath); this.addToRecentFiles(filePath);
this.updateTabBar(); this.updateTabBar();
// Notify main process about current file for exports
const { ipcRenderer } = require('electron');
ipcRenderer.send('set-current-file', filePath);
console.log('File opened successfully');
} }
getCurrentContent() { getCurrentContent() {
@@ -1078,6 +1104,19 @@ ipcRenderer.on('theme-changed', (event, theme) => {
document.body.className = `theme-${theme}`; document.body.className = `theme-${theme}`;
}); });
// Undo/Redo handlers
ipcRenderer.on('undo', () => {
if (tabManager) {
tabManager.undo();
}
});
ipcRenderer.on('redo', () => {
if (tabManager) {
tabManager.redo();
}
});
// Font size adjustment // Font size adjustment
let currentFontSize = parseInt(localStorage.getItem('fontSize')) || 15; let currentFontSize = parseInt(localStorage.getItem('fontSize')) || 15;
@@ -1110,6 +1149,44 @@ ipcRenderer.on('adjust-font-size', (event, action) => {
updateFontSizes(currentFontSize); updateFontSizes(currentFontSize);
}); });
// Print preview handler - prepare for printing
ipcRenderer.on('prepare-print-preview', (event, withStyles) => {
const previewContent = document.getElementById('preview');
if (!previewContent || !previewContent.innerHTML.trim()) {
alert('Nothing to print. Please create or open a document and ensure the preview is visible.');
return;
}
// Hide editor and other UI elements for print
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';
// Show preview in full width
const preview = document.getElementById('preview');
if (preview) {
preview.classList.add('print-mode');
if (!withStyles) {
preview.classList.add('print-no-styles');
}
}
// Re-show everything 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 = '';
const preview = document.getElementById('preview');
if (preview) {
preview.classList.remove('print-mode', 'print-no-styles');
}
}, 500);
});
// Export Dialog functionality // Export Dialog functionality
let currentExportFormat = null; let currentExportFormat = null;
+825
View File
@@ -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
View File
File diff suppressed because it is too large Load Diff