mirror of
https://github.com/amitwh/markdown-converter.git
synced 2026-08-02 18:10:18 +05:30
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
94906a068a | ||
|
|
e72b863362 |
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
Electron desktop app for Markdown editing and universal file conversion powered by Pandoc. Cross-platform (Win/macOS/Linux). Features: multi-tab editor with live preview, 25+ themes, PDF viewer/editor (merge/split/compress/rotate/watermark/password), export to 20+ formats (PDF/DOCX/ODT/EPUB/HTML/LaTeX/RTF/PPTX), batch conversion, syntax highlighting, diagram support (Mermaid), Git integration, and a plugin system.
|
Electron desktop app for Markdown editing and universal file conversion powered by Pandoc. Cross-platform (Win/macOS/Linux). Features: multi-tab editor with live preview, 25+ themes, PDF viewer/editor (merge/split/compress/rotate/watermark/password), export to 20+ formats (PDF/DOCX/ODT/EPUB/HTML/LaTeX/RTF/PPTX), batch conversion, syntax highlighting, diagram support (Mermaid), Git integration, and a plugin system.
|
||||||
|
|
||||||
- **Version:** 4.4.3
|
- **Version:** 4.4.5
|
||||||
- **License:** MIT
|
- **License:** MIT
|
||||||
- **App ID:** `com.concreteinfo.markdownconverter`
|
- **App ID:** `com.concreteinfo.markdownconverter`
|
||||||
|
|
||||||
|
|||||||
@@ -162,4 +162,4 @@ Amit Haridas (amit.wh@gmail.com)
|
|||||||
|
|
||||||
## Version
|
## Version
|
||||||
|
|
||||||
v4.4.4
|
v4.4.5
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "markdown-converter",
|
"name": "markdown-converter",
|
||||||
"version": "4.4.4",
|
"version": "4.4.5",
|
||||||
"description": "Professional Markdown editor and universal file converter with PDF editing, batch processing, and syntax highlighting",
|
"description": "Professional Markdown editor and universal file converter with PDF editing, batch processing, and syntax highlighting",
|
||||||
"main": "src/main.js",
|
"main": "src/main.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
+56
-4
@@ -2562,7 +2562,23 @@ function performExportWithOptions(format, options) {
|
|||||||
|
|
||||||
// Use pandoc for export with advanced options
|
// Use pandoc for export with advanced options
|
||||||
|
|
||||||
let pandocCmd = `${getPandocPath()} "${currentFile}" -o "${outputFile}"`;
|
let inputFile = currentFile;
|
||||||
|
let tempInputFile = null;
|
||||||
|
|
||||||
|
// Pre-process markdown for Word output so that <style> blocks, HTML
|
||||||
|
// comments, and alignment <div> tags do not appear as visible text.
|
||||||
|
if (format === 'docx') {
|
||||||
|
const content = fs.readFileSync(currentFile, 'utf-8');
|
||||||
|
const cleanedContent = WordTemplateExporter.preprocessMarkdownForWordExport(content);
|
||||||
|
tempInputFile = path.join(
|
||||||
|
require('os').tmpdir(),
|
||||||
|
`mc_export_${Date.now()}_${path.basename(currentFile)}`
|
||||||
|
);
|
||||||
|
fs.writeFileSync(tempInputFile, cleanedContent, 'utf-8');
|
||||||
|
inputFile = tempInputFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
let pandocCmd = `${getPandocPath()} "${inputFile}" -o "${outputFile}"`;
|
||||||
|
|
||||||
// Add template if specified
|
// Add template if specified
|
||||||
if (options.template && options.template !== 'default') {
|
if (options.template && options.template !== 'default') {
|
||||||
@@ -2657,7 +2673,15 @@ function performExportWithOptions(format, options) {
|
|||||||
});
|
});
|
||||||
} else if (format === 'docx') {
|
} else if (format === 'docx') {
|
||||||
pandocCmd += ' -t docx';
|
pandocCmd += ' -t docx';
|
||||||
exportWithPandoc(pandocCmd, outputFile, format);
|
exportWithPandoc(pandocCmd, outputFile, format, () => {
|
||||||
|
if (tempInputFile) {
|
||||||
|
try {
|
||||||
|
fs.unlinkSync(tempInputFile);
|
||||||
|
} catch {
|
||||||
|
// Ignore cleanup errors
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
} else if (format === 'pptx') {
|
} else if (format === 'pptx') {
|
||||||
// Add PowerPoint footer if enabled
|
// Add PowerPoint footer if enabled
|
||||||
if (headerFooterSettings.enabled && headerFooterSettings.footer.center) {
|
if (headerFooterSettings.enabled && headerFooterSettings.footer.center) {
|
||||||
@@ -2859,7 +2883,7 @@ function showExportSuccess(outputFile) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Helper function to export with pandoc (general) - uses runPandocCmd for safety
|
// Helper function to export with pandoc (general) - uses runPandocCmd for safety
|
||||||
function exportWithPandoc(pandocCmd, outputFile, format) {
|
function exportWithPandoc(pandocCmd, outputFile, format, onComplete) {
|
||||||
runPandocCmd(pandocCmd, async (error, stdout, stderr) => {
|
runPandocCmd(pandocCmd, async (error, stdout, stderr) => {
|
||||||
if (error) {
|
if (error) {
|
||||||
console.error(`Pandoc error for ${format}:`, error);
|
console.error(`Pandoc error for ${format}:`, error);
|
||||||
@@ -2924,6 +2948,9 @@ function exportWithPandoc(pandocCmd, outputFile, format) {
|
|||||||
}
|
}
|
||||||
showExportSuccess(outputFile);
|
showExportSuccess(outputFile);
|
||||||
}
|
}
|
||||||
|
if (typeof onComplete === 'function') {
|
||||||
|
onComplete(error);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3749,7 +3776,23 @@ async function performBatchConversion(inputFolder, outputFolder, format, options
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Build pandoc command for other formats
|
// Build pandoc command for other formats
|
||||||
let pandocCmd = `${getPandocPath()} "${inputFile}" -o "${outputFile}"`;
|
let pandocInputFile = inputFile;
|
||||||
|
let batchTempInputFile = null;
|
||||||
|
|
||||||
|
// Pre-process markdown for Word output so that <style> blocks, HTML
|
||||||
|
// comments, and alignment <div> tags do not appear as visible text.
|
||||||
|
if (format === 'docx') {
|
||||||
|
const content = fs.readFileSync(inputFile, 'utf-8');
|
||||||
|
const cleanedContent = WordTemplateExporter.preprocessMarkdownForWordExport(content);
|
||||||
|
batchTempInputFile = path.join(
|
||||||
|
require('os').tmpdir(),
|
||||||
|
`mc_batch_export_${Date.now()}_${path.basename(inputFile)}`
|
||||||
|
);
|
||||||
|
fs.writeFileSync(batchTempInputFile, cleanedContent, 'utf-8');
|
||||||
|
pandocInputFile = batchTempInputFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
let pandocCmd = `${getPandocPath()} "${pandocInputFile}" -o "${outputFile}"`;
|
||||||
|
|
||||||
// Add template if specified
|
// Add template if specified
|
||||||
if (options.template && options.template !== 'default') {
|
if (options.template && options.template !== 'default') {
|
||||||
@@ -3850,6 +3893,15 @@ async function performBatchConversion(inputFolder, outputFolder, format, options
|
|||||||
|
|
||||||
// Execute conversion (using runPandocCmd for safety)
|
// Execute conversion (using runPandocCmd for safety)
|
||||||
runPandocCmd(pandocCmd, async (error, _stdout, stderr) => {
|
runPandocCmd(pandocCmd, async (error, _stdout, stderr) => {
|
||||||
|
// Clean up temporary pre-processed input file
|
||||||
|
if (batchTempInputFile) {
|
||||||
|
try {
|
||||||
|
fs.unlinkSync(batchTempInputFile);
|
||||||
|
} catch {
|
||||||
|
// Ignore cleanup errors
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
console.error(`Batch: Failed to convert ${path.basename(inputFile)}:`, error.message, stderr);
|
console.error(`Batch: Failed to convert ${path.basename(inputFile)}:`, error.message, stderr);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* MarkdownConverter Renderer Process
|
* MarkdownConverter Renderer Process
|
||||||
* @version 4.4.4
|
* @version 4.4.5
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const { ipcRenderer } = require('electron');
|
const { ipcRenderer } = require('electron');
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* ModalManager - Unified modal system with accessibility support
|
* ModalManager - Unified modal system with accessibility support
|
||||||
* @version 4.4.4
|
* @version 4.4.5
|
||||||
*/
|
*/
|
||||||
class ModalManager {
|
class ModalManager {
|
||||||
#modal;
|
#modal;
|
||||||
|
|||||||
@@ -15,6 +15,26 @@ class WordTemplateExporter {
|
|||||||
this.pageSettings = pageSettings; // Page size and orientation settings
|
this.pageSettings = pageSettings; // Page size and orientation settings
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Strip HTML artifacts that Pandoc / Word cannot render and that would
|
||||||
|
* otherwise appear as visible text in DOCX output.
|
||||||
|
* Removes HTML comments, <style> blocks, and alignment <div> tags.
|
||||||
|
*/
|
||||||
|
static preprocessMarkdownForWordExport(markdown) {
|
||||||
|
if (typeof markdown !== 'string') return markdown;
|
||||||
|
return (
|
||||||
|
markdown
|
||||||
|
// HTML comments (multi-line)
|
||||||
|
.replace(/<!--[\s\S]*?-->/g, '')
|
||||||
|
// <style> blocks (case-insensitive, multi-line)
|
||||||
|
.replace(/<style\b[\s\S]*?<\/style>/gi, '')
|
||||||
|
// Opening <div align="..."> tags
|
||||||
|
.replace(/<div\b[^>]*?\balign\s*=\s*["'][^"']*["'][^>]*>/gi, '')
|
||||||
|
// Closing </div> tags
|
||||||
|
.replace(/<\/div\s*>/gi, '')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert markdown to Word document using template
|
* Convert markdown to Word document using template
|
||||||
*/
|
*/
|
||||||
@@ -33,7 +53,8 @@ class WordTemplateExporter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Parse markdown and generate Word XML
|
// Parse markdown and generate Word XML
|
||||||
const newContentXml = this.markdownToWordXml(markdownContent);
|
const cleanedContent = WordTemplateExporter.preprocessMarkdownForWordExport(markdownContent);
|
||||||
|
const newContentXml = this.markdownToWordXml(cleanedContent);
|
||||||
|
|
||||||
// Insert new content after the specified start page
|
// Insert new content after the specified start page
|
||||||
const modifiedXml = this.insertContentAfterPage(documentXml, newContentXml, this.startPage);
|
const modifiedXml = this.insertContentAfterPage(documentXml, newContentXml, this.startPage);
|
||||||
|
|||||||
@@ -16,6 +16,6 @@ describe('Project version consistency', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('README contains current version string', () => {
|
test('README contains current version string', () => {
|
||||||
expect(readme).toContain('v4.4.4');
|
expect(readme).toContain(`v${packageJson.version}`);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -0,0 +1,61 @@
|
|||||||
|
/**
|
||||||
|
* Tests for WordTemplateExporter
|
||||||
|
*/
|
||||||
|
|
||||||
|
const WordTemplateExporter = require('../src/wordTemplateExporter');
|
||||||
|
|
||||||
|
describe('WordTemplateExporter.preprocessMarkdownForWordExport', () => {
|
||||||
|
test('removes HTML style blocks', () => {
|
||||||
|
const input = `<style>
|
||||||
|
<!-- sneh-a4-print v1 -->
|
||||||
|
@media print { body { font-size: 8pt; } }
|
||||||
|
</style>
|
||||||
|
|
||||||
|
# Heading
|
||||||
|
`;
|
||||||
|
const output = WordTemplateExporter.preprocessMarkdownForWordExport(input);
|
||||||
|
expect(output).not.toContain('<style');
|
||||||
|
expect(output).not.toContain('</style>');
|
||||||
|
expect(output).not.toContain('sneh-a4-print');
|
||||||
|
expect(output).not.toContain('@media print');
|
||||||
|
expect(output).toContain('# Heading');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('removes HTML comments outside style blocks', () => {
|
||||||
|
const input = `<!-- comment -->
|
||||||
|
Hello world
|
||||||
|
`;
|
||||||
|
const output = WordTemplateExporter.preprocessMarkdownForWordExport(input);
|
||||||
|
expect(output).not.toContain('<!--');
|
||||||
|
expect(output).not.toContain('-->');
|
||||||
|
expect(output).toContain('Hello world');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('removes alignment div tags', () => {
|
||||||
|
const input = `<div align="center">
|
||||||
|
Centered content
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
const output = WordTemplateExporter.preprocessMarkdownForWordExport(input);
|
||||||
|
expect(output).not.toContain('<div align="center">');
|
||||||
|
expect(output).not.toContain('</div>');
|
||||||
|
expect(output).toContain('Centered content');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('preserves regular markdown content', () => {
|
||||||
|
const input = `# Title
|
||||||
|
|
||||||
|
| A | B |
|
||||||
|
|---|---|
|
||||||
|
| 1 | 2 |
|
||||||
|
`;
|
||||||
|
const output = WordTemplateExporter.preprocessMarkdownForWordExport(input);
|
||||||
|
expect(output).toBe(input);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('handles content with no HTML artifacts', () => {
|
||||||
|
const input = 'Plain text paragraph.';
|
||||||
|
const output = WordTemplateExporter.preprocessMarkdownForWordExport(input);
|
||||||
|
expect(output).toBe(input);
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user