mirror of
https://github.com/amitwh/markdown-converter.git
synced 2026-08-03 02:11:07 +05:30
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7e67667035 | ||
|
|
7783fbf622 | ||
|
|
458a387c15 | ||
|
|
72677698d2 | ||
|
|
59c6513b49 | ||
|
|
c305789428 | ||
|
|
fefb299e6d | ||
|
|
3f3bdf5637 | ||
|
|
2ab8deffeb | ||
|
|
cc471d4101 | ||
|
|
0ac92b7060 |
@@ -26,7 +26,14 @@
|
|||||||
"Bash(dir:*)",
|
"Bash(dir:*)",
|
||||||
"Bash(git merge:*)",
|
"Bash(git merge:*)",
|
||||||
"Bash(timeout:*)",
|
"Bash(timeout:*)",
|
||||||
"Bash(findstr:*)"
|
"Bash(findstr:*)",
|
||||||
|
"Bash(git stash:*)",
|
||||||
|
"Bash(npm start:*)",
|
||||||
|
"Bash(gh release delete-asset:*)",
|
||||||
|
"Bash(git rm:*)",
|
||||||
|
"Bash(gh release view:*)",
|
||||||
|
"Bash(npm run build:linux:*)",
|
||||||
|
"Bash(git reset:*)"
|
||||||
],
|
],
|
||||||
"deny": []
|
"deny": []
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"CurrentProjectSetting": null
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"ExpandedNodes": [
|
||||||
|
""
|
||||||
|
],
|
||||||
|
"SelectedNode": "\\package.json",
|
||||||
|
"PreviewInSolutionExplorer": false
|
||||||
|
}
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@@ -0,0 +1,41 @@
|
|||||||
|
{
|
||||||
|
"Version": 1,
|
||||||
|
"WorkspaceRootPath": "H:\\Development\\pan-converter\\",
|
||||||
|
"Documents": [
|
||||||
|
{
|
||||||
|
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|H:\\Development\\pan-converter\\package.json||{90A6B3A7-C1A3-4009-A288-E2FF89E96FA0}",
|
||||||
|
"RelativeMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|solutionrelative:package.json||{90A6B3A7-C1A3-4009-A288-E2FF89E96FA0}"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"DocumentGroupContainers": [
|
||||||
|
{
|
||||||
|
"Orientation": 0,
|
||||||
|
"VerticalTabListWidth": 256,
|
||||||
|
"DocumentGroups": [
|
||||||
|
{
|
||||||
|
"DockedWidth": 200,
|
||||||
|
"SelectedChildIndex": 0,
|
||||||
|
"Children": [
|
||||||
|
{
|
||||||
|
"$type": "Document",
|
||||||
|
"DocumentIndex": 0,
|
||||||
|
"Title": "package.json",
|
||||||
|
"DocumentMoniker": "H:\\Development\\pan-converter\\package.json",
|
||||||
|
"RelativeDocumentMoniker": "package.json",
|
||||||
|
"ToolTip": "H:\\Development\\pan-converter\\package.json",
|
||||||
|
"RelativeToolTip": "package.json",
|
||||||
|
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAsAAAAqAAAAAAAAAA==",
|
||||||
|
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001642|",
|
||||||
|
"WhenOpened": "2025-10-11T13:44:30.357Z",
|
||||||
|
"EditorCaption": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$type": "Bookmark",
|
||||||
|
"Name": "ST:0:0:{cce594b6-0c39-4442-ba28-10c64ac7e89f}"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
{
|
||||||
|
"Version": 1,
|
||||||
|
"WorkspaceRootPath": "H:\\Development\\pan-converter\\",
|
||||||
|
"Documents": [
|
||||||
|
{
|
||||||
|
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|H:\\Development\\pan-converter\\package.json||{90A6B3A7-C1A3-4009-A288-E2FF89E96FA0}",
|
||||||
|
"RelativeMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|solutionrelative:package.json||{90A6B3A7-C1A3-4009-A288-E2FF89E96FA0}"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"DocumentGroupContainers": [
|
||||||
|
{
|
||||||
|
"Orientation": 0,
|
||||||
|
"VerticalTabListWidth": 256,
|
||||||
|
"DocumentGroups": [
|
||||||
|
{
|
||||||
|
"DockedWidth": 200,
|
||||||
|
"SelectedChildIndex": 0,
|
||||||
|
"Children": [
|
||||||
|
{
|
||||||
|
"$type": "Document",
|
||||||
|
"DocumentIndex": 0,
|
||||||
|
"Title": "package.json",
|
||||||
|
"DocumentMoniker": "H:\\Development\\pan-converter\\package.json",
|
||||||
|
"RelativeDocumentMoniker": "package.json",
|
||||||
|
"ToolTip": "H:\\Development\\pan-converter\\package.json",
|
||||||
|
"RelativeToolTip": "package.json",
|
||||||
|
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAsAAAAqAAAAAAAAAA==",
|
||||||
|
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001642|",
|
||||||
|
"WhenOpened": "2025-10-11T13:44:30.357Z",
|
||||||
|
"EditorCaption": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$type": "Bookmark",
|
||||||
|
"Name": "ST:0:0:{cce594b6-0c39-4442-ba28-10c64ac7e89f}"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
Binary file not shown.
@@ -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.7.7
|
**Current Version**: v1.7.8
|
||||||
**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
|
||||||
@@ -113,7 +113,109 @@ gh release create v1.2.1 --title "Title" --notes "Release notes" \
|
|||||||
|
|
||||||
## Feature Implementation Guide
|
## Feature Implementation Guide
|
||||||
|
|
||||||
### v1.7.7 Print & Enhanced PDF Support (Latest)
|
### v1.7.8 Critical Bug Fixes (Latest)
|
||||||
|
|
||||||
|
#### 🐛 File Association Fix for Packaged Apps
|
||||||
|
**Fixed Command-Line Argument Parsing** (`src/main.js:1598-1627`, `src/main.js:70-90`)
|
||||||
|
- **Root Cause**: `process.argv.slice(2)` works in development but fails in packaged apps
|
||||||
|
- **Solution**: Detect if app is packaged using `app.isPackaged` and use correct slice index
|
||||||
|
- Development: `process.argv.slice(2)` - ['electron', 'app.js', 'file.md']
|
||||||
|
- Packaged: `process.argv.slice(1)` - ['PanConverter.exe', 'file.md']
|
||||||
|
- **Path Resolution**: Added proper path resolution for both absolute and relative paths
|
||||||
|
- **Comprehensive Logging**: Added detailed console logs to debug file loading issues
|
||||||
|
- **Second Instance Handler**: Also fixed second-instance handler to use same logic
|
||||||
|
|
||||||
|
**Technical Implementation:**
|
||||||
|
```javascript
|
||||||
|
// Detect packaged vs development mode
|
||||||
|
const startIndex = app.isPackaged ? 1 : 2;
|
||||||
|
const fileArgs = process.argv.slice(startIndex);
|
||||||
|
|
||||||
|
// Resolve paths properly
|
||||||
|
for (const arg of fileArgs) {
|
||||||
|
if ((arg.endsWith('.md') || arg.endsWith('.markdown'))) {
|
||||||
|
const resolvedPath = path.isAbsolute(arg) ? arg : path.resolve(process.cwd(), arg);
|
||||||
|
if (fs.existsSync(resolvedPath)) {
|
||||||
|
app.pendingFile = resolvedPath;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Fixed Issues:**
|
||||||
|
- ✅ Files now open correctly on first double-click in packaged app
|
||||||
|
- ✅ Command-line arguments properly parsed in both dev and production
|
||||||
|
- ✅ Relative and absolute paths handled correctly
|
||||||
|
- ✅ Comprehensive logging for debugging file association issues
|
||||||
|
|
||||||
|
#### 🖨️ Print Preview Fix
|
||||||
|
**Fixed Print Output to Show Preview Content** (`src/renderer.js:1178-1204`, `src/styles.css:2828-3014`)
|
||||||
|
- **Root Cause**: Manual DOM manipulation conflicted with CSS `@media print` rules
|
||||||
|
- **Solution**: Simplified handler to rely on CSS `@media print` for hiding UI elements
|
||||||
|
- **CSS Enhancements**:
|
||||||
|
- Added selectors for dynamic preview panes `[id^="preview-pane-"]` and `[id^="editor-pane-"]`
|
||||||
|
- Added `.tab-content` to ensure full-width rendering
|
||||||
|
- Added body class-based styling for no-styles mode
|
||||||
|
- **Removed Manual Hiding**: Eliminated manual `display: none` manipulation in favor of CSS
|
||||||
|
|
||||||
|
**Technical Implementation:**
|
||||||
|
```javascript
|
||||||
|
// Simplified print handler - let CSS do the work
|
||||||
|
function handlePrintPreview(withStyles) {
|
||||||
|
const activeTabId = tabManager ? tabManager.activeTabId : 1;
|
||||||
|
const previewContent = document.getElementById(`preview-${activeTabId}`);
|
||||||
|
|
||||||
|
if (!previewContent || !previewContent.innerHTML.trim()) {
|
||||||
|
alert('Nothing to print...');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add body classes for CSS styling
|
||||||
|
if (!withStyles) {
|
||||||
|
document.body.classList.add('printing-no-styles');
|
||||||
|
}
|
||||||
|
document.body.classList.add('printing');
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
ipcRenderer.send('do-print', { withStyles });
|
||||||
|
setTimeout(() => {
|
||||||
|
document.body.classList.remove('printing', 'printing-no-styles');
|
||||||
|
}, 1000);
|
||||||
|
}, 100);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**CSS Updates:**
|
||||||
|
```css
|
||||||
|
@media print {
|
||||||
|
/* Hide all UI elements */
|
||||||
|
#toolbar, #tab-bar, .editor-pane, [id^="editor-pane-"] {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Show preview in full width */
|
||||||
|
[id^="preview-"], [id^="preview-pane-"], .tab-content {
|
||||||
|
display: block !important;
|
||||||
|
width: 100% !important;
|
||||||
|
padding: 20px !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Fixed Issues:**
|
||||||
|
- ✅ Print now correctly renders preview content, not toolbar
|
||||||
|
- ✅ Both print modes work (with styles and without styles)
|
||||||
|
- ✅ Clean, professional print output with proper formatting
|
||||||
|
- ✅ Proper page breaks, typography, and layout optimization
|
||||||
|
|
||||||
|
#### 🔧 Development Cleanup
|
||||||
|
**Removed Debugging Code** (`src/main.js:120-123`)
|
||||||
|
- Removed auto-opening of DevTools
|
||||||
|
- Clean production-ready build
|
||||||
|
- Debugging logs remain for troubleshooting but DevTools disabled by default
|
||||||
|
|
||||||
|
### v1.7.7 Print & Enhanced PDF Support
|
||||||
|
|
||||||
#### 🖨️ Native Print Functionality
|
#### 🖨️ Native Print Functionality
|
||||||
**Added Print Submenu to File Menu** (`src/main.js:202-215`, `src/renderer.js:1152-1188`, `src/styles.css:2828-2994`)
|
**Added Print Submenu to File Menu** (`src/main.js:202-215`, `src/renderer.js:1152-1188`, `src/styles.css:2828-2994`)
|
||||||
@@ -782,5 +884,10 @@ if (!gotTheLock) {
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
**Last Updated**: October 24, 2025
|
**Last Updated**: October 26, 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.
|
**Claude Assistant**: Development completed for v1.7.8 with critical bug fixes:
|
||||||
|
1. **File Association Fix**: Fixed command-line argument parsing to properly detect packaged vs development mode using `app.isPackaged`, enabling files to open correctly on first double-click in packaged app
|
||||||
|
2. **Print Preview Fix**: Completely rewrote print handler to rely on CSS `@media print` rules instead of manual DOM manipulation, ensuring preview content (not toolbar) is printed
|
||||||
|
3. **Code Cleanup**: Removed auto-opening DevTools for production-ready build
|
||||||
|
|
||||||
|
Previous releases: v1.7.7 (print menu with two options, PDFKit/html2pdf integration), v1.7.6 (table header styling cleanup), v1.7.5 (single-instance lock).
|
||||||
+9
-92
@@ -1,95 +1,12 @@
|
|||||||
# Push Instructions for Pan Converter
|
# Test Double-Click File Opening
|
||||||
|
|
||||||
## Prerequisites
|
This is a test file to verify double-click functionality.
|
||||||
1. Create a new repository on GitHub: https://github.com/new
|
|
||||||
- Repository name: `pan-converter`
|
|
||||||
- Keep it empty (no README, .gitignore, or license)
|
|
||||||
- Make it public or private as desired
|
|
||||||
|
|
||||||
2. Set up authentication:
|
## Features to Test
|
||||||
- **Option A - SSH Key** (Recommended):
|
- File should load automatically
|
||||||
```bash
|
- Content should display in editor
|
||||||
# Check if you have SSH key
|
- Tab should show filename
|
||||||
ls -la ~/.ssh/id_rsa.pub
|
- Preview should render markdown
|
||||||
|
|
||||||
# If not, generate one:
|
**This text should be bold**
|
||||||
ssh-keygen -t rsa -b 4096 -C "your_email@example.com"
|
*This text should be italic*
|
||||||
|
|
||||||
# Add to GitHub: Settings > SSH and GPG keys > New SSH key
|
|
||||||
cat ~/.ssh/id_rsa.pub
|
|
||||||
```
|
|
||||||
|
|
||||||
- **Option B - Personal Access Token**:
|
|
||||||
- Go to GitHub Settings > Developer settings > Personal access tokens
|
|
||||||
- Generate new token with 'repo' scope
|
|
||||||
- Save the token securely
|
|
||||||
|
|
||||||
## Push Commands
|
|
||||||
|
|
||||||
### Using SSH (Recommended)
|
|
||||||
```bash
|
|
||||||
# Set SSH remote
|
|
||||||
git remote set-url origin git@github.com:amitwh/pan-converter.git
|
|
||||||
|
|
||||||
# Push all branches
|
|
||||||
git push -u origin master
|
|
||||||
git push origin linux
|
|
||||||
git push origin macos
|
|
||||||
git push origin windows
|
|
||||||
```
|
|
||||||
|
|
||||||
### Using HTTPS with Token
|
|
||||||
```bash
|
|
||||||
# Set HTTPS remote
|
|
||||||
git remote set-url origin https://github.com/amitwh/pan-converter.git
|
|
||||||
|
|
||||||
# Push all branches (you'll be prompted for username and token)
|
|
||||||
git push -u origin master
|
|
||||||
git push origin linux
|
|
||||||
git push origin macos
|
|
||||||
git push origin windows
|
|
||||||
|
|
||||||
# When prompted:
|
|
||||||
# Username: amitwh
|
|
||||||
# Password: [paste your Personal Access Token]
|
|
||||||
```
|
|
||||||
|
|
||||||
### Using GitHub CLI (if installed)
|
|
||||||
```bash
|
|
||||||
# Login to GitHub CLI
|
|
||||||
gh auth login
|
|
||||||
|
|
||||||
# Create repo and push
|
|
||||||
gh repo create pan-converter --public --source=. --remote=origin --push
|
|
||||||
```
|
|
||||||
|
|
||||||
## Automated Script
|
|
||||||
```bash
|
|
||||||
# Run the provided script
|
|
||||||
./push-to-github.sh
|
|
||||||
```
|
|
||||||
|
|
||||||
## Verify Success
|
|
||||||
After pushing, verify at: https://github.com/amitwh/pan-converter
|
|
||||||
|
|
||||||
You should see:
|
|
||||||
- 4 branches (master, linux, macos, windows)
|
|
||||||
- All project files
|
|
||||||
- Complete commit history
|
|
||||||
|
|
||||||
## Troubleshooting
|
|
||||||
|
|
||||||
### Authentication Failed
|
|
||||||
- For SSH: Ensure your SSH key is added to GitHub
|
|
||||||
- For HTTPS: Use Personal Access Token, not password
|
|
||||||
- Check token has 'repo' scope
|
|
||||||
|
|
||||||
### Repository Not Found
|
|
||||||
- Ensure repository exists on GitHub
|
|
||||||
- Check spelling: `pan-converter`
|
|
||||||
- Verify you're logged into the correct GitHub account
|
|
||||||
|
|
||||||
### Permission Denied
|
|
||||||
- Check repository ownership
|
|
||||||
- Ensure you have write access
|
|
||||||
- Verify authentication method is correct
|
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 27 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 41 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 190 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 24 KiB |
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "pan-converter",
|
"name": "pan-converter",
|
||||||
"version": "1.7.6",
|
"version": "1.7.8",
|
||||||
"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": {
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 170 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 25 KiB |
+80
-56
@@ -68,17 +68,24 @@ if (!gotTheLock) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check if a file was passed to the second instance
|
// Check if a file was passed to the second instance
|
||||||
// commandLine is an array like: ['electron.exe', 'app.asar', 'file.md']
|
// commandLine is an array like: ['PanConverter.exe', 'file.md']
|
||||||
const fileArgs = commandLine.slice(2);
|
console.log('[MAIN] Second instance commandLine:', JSON.stringify(commandLine));
|
||||||
|
const startIndex = app.isPackaged ? 1 : 2;
|
||||||
|
const fileArgs = commandLine.slice(startIndex);
|
||||||
|
console.log('[MAIN] Second instance file args:', fileArgs);
|
||||||
for (const arg of fileArgs) {
|
for (const arg of fileArgs) {
|
||||||
if ((arg.endsWith('.md') || arg.endsWith('.markdown')) && fs.existsSync(arg)) {
|
if ((arg.endsWith('.md') || arg.endsWith('.markdown'))) {
|
||||||
// Open the file in the existing instance
|
const resolvedPath = path.isAbsolute(arg) ? arg : path.resolve(workingDirectory, arg);
|
||||||
if (rendererReady) {
|
console.log('[MAIN] Second instance resolved path:', resolvedPath);
|
||||||
openFileFromPath(arg);
|
if (fs.existsSync(resolvedPath)) {
|
||||||
} else {
|
// Open the file in the existing instance
|
||||||
app.pendingFile = arg;
|
if (rendererReady) {
|
||||||
|
openFileFromPath(resolvedPath);
|
||||||
|
} else {
|
||||||
|
app.pendingFile = resolvedPath;
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -460,7 +467,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.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',
|
detail: 'A cross-platform Markdown editor and converter using Pandoc.\n\nVersion: 1.7.8\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']
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -784,20 +791,17 @@ function performExportWithOptions(format, options) {
|
|||||||
|
|
||||||
function tryPdfFallback(inputFile, outputFile, engines, index, options, lastError) {
|
function tryPdfFallback(inputFile, outputFile, engines, index, options, lastError) {
|
||||||
if (index >= engines.length) {
|
if (index >= engines.length) {
|
||||||
dialog.showErrorBox('PDF Export Error',
|
// All Pandoc PDF engines failed, fallback to Electron's built-in PDF export
|
||||||
`Failed to export PDF with all available engines. Please ensure you have one of the following installed:\n` +
|
console.log('All Pandoc PDF engines failed, falling back to Electron PDF export');
|
||||||
`• XeLaTeX (recommended): sudo apt-get install texlive-xetex\n` +
|
exportToPDFElectron(outputFile);
|
||||||
`• PDFLaTeX: sudo apt-get install texlive-latex-base\n` +
|
|
||||||
`• LuaLaTeX: sudo apt-get install texlive-luatex\n\n` +
|
|
||||||
`Last error: ${lastError.message}`
|
|
||||||
);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const engine = engines[index];
|
const engine = engines[index];
|
||||||
let pandocCmd = `${getPandocPath()} "${inputFile}" --pdf-engine=${engine} -V geometry:${geometry} -o "${outputFile}"`;
|
let pandocCmd = `${getPandocPath()} "${inputFile}" --pdf-engine=${engine} -o "${outputFile}"`;
|
||||||
|
|
||||||
if (options.geometry) pandocCmd += ` -V geometry:"${options.geometry}"`;
|
// Add geometry if specified
|
||||||
|
if (options.geometry) pandocCmd = pandocCmd.replace(` -o `, ` -V geometry:"${options.geometry}" -o `);
|
||||||
|
|
||||||
// Add all other options
|
// Add all other options
|
||||||
if (options.template && options.template !== 'default') {
|
if (options.template && options.template !== 'default') {
|
||||||
@@ -1157,7 +1161,7 @@ ipcMain.on('save-current-file', (event, content) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.on('get-theme', (event) => {
|
ipcMain.on('get-theme', (event) => {
|
||||||
const theme = store.get('theme', 'light');
|
const theme = store.get('theme', 'github');
|
||||||
event.reply('theme-changed', theme);
|
event.reply('theme-changed', theme);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1166,44 +1170,36 @@ ipcMain.on('set-current-file', (event, filePath) => {
|
|||||||
currentFile = filePath;
|
currentFile = filePath;
|
||||||
});
|
});
|
||||||
|
|
||||||
// Handle print preview
|
// Handle actual printing when renderer is ready
|
||||||
ipcMain.on('print-preview', (event) => {
|
ipcMain.on('do-print', (event, { withStyles }) => {
|
||||||
if (mainWindow) {
|
if (mainWindow) {
|
||||||
// Prepare for printing preview only (black text, no colors)
|
// Renderer has already hidden UI, waited 300ms, and prepared the page
|
||||||
mainWindow.webContents.send('prepare-print-preview', false);
|
// Print immediately - DOM is fully rendered
|
||||||
// Give renderer time to prepare, then print
|
mainWindow.webContents.print({
|
||||||
setTimeout(() => {
|
silent: false,
|
||||||
mainWindow.webContents.print({
|
printBackground: withStyles,
|
||||||
silent: false,
|
color: true,
|
||||||
printBackground: false,
|
margin: { marginType: 'default' }
|
||||||
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) => {
|
||||||
|
console.log('[MAIN] renderer-ready received, rendererReady was:', rendererReady);
|
||||||
|
if (mainWindow) {
|
||||||
|
mainWindow.webContents.executeJavaScript(`console.log('[MAIN->RENDERER] renderer-ready received, rendererReady was: ${rendererReady}')`);
|
||||||
|
}
|
||||||
rendererReady = true;
|
rendererReady = true;
|
||||||
|
console.log('[MAIN] app.pendingFile:', app.pendingFile);
|
||||||
|
if (mainWindow) {
|
||||||
|
mainWindow.webContents.executeJavaScript(`console.log('[MAIN->RENDERER] app.pendingFile: ${app.pendingFile}')`);
|
||||||
|
}
|
||||||
if (app.pendingFile) {
|
if (app.pendingFile) {
|
||||||
|
console.log('[MAIN] Opening pending file:', app.pendingFile);
|
||||||
|
if (mainWindow) {
|
||||||
|
mainWindow.webContents.executeJavaScript(`console.log('[MAIN->RENDERER] Opening pending file: ${app.pendingFile}')`);
|
||||||
|
}
|
||||||
openFileFromPath(app.pendingFile);
|
openFileFromPath(app.pendingFile);
|
||||||
app.pendingFile = null;
|
app.pendingFile = null;
|
||||||
}
|
}
|
||||||
@@ -1604,15 +1600,35 @@ app.whenReady().then(() => {
|
|||||||
createWindow();
|
createWindow();
|
||||||
|
|
||||||
// Handle file association on app startup
|
// Handle file association on app startup
|
||||||
// Process all command line arguments except the first two (node and script path)
|
// In packaged apps, process.argv structure is different:
|
||||||
const fileArgs = process.argv.slice(2);
|
// Development: ['electron', 'app.js', 'file.md'] - need slice(2)
|
||||||
|
// Packaged: ['PanConverter.exe', 'file.md'] - need slice(1)
|
||||||
|
// We'll check all arguments after the executable
|
||||||
|
console.log('[MAIN] Full command line args:', JSON.stringify(process.argv));
|
||||||
|
console.log('[MAIN] App is packaged:', app.isPackaged);
|
||||||
|
|
||||||
|
// Start from index 1 (skip executable) and check each argument
|
||||||
|
const startIndex = app.isPackaged ? 1 : 2;
|
||||||
|
const fileArgs = process.argv.slice(startIndex);
|
||||||
|
console.log('[MAIN] File args to process (starting from index', startIndex + '):', fileArgs);
|
||||||
|
|
||||||
for (const arg of fileArgs) {
|
for (const arg of fileArgs) {
|
||||||
if ((arg.endsWith('.md') || arg.endsWith('.markdown')) && fs.existsSync(arg)) {
|
console.log('[MAIN] Checking arg:', arg);
|
||||||
// Store the file to open after window is ready
|
if ((arg.endsWith('.md') || arg.endsWith('.markdown'))) {
|
||||||
app.pendingFile = arg;
|
// Try to resolve the path (might be relative)
|
||||||
break;
|
const resolvedPath = path.isAbsolute(arg) ? arg : path.resolve(process.cwd(), arg);
|
||||||
|
console.log('[MAIN] Resolved path:', resolvedPath);
|
||||||
|
if (fs.existsSync(resolvedPath)) {
|
||||||
|
// Store the file to open after window is ready
|
||||||
|
console.log('[MAIN] Setting pendingFile to:', resolvedPath);
|
||||||
|
app.pendingFile = resolvedPath;
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
console.log('[MAIN] File does not exist:', resolvedPath);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
console.log('[MAIN] Final app.pendingFile:', app.pendingFile);
|
||||||
});
|
});
|
||||||
|
|
||||||
app.on('window-all-closed', () => {
|
app.on('window-all-closed', () => {
|
||||||
@@ -1664,15 +1680,23 @@ app.on('open-file', (event, filePath) => {
|
|||||||
|
|
||||||
// Handle file opening from command line or file association
|
// Handle file opening from command line or file association
|
||||||
function openFileFromPath(filePath) {
|
function openFileFromPath(filePath) {
|
||||||
|
console.log('[MAIN] openFileFromPath called with:', filePath);
|
||||||
|
console.log('[MAIN] rendererReady:', rendererReady, 'mainWindow exists:', !!mainWindow);
|
||||||
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');
|
||||||
|
console.log('[MAIN] File read successfully, content length:', content.length);
|
||||||
if (mainWindow && mainWindow.webContents && rendererReady) {
|
if (mainWindow && mainWindow.webContents && rendererReady) {
|
||||||
|
// Send file immediately - renderer-ready means UI is initialized
|
||||||
|
console.log('[MAIN] Sending file-opened to renderer');
|
||||||
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
|
||||||
|
console.log('[MAIN] Storing as pending file');
|
||||||
app.pendingFile = filePath;
|
app.pendingFile = filePath;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
console.error('[MAIN] File does not exist:', filePath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+49
-33
@@ -964,10 +964,13 @@ class TabManager {
|
|||||||
tab.originalContent = content;
|
tab.originalContent = content;
|
||||||
tab.isDirty = false;
|
tab.isDirty = false;
|
||||||
|
|
||||||
// Update the editor immediately
|
// Update the editor and preview
|
||||||
const editor = document.getElementById(`editor-${this.activeTabId}`);
|
const editor = document.getElementById(`editor-${this.activeTabId}`);
|
||||||
if (editor) {
|
if (editor) {
|
||||||
editor.value = content;
|
editor.value = content;
|
||||||
|
// Update preview after editor is updated
|
||||||
|
this.updatePreview(this.activeTabId);
|
||||||
|
this.updateWordCount();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Create new tab for the file
|
// Create new tab for the file
|
||||||
@@ -990,9 +993,6 @@ class TabManager {
|
|||||||
}
|
}
|
||||||
}, 50);
|
}, 50);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.updatePreview(this.activeTabId);
|
|
||||||
this.updateWordCount();
|
|
||||||
this.startAutoSave();
|
this.startAutoSave();
|
||||||
this.addToRecentFiles(filePath);
|
this.addToRecentFiles(filePath);
|
||||||
this.updateTabBar();
|
this.updateTabBar();
|
||||||
@@ -1042,8 +1042,12 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
// Request current theme
|
// Request current theme
|
||||||
ipcRenderer.send('get-theme');
|
ipcRenderer.send('get-theme');
|
||||||
|
|
||||||
// Signal that renderer is ready for file operations
|
// Also send renderer-ready immediately as backup
|
||||||
ipcRenderer.send('renderer-ready');
|
// This ensures we don't get stuck waiting for theme-changed
|
||||||
|
setTimeout(() => {
|
||||||
|
console.log('Backup renderer-ready timeout triggered');
|
||||||
|
ipcRenderer.send('renderer-ready');
|
||||||
|
}, 100);
|
||||||
|
|
||||||
// Set up auto-save interval
|
// Set up auto-save interval
|
||||||
setInterval(() => {
|
setInterval(() => {
|
||||||
@@ -1062,8 +1066,11 @@ ipcRenderer.on('file-new', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
ipcRenderer.on('file-opened', (event, data) => {
|
ipcRenderer.on('file-opened', (event, data) => {
|
||||||
|
console.log('[RENDERER] file-opened received:', data.path, 'content length:', data.content.length);
|
||||||
if (tabManager) {
|
if (tabManager) {
|
||||||
tabManager.openFile(data.path, data.content);
|
tabManager.openFile(data.path, data.content);
|
||||||
|
} else {
|
||||||
|
console.error('[RENDERER] tabManager not initialized!');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1101,7 +1108,15 @@ ipcRenderer.on('toggle-find', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
ipcRenderer.on('theme-changed', (event, theme) => {
|
ipcRenderer.on('theme-changed', (event, theme) => {
|
||||||
|
console.log('[RENDERER] Theme changed to:', theme);
|
||||||
document.body.className = `theme-${theme}`;
|
document.body.className = `theme-${theme}`;
|
||||||
|
|
||||||
|
// After theme is applied, wait for next frame then signal renderer is ready
|
||||||
|
// This ensures complete UI initialization before files are opened
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
console.log('[RENDERER] Sending renderer-ready from theme-changed');
|
||||||
|
ipcRenderer.send('renderer-ready');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// Undo/Redo handlers
|
// Undo/Redo handlers
|
||||||
@@ -1149,43 +1164,44 @@ ipcRenderer.on('adjust-font-size', (event, action) => {
|
|||||||
updateFontSizes(currentFontSize);
|
updateFontSizes(currentFontSize);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Print preview handler - prepare for printing
|
// Print preview request handlers - handle printing directly
|
||||||
ipcRenderer.on('prepare-print-preview', (event, withStyles) => {
|
ipcRenderer.on('print-preview', () => {
|
||||||
const previewContent = document.getElementById('preview');
|
console.log('[RENDERER] print-preview received');
|
||||||
|
handlePrintPreview(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcRenderer.on('print-preview-styled', () => {
|
||||||
|
console.log('[RENDERER] print-preview-styled received');
|
||||||
|
handlePrintPreview(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
function handlePrintPreview(withStyles) {
|
||||||
|
const activeTabId = tabManager ? tabManager.activeTabId : 1;
|
||||||
|
const previewContent = document.getElementById(`preview-${activeTabId}`);
|
||||||
|
|
||||||
if (!previewContent || !previewContent.innerHTML.trim()) {
|
if (!previewContent || !previewContent.innerHTML.trim()) {
|
||||||
alert('Nothing to print. Please create or open a document and ensure the preview is visible.');
|
alert('Nothing to print. Please create or open a document and ensure the preview is visible.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hide editor and other UI elements for print
|
// Add print style classes to body for styling control
|
||||||
document.getElementById('editor-container').style.display = 'none';
|
// The @media print CSS will handle hiding toolbar/tabs automatically
|
||||||
document.getElementById('toolbar').style.display = 'none';
|
if (!withStyles) {
|
||||||
document.getElementById('tab-bar').style.display = 'none';
|
document.body.classList.add('printing-no-styles');
|
||||||
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');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
document.body.classList.add('printing');
|
||||||
|
|
||||||
// Re-show everything after print
|
// Give browser time to apply classes before printing
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
document.getElementById('editor-container').style.display = '';
|
// Tell main process to print
|
||||||
document.getElementById('toolbar').style.display = '';
|
ipcRenderer.send('do-print', { withStyles });
|
||||||
document.getElementById('tab-bar').style.display = '';
|
|
||||||
document.getElementById('status-bar').style.display = '';
|
|
||||||
|
|
||||||
const preview = document.getElementById('preview');
|
// Restore classes after print dialog appears
|
||||||
if (preview) {
|
setTimeout(() => {
|
||||||
preview.classList.remove('print-mode', 'print-no-styles');
|
document.body.classList.remove('printing', 'printing-no-styles');
|
||||||
}
|
}, 1000);
|
||||||
}, 500);
|
}, 100);
|
||||||
});
|
}
|
||||||
|
|
||||||
// Export Dialog functionality
|
// Export Dialog functionality
|
||||||
let currentExportFormat = null;
|
let currentExportFormat = null;
|
||||||
|
|||||||
+32
-3
@@ -2833,14 +2833,19 @@ body.theme-concrete-warm .status-bar {
|
|||||||
#editor-container,
|
#editor-container,
|
||||||
.editor-container,
|
.editor-container,
|
||||||
#status-bar,
|
#status-bar,
|
||||||
.status-bar {
|
.status-bar,
|
||||||
|
.editor-pane,
|
||||||
|
[id^="editor-pane-"] {
|
||||||
display: none !important;
|
display: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Show preview in full width */
|
/* Show preview in full width */
|
||||||
#preview,
|
#preview,
|
||||||
.preview,
|
.preview,
|
||||||
.preview-content {
|
.preview-content,
|
||||||
|
[id^="preview-"],
|
||||||
|
[id^="preview-pane-"],
|
||||||
|
.tab-content {
|
||||||
display: block !important;
|
display: block !important;
|
||||||
width: 100% !important;
|
width: 100% !important;
|
||||||
max-width: 100% !important;
|
max-width: 100% !important;
|
||||||
@@ -2848,10 +2853,12 @@ body.theme-concrete-warm .status-bar {
|
|||||||
margin: 0 !important;
|
margin: 0 !important;
|
||||||
padding: 20px !important;
|
padding: 20px !important;
|
||||||
overflow: visible !important;
|
overflow: visible !important;
|
||||||
|
position: static !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Optimize preview content for printing */
|
/* Optimize preview content for printing */
|
||||||
.preview-content {
|
.preview-content,
|
||||||
|
[id^="preview-"] {
|
||||||
line-height: 1.6;
|
line-height: 1.6;
|
||||||
font-size: 12pt;
|
font-size: 12pt;
|
||||||
color: #000 !important;
|
color: #000 !important;
|
||||||
@@ -2956,13 +2963,33 @@ body.theme-concrete-warm .status-bar {
|
|||||||
display: block !important;
|
display: block !important;
|
||||||
width: 100% !important;
|
width: 100% !important;
|
||||||
max-width: 100% !important;
|
max-width: 100% !important;
|
||||||
|
position: fixed !important;
|
||||||
|
top: 0 !important;
|
||||||
|
left: 0 !important;
|
||||||
|
z-index: 9999 !important;
|
||||||
|
background: white !important;
|
||||||
|
padding: 20px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Ensure preview panes are visible in print mode */
|
||||||
|
#preview-pane-1, #preview-pane-2, #preview-pane-3, #preview-pane-4, #preview-pane-5 {
|
||||||
|
display: block !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* No-styles printing mode */
|
||||||
|
body.printing-no-styles .preview-content,
|
||||||
|
body.printing-no-styles [id^="preview-"],
|
||||||
.print-no-styles .preview-content {
|
.print-no-styles .preview-content {
|
||||||
color: #000 !important;
|
color: #000 !important;
|
||||||
background: white !important;
|
background: white !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
body.printing-no-styles .preview-content h1,
|
||||||
|
body.printing-no-styles .preview-content h2,
|
||||||
|
body.printing-no-styles .preview-content h3,
|
||||||
|
body.printing-no-styles .preview-content h4,
|
||||||
|
body.printing-no-styles .preview-content h5,
|
||||||
|
body.printing-no-styles .preview-content h6,
|
||||||
.print-no-styles .preview-content h1,
|
.print-no-styles .preview-content h1,
|
||||||
.print-no-styles .preview-content h2,
|
.print-no-styles .preview-content h2,
|
||||||
.print-no-styles .preview-content h3,
|
.print-no-styles .preview-content h3,
|
||||||
@@ -2973,11 +3000,13 @@ body.theme-concrete-warm .status-bar {
|
|||||||
border-color: #999 !important;
|
border-color: #999 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
body.printing-no-styles .preview-content code,
|
||||||
.print-no-styles .preview-content code {
|
.print-no-styles .preview-content code {
|
||||||
background: #f5f5f5 !important;
|
background: #f5f5f5 !important;
|
||||||
color: #000 !important;
|
color: #000 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
body.printing-no-styles .preview-content pre,
|
||||||
.print-no-styles .preview-content pre {
|
.print-no-styles .preview-content pre {
|
||||||
background: #f5f5f5 !important;
|
background: #f5f5f5 !important;
|
||||||
color: #000 !important;
|
color: #000 !important;
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
# Test Double-Click File Opening
|
||||||
|
|
||||||
|
This is a test file to verify double-click functionality.
|
||||||
|
|
||||||
|
## Features to Test
|
||||||
|
- File should load automatically
|
||||||
|
- Content should display in editor
|
||||||
|
- Tab should show filename
|
||||||
|
- Preview should render markdown
|
||||||
|
|
||||||
|
**This text should be bold**
|
||||||
|
*This text should be italic*
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
# Test File Opening
|
||||||
|
|
||||||
|
This is a test file to verify that double-click file opening works correctly.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
- **Bold text**
|
||||||
|
- *Italic text*
|
||||||
|
- `Code`
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
console.log("Hello World");
|
||||||
|
```
|
||||||
|
|
||||||
|
This should appear when you double-click to open the file!
|
||||||
Reference in New Issue
Block a user