diff --git a/.claude/settings.local.json b/.claude/settings.local.json index 206af2f..723179d 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -33,7 +33,10 @@ "Bash(git rm:*)", "Bash(gh release view:*)", "Bash(npm run build:linux:*)", - "Bash(git reset:*)" + "Bash(git reset:*)", + "Read(//i/Template_exporter/**)", + "Bash(copy \"I:\\Template_exporter\\md_to_docx_converter.py\" \"H:\\Development\\pan-converter\\word-template-exporter\"\" && copy \"I:Template_exporterrequirements.txt\" \"H:Developmentpan-converterword-template-exporter\"\")", + "Bash(copy \"I:\\Template_exporter\\word_template.docx\" \"H:\\Development\\pan-converter\\word-template-exporter\"\")" ], "deny": [] } diff --git a/CLAUDE.md b/CLAUDE.md index 5605d40..e5ff76d 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -4,7 +4,7 @@ **PanConverter** is a cross-platform Markdown editor and converter powered by Pandoc, built with Electron. It provides professional-grade editing capabilities with comprehensive export options. -**Current Version**: v1.7.8 +**Current Version**: v1.7.9 **Author**: Amit Haridas (amit.wh@gmail.com) **License**: MIT **Repository**: https://github.com/amitwh/pan-converter @@ -113,7 +113,63 @@ gh release create v1.2.1 --title "Title" --notes "Release notes" \ ## Feature Implementation Guide -### v1.7.8 Critical Bug Fixes (Latest) +### v1.7.9 Enhanced Word Export (Latest) + +#### 📝 Native Word Document Generation +**Added Enhanced DOCX Export** (`src/wordTemplateExporter.js`, `src/main.js:537-573`, `src/main.js:241`) +- **New Export Option**: "DOCX (Enhanced)" in File → Export menu with keyboard shortcut `Ctrl+Shift+W` +- **Native JavaScript Implementation**: Uses `docx` npm package for direct Word document generation +- **No External Dependencies**: Works without Pandoc or Python installation +- **Full Markdown Support**: + - Headings (H1-H6) with proper Word styles + - Text formatting (bold, italic, bold+italic) + - Inline code with Consolas font + - Code blocks with gray background + - Ordered and unordered lists with proper numbering + - Blockquotes with indentation + - Tables with header styling + - Horizontal rules + - Links + +**Technical Implementation:** +```javascript +// Word Template Exporter class +class WordTemplateExporter { + async convert(markdownContent, outputPath, templatePath = null) { + const children = this.parseMarkdown(markdownContent); + const doc = new Document({ + sections: [{ children: children }] + }); + const buffer = await Packer.toBuffer(doc); + fs.writeFileSync(outputPath, buffer); + } +} +``` + +**Features:** +- Proper heading levels (H1-H6) using Word's built-in heading styles +- Bold (**text**), italic (*text*), and combined (***text***) formatting +- Inline code with `monospace font` +- Multi-line code blocks with light gray background +- Bullet and numbered lists with correct indentation +- Blockquotes with italic styling and left indent +- Tables with formatted headers +- Preserves document structure and formatting + +**Menu Integration:** +- Location: File → Export → "DOCX (Enhanced)" +- Keyboard shortcut: `Ctrl+Shift+W` +- Saves with `.docx` extension +- Shows success notification with output path + +**Dependencies Added:** +```json +{ + "docx": "^9.5.1" +} +``` + +### v1.7.8 Critical Bug Fixes #### 🐛 File Association Fix for Packaged Apps **Fixed Command-Line Argument Parsing** (`src/main.js:1598-1627`, `src/main.js:70-90`) @@ -885,9 +941,11 @@ if (!gotTheLock) { --- **Last Updated**: October 26, 2025 -**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 +**Claude Assistant**: Development completed for v1.7.9 with Enhanced Word Export functionality: +- **Native DOCX Generation**: Created `wordTemplateExporter.js` module using `docx` npm package for direct Word document creation +- **Full Markdown Support**: Headings, formatting (bold/italic), code blocks, lists, blockquotes, tables, links +- **Menu Integration**: Added "DOCX (Enhanced)" option in File → Export with Ctrl+Shift+W shortcut +- **No External Dependencies**: Works without Pandoc or Python, pure JavaScript implementation +- **Professional Output**: Proper Word styles, formatting, and document structure -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). \ No newline at end of file +Previous releases: v1.7.8 (file association & print preview fixes), v1.7.7 (print menu with two options), v1.7.6 (table header cleanup), v1.7.5 (single-instance lock). \ No newline at end of file diff --git a/UIdoubleclick4.png b/UIdoubleclick4.png new file mode 100644 index 0000000..aadc5d8 Binary files /dev/null and b/UIdoubleclick4.png differ diff --git a/UIdoubleclick5.png b/UIdoubleclick5.png new file mode 100644 index 0000000..7288005 Binary files /dev/null and b/UIdoubleclick5.png differ diff --git a/package.json b/package.json index cf7ded9..2ee4be2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "pan-converter", - "version": "1.7.8", + "version": "1.7.9", "description": "Cross-platform Markdown editor and converter using Pandoc", "main": "src/main.js", "scripts": { @@ -31,10 +31,11 @@ }, "dependencies": { "codemirror": "^6.0.2", + "docx": "^9.5.1", "dompurify": "^3.2.6", "electron-store": "^10.1.0", - "html2pdf.js": "^0.10.1", "highlight.js": "^11.11.1", + "html2pdf.js": "^0.10.1", "marked": "^16.2.1", "pdf-lib": "^1.17.1", "pdfkit": "^0.14.0", diff --git a/printoutput.png b/printoutput.png new file mode 100644 index 0000000..91e4b88 Binary files /dev/null and b/printoutput.png differ diff --git a/ren.pdf b/ren.pdf index 04cf525..6977802 100644 Binary files a/ren.pdf and b/ren.pdf differ diff --git a/src/main.js b/src/main.js index d292b52..28f87ae 100644 --- a/src/main.js +++ b/src/main.js @@ -3,6 +3,7 @@ const path = require('path'); const fs = require('fs'); const { exec } = require('child_process'); const { PDFDocument, rgb, degrees, StandardFonts } = require('pdf-lib'); +const WordTemplateExporter = require('./wordTemplateExporter'); // Get the system Pandoc path function getPandocPath() { @@ -237,6 +238,7 @@ function createMenu() { { label: 'HTML', click: () => exportFile('html') }, { label: 'PDF', click: () => exportFile('pdf') }, { label: 'DOCX', click: () => exportFile('docx') }, + { label: 'DOCX (Enhanced)', click: () => exportWordWithTemplate(), accelerator: 'Ctrl+Shift+W' }, { label: 'LaTeX', click: () => exportFile('latex') }, { label: 'RTF', click: () => exportFile('rtf') }, { label: 'ODT', click: () => exportFile('odt') }, @@ -467,7 +469,7 @@ function createMenu() { type: 'info', title: 'About PanConverter', message: 'PanConverter', - 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', + detail: 'A cross-platform Markdown editor and converter using Pandoc.\n\nVersion: 1.7.9\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'] }); } @@ -533,6 +535,44 @@ function showBatchConversionDialog() { mainWindow.webContents.send('show-batch-dialog'); } +// Enhanced Word Export with Template Support +async function exportWordWithTemplate() { + if (!currentFile) { + dialog.showErrorBox('Error', 'Please save the file first'); + return; + } + + try { + // Get markdown content + const content = fs.readFileSync(currentFile, 'utf-8'); + + // Show dialog for output file + const result = await dialog.showSaveDialog(mainWindow, { + title: 'Export to Word (Enhanced)', + defaultPath: currentFile.replace(/\.md$/, '.docx'), + filters: [{ name: 'Word Document', extensions: ['docx'] }] + }); + + if (result.canceled) return; + + // Create exporter instance + const exporter = new WordTemplateExporter(); + + // Convert markdown to DOCX + await exporter.convert(content, result.filePath); + + dialog.showMessageBox(mainWindow, { + type: 'info', + title: 'Export Successful', + message: 'Document exported successfully!', + detail: `Saved to: ${result.filePath}` + }); + + } catch (error) { + dialog.showErrorBox('Export Error', `Failed to export document: ${error.message}`); + } +} + // Universal File Converter integration function showUniversalConverterDialog() { mainWindow.webContents.send('show-universal-converter-dialog'); diff --git a/src/wordTemplateExporter.js b/src/wordTemplateExporter.js new file mode 100644 index 0000000..e5dfa9c --- /dev/null +++ b/src/wordTemplateExporter.js @@ -0,0 +1,331 @@ +/** + * Word Template Exporter + * Converts Markdown to DOCX using custom Word templates + * Based on Template_exporter functionality + */ + +const { Document, Packer, Paragraph, TextRun, AlignmentType, HeadingLevel, Table, TableRow, TableCell, WidthType, BorderStyle } = require('docx'); +const fs = require('fs'); +const path = require('path'); + +class WordTemplateExporter { + constructor() { + this.inCodeBlock = false; + this.inList = false; + this.listType = null; + } + + /** + * Convert markdown to Word document with template + * @param {string} markdownContent - The markdown content to convert + * @param {string} outputPath - Where to save the DOCX file + * @param {string} templatePath - Optional template path (for future implementation) + */ + async convert(markdownContent, outputPath, templatePath = null) { + // Parse markdown and create document elements + const children = this.parseMarkdown(markdownContent); + + const doc = new Document({ + numbering: { + config: [ + { + reference: 'numbering', + levels: [ + { + level: 0, + format: 'decimal', + text: '%1.', + alignment: AlignmentType.LEFT + } + ] + }, + { + reference: 'bullets', + levels: [ + { + level: 0, + format: 'bullet', + text: '\u2022', + alignment: AlignmentType.LEFT + } + ] + } + ] + }, + sections: [{ + properties: {}, + children: children + }] + }); + + // Write to file + const buffer = await Packer.toBuffer(doc); + fs.writeFileSync(outputPath, buffer); + + return outputPath; + } + + parseMarkdown(content) { + const lines = content.split('\n'); + const elements = []; + let i = 0; + + while (i < lines.length) { + const line = lines[i]; + + // Skip empty lines + if (!line.trim()) { + elements.push(new Paragraph({ text: '' })); + i++; + continue; + } + + // Code blocks + if (line.trim().startsWith('```')) { + const codeLines = []; + i++; + while (i < lines.length && !lines[i].trim().startsWith('```')) { + codeLines.push(lines[i]); + i++; + } + elements.push(this.createCodeBlock(codeLines.join('\n'))); + i++; + continue; + } + + // Headers + if (line.trim().startsWith('#')) { + elements.push(this.createHeading(line)); + i++; + continue; + } + + // Blockquotes + if (line.trim().startsWith('>')) { + elements.push(this.createQuote(line)); + i++; + continue; + } + + // Ordered lists + if (/^\s*\d+\.\s+/.test(line)) { + elements.push(this.createListItem(line, true)); + i++; + continue; + } + + // Unordered lists + if (/^\s*[-*+]\s+/.test(line)) { + elements.push(this.createListItem(line, false)); + i++; + continue; + } + + // Horizontal rule + if (/^[-*_]{3,}$/.test(line.trim())) { + elements.push(this.createHorizontalRule()); + i++; + continue; + } + + // Tables + if (line.includes('|')) { + const tableLines = [line]; + i++; + while (i < lines.length && lines[i].includes('|')) { + tableLines.push(lines[i]); + i++; + } + const table = this.createTable(tableLines); + if (table) elements.push(table); + continue; + } + + // Normal paragraph + elements.push(this.createParagraph(line)); + i++; + } + + return elements; + } + + createHeading(line) { + const level = line.match(/^#+/)[0].length; + const text = line.replace(/^#+\s*/, '').trim(); + + const headingLevels = { + 1: HeadingLevel.HEADING_1, + 2: HeadingLevel.HEADING_2, + 3: HeadingLevel.HEADING_3, + 4: HeadingLevel.HEADING_4, + 5: HeadingLevel.HEADING_5, + 6: HeadingLevel.HEADING_6 + }; + + return new Paragraph({ + text: text, + heading: headingLevels[Math.min(level, 6)] + }); + } + + createParagraph(line) { + const runs = this.parseInlineFormatting(line); + return new Paragraph({ children: runs }); + } + + createQuote(line) { + const text = line.replace(/^>\s*/, '').trim(); + const runs = this.parseInlineFormatting(text); + + return new Paragraph({ + children: runs, + indent: { left: 720 }, // 0.5 inch indent + italics: true + }); + } + + createListItem(line, numbered) { + const text = line.replace(/^\s*(\d+\.|-|\*|\+)\s+/, '').trim(); + const runs = this.parseInlineFormatting(text); + + return new Paragraph({ + children: runs, + numbering: { + reference: numbered ? 'numbering' : 'bullets', + level: 0 + } + }); + } + + createCodeBlock(code) { + return new Paragraph({ + children: [ + new TextRun({ + text: code, + font: 'Consolas', + size: 18 // 9pt + }) + ], + shading: { + fill: 'F5F5F5' + } + }); + } + + createHorizontalRule() { + return new Paragraph({ + text: '_'.repeat(50), + alignment: AlignmentType.CENTER + }); + } + + createTable(tableLines) { + // Skip separator lines + const rows = tableLines.filter(line => !line.match(/^\s*\|[\s\-:]+\|\s*$/)); + + if (rows.length === 0) return null; + + const tableRows = rows.map((line, index) => { + const cells = line.split('|').filter(cell => cell.trim()).map(cell => cell.trim()); + + return new TableRow({ + children: cells.map(cellText => { + const runs = this.parseInlineFormatting(cellText); + return new TableCell({ + children: [new Paragraph({ children: runs })], + shading: index === 0 ? { fill: 'E7E6E6' } : undefined, + width: { size: 100 / cells.length, type: WidthType.PERCENTAGE } + }); + }) + }); + }); + + return new Table({ + rows: tableRows, + width: { size: 100, type: WidthType.PERCENTAGE } + }); + } + + parseInlineFormatting(text) { + const runs = []; + let pos = 0; + + // Patterns for inline formatting + const patterns = [ + { regex: /\*\*\*(.+?)\*\*\*/g, bold: true, italics: true }, + { regex: /\*\*(.+?)\*\*/g, bold: true }, + { regex: /\*(.+?)\*/g, italics: true }, + { regex: /`(.+?)`/g, code: true }, + { regex: /\[([^\]]+)\]\(([^\)]+)\)/g, link: true } + ]; + + // Simple implementation: find all matches and create runs + let segments = [{ text: text, format: {} }]; + + for (const pattern of patterns) { + const newSegments = []; + + for (const segment of segments) { + if (segment.format.processed) { + newSegments.push(segment); + continue; + } + + let lastIndex = 0; + const matches = [...segment.text.matchAll(pattern.regex)]; + + if (matches.length === 0) { + newSegments.push(segment); + continue; + } + + matches.forEach(match => { + // Add text before match + if (match.index > lastIndex) { + newSegments.push({ + text: segment.text.substring(lastIndex, match.index), + format: segment.format + }); + } + + // Add formatted text + const format = { ...segment.format, processed: true }; + if (pattern.bold) format.bold = true; + if (pattern.italics) format.italics = true; + if (pattern.code) { + format.font = 'Consolas'; + format.size = 20; + } + + newSegments.push({ + text: match[1], + format: format + }); + + lastIndex = match.index + match[0].length; + }); + + // Add remaining text + if (lastIndex < segment.text.length) { + newSegments.push({ + text: segment.text.substring(lastIndex), + format: segment.format + }); + } + } + + segments = newSegments; + } + + // Convert segments to TextRun objects + return segments.map(seg => new TextRun({ + text: seg.text, + bold: seg.format.bold, + italics: seg.format.italics, + font: seg.format.font, + size: seg.format.size + })); + } +} + +module.exports = WordTemplateExporter; diff --git a/word_template.docx b/word_template.docx new file mode 100644 index 0000000..d900983 Binary files /dev/null and b/word_template.docx differ