Merged in feature/update-docs (pull request #180)
Update complete pdf code for entire system * docs and code for generation of updated docs
This commit is contained in:
@@ -0,0 +1,153 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const { execSync } = require('child_process');
|
||||
const { marked } = require('marked');
|
||||
|
||||
// Custom script to handle Mermaid rendering and fix internal links
|
||||
async function convertMarkdownToPDF() {
|
||||
const inputFile = process.argv[2] || 'complete-documentation.md';
|
||||
const outputFile = inputFile.replace('.md', '.pdf');
|
||||
|
||||
console.log(`Converting ${inputFile} to PDF with Mermaid support...`);
|
||||
|
||||
try {
|
||||
// Read the markdown file
|
||||
let content = fs.readFileSync(`/app/docs/${inputFile}`, 'utf8');
|
||||
|
||||
// Remove YAML front matter if present
|
||||
content = content.replace(/^---\n[\s\S]*?\n---\n/, '');
|
||||
|
||||
// Remove emojis that don't render well in PDF
|
||||
content = content.replace(/📋|🛠️|🔧|📊|🏗️|🎉|✅|❌|🚀|📝|📚|🔍|⚠️|💡/g, '');
|
||||
|
||||
// Helper function to generate section ID from text
|
||||
function generateSectionId(text) {
|
||||
return text
|
||||
.toLowerCase()
|
||||
.replace(/[^\w\s-]/g, '') // Remove special chars
|
||||
.replace(/\s+/g, '-') // Replace spaces with hyphens
|
||||
.replace(/-+/g, '-') // Collapse multiple hyphens
|
||||
.replace(/^-|-$/g, ''); // Remove leading/trailing hyphens
|
||||
}
|
||||
|
||||
// Fix internal links to work within the same document
|
||||
// Convert [text](filename.md) to [text](#section-name)
|
||||
let linkCount = 0;
|
||||
content = content.replace(/\[([^\]]+)\]\((\d+-[^)]+\.md)\)/g, (match, linkText, filename) => {
|
||||
// Use the link text to generate the section ID (since that's what the header will be)
|
||||
const sectionId = generateSectionId(linkText);
|
||||
linkCount++;
|
||||
return `[${linkText}](#${sectionId})`;
|
||||
});
|
||||
|
||||
// Fix relative links ./filename.md
|
||||
content = content.replace(/\[([^\]]+)\]\(\.\/(\d+-[^)]+\.md)\)/g, (match, linkText, filename) => {
|
||||
const sectionId = generateSectionId(linkText);
|
||||
linkCount++;
|
||||
return `[${linkText}](#${sectionId})`;
|
||||
});
|
||||
|
||||
console.log(`✅ Processed ${linkCount} internal links`);
|
||||
|
||||
// Process Mermaid diagrams
|
||||
let mermaidCounter = 0;
|
||||
content = content.replace(/```mermaid\n([\s\S]*?)\n```/g, (match, mermaidCode) => {
|
||||
mermaidCounter++;
|
||||
const diagramFile = `/app/docs/diagram-${mermaidCounter}.mmd`;
|
||||
const imageFile = `/app/docs/diagram-${mermaidCounter}.png`;
|
||||
|
||||
// Write mermaid code to temporary file
|
||||
fs.writeFileSync(diagramFile, mermaidCode.trim());
|
||||
|
||||
try {
|
||||
// Generate PNG from mermaid
|
||||
execSync(`mmdc -i ${diagramFile} -o ${imageFile} -w 800 -H 600 --backgroundColor white`);
|
||||
|
||||
// Replace with image reference
|
||||
return ``;
|
||||
} catch (error) {
|
||||
console.warn(`Warning: Failed to render Mermaid diagram ${mermaidCounter}:`, error.message);
|
||||
// Return original code block if mermaid fails
|
||||
return match;
|
||||
}
|
||||
});
|
||||
|
||||
// Configure marked to generate proper IDs for headers
|
||||
marked.use({
|
||||
renderer: {
|
||||
heading(text, level) {
|
||||
// Generate ID from text content
|
||||
const id = text
|
||||
.toLowerCase()
|
||||
.replace(/[^\w\s-]/g, '') // Remove special chars
|
||||
.replace(/\s+/g, '-') // Replace spaces with hyphens
|
||||
.replace(/-+/g, '-') // Collapse multiple hyphens
|
||||
.replace(/^-|-$/g, ''); // Remove leading/trailing hyphens
|
||||
|
||||
return `<h${level} id="${id}">${text}</h${level}>`;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Convert markdown to HTML
|
||||
const html = marked(content, {
|
||||
gfm: true,
|
||||
breaks: false,
|
||||
headerIds: true,
|
||||
mangle: false
|
||||
});
|
||||
|
||||
// Create complete HTML document with CSS
|
||||
const cssContent = fs.readFileSync('/app/docs/custom-styles.css', 'utf8');
|
||||
const fullHtml = `
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>DoczyAI Documentation</title>
|
||||
<style>
|
||||
${cssContent}
|
||||
body { margin: 0; padding: 20px; }
|
||||
@page { margin: 2cm; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
${html}
|
||||
</body>
|
||||
</html>`;
|
||||
|
||||
// Write HTML to temporary file
|
||||
const htmlFile = `/app/docs/temp-${inputFile.replace('.md', '.html')}`;
|
||||
fs.writeFileSync(htmlFile, fullHtml);
|
||||
|
||||
// Use Chromium to generate PDF with clickable links (suppress stderr)
|
||||
execSync(`/usr/bin/chromium-browser --headless --disable-gpu --no-sandbox --run-all-compositor-stages-before-draw --virtual-time-budget=10000 --print-to-pdf=/app/docs/${outputFile} --print-to-pdf-no-header file://${htmlFile} 2>/dev/null`);
|
||||
|
||||
console.log(`✅ PDF generated: ${outputFile}`);
|
||||
|
||||
// Clean up temporary files
|
||||
try {
|
||||
fs.unlinkSync(htmlFile);
|
||||
for (let i = 1; i <= mermaidCounter; i++) {
|
||||
try {
|
||||
fs.unlinkSync(`/app/docs/diagram-${i}.mmd`);
|
||||
// Keep PNG files for debugging, remove in production
|
||||
// fs.unlinkSync(`/app/docs/diagram-${i}.png`);
|
||||
} catch (e) {
|
||||
// Ignore cleanup errors
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
// Ignore cleanup errors
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ Error converting to PDF:', error.message);
|
||||
console.error(error.stack);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
convertMarkdownToPDF();
|
||||
Reference in New Issue
Block a user