Fix template rendering issue and improve export functionality
- Fix template rendering by storing rendered HTML in variable before writing This resolves subdirectories not appearing in navigation despite being correctly detected and included in template variables - Add missing template variables (paths.work_dir) to gallery config - Add CGI refresh handler for web-based gallery regeneration - Improve PDF export functionality with better error handling - Add ESC key shortcut documentation for exiting selection mode - Enhanced export manager with proper dependency checking Fixes issue where new subdirectories were detected but not displayed in browser navigation due to incomplete template rendering.
This commit is contained in:
+143
-15
@@ -52,7 +52,7 @@ export class ExportManager {
|
||||
this.toggleSelectionMode();
|
||||
}
|
||||
if (e.key === 'Escape') {
|
||||
this.clearSelection();
|
||||
this.exitSelectionMode();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -118,10 +118,17 @@ export class ExportManager {
|
||||
* Exit selection mode
|
||||
*/
|
||||
exitSelectionMode() {
|
||||
const wasInSelectionMode = document.body.classList.contains('selection-mode');
|
||||
|
||||
document.body.classList.remove('selection-mode');
|
||||
document.getElementById('exportBtn').style.display = 'none';
|
||||
document.getElementById('selectionCounter').style.display = 'none';
|
||||
this.clearSelection();
|
||||
|
||||
// Show message if user was actually in selection mode
|
||||
if (wasInSelectionMode) {
|
||||
this.showMessage('Exited selection mode', 'info');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -229,17 +236,26 @@ export class ExportManager {
|
||||
return url;
|
||||
});
|
||||
|
||||
const timestamp = new Date().toISOString().replace(/[:.]/g, '-').split('T')[0];
|
||||
const outputName = `merged_plots_${timestamp}.pdf`;
|
||||
|
||||
const payload = {
|
||||
plots: actualPaths,
|
||||
layout: this.calculateLayout(actualPaths.length),
|
||||
output_name: `merged_plots_${new Date().toISOString().split('T')[0]}.pdf`
|
||||
output_name: outputName
|
||||
};
|
||||
|
||||
// Generate a unique temporary filename
|
||||
const tempFileName = `export_request_${Date.now()}.json`;
|
||||
|
||||
// Get work directory from config or fallback
|
||||
const workDir = window.galleryConfig?.workDir || '/work/kschmidt/web';
|
||||
|
||||
// Save the request to a JSON file that can be picked up by a Python script
|
||||
const requestData = JSON.stringify(payload, null, 2);
|
||||
|
||||
// Since we can't directly call Python from the browser, we'll show instructions
|
||||
this.showExportInstructions(requestData);
|
||||
// Show improved export instructions with full command
|
||||
this.showExportInstructions(requestData, tempFileName, workDir);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -258,21 +274,49 @@ export class ExportManager {
|
||||
/**
|
||||
* Show export instructions to user
|
||||
*/
|
||||
showExportInstructions(requestData) {
|
||||
showExportInstructions(requestData, tempFileName, workDir) {
|
||||
const tempFilePath = `/tmp/${tempFileName}`;
|
||||
const fullCommand = `echo '${requestData.replace(/'/g, "'\\''")}' > ${tempFilePath} && cd ${workDir} && python export_plots.py ${tempFilePath}`;
|
||||
|
||||
const instructions = `
|
||||
<div class="export-instructions">
|
||||
<h3>Export Instructions</h3>
|
||||
<p>To export your selected plots, save the following data to a file called <code>export_request.json</code> and run the export script:</p>
|
||||
<div class="export-data">
|
||||
<textarea readonly>${requestData}</textarea>
|
||||
<h3>🚀 Export Selected Plots</h3>
|
||||
<p>Run the following command in your terminal to export the selected plots:</p>
|
||||
|
||||
<div class="export-command-container">
|
||||
<div class="export-command">
|
||||
<code id="exportCommand">${fullCommand}</code>
|
||||
</div>
|
||||
<div class="export-actions">
|
||||
<button onclick="this.copyCommand()" class="copy-btn" title="Copy command to clipboard">
|
||||
📋 Copy Command
|
||||
</button>
|
||||
<button onclick="this.copyJSON()" class="copy-btn" title="Copy JSON only">
|
||||
📄 Copy JSON
|
||||
</button>
|
||||
<button onclick="this.close()" class="close-btn">
|
||||
✕ Close
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="export-commands">
|
||||
<p><strong>Command to run:</strong></p>
|
||||
<code>cd /work/kschmidt/web && python export_plots.py export_request.json</code>
|
||||
|
||||
<div class="export-details">
|
||||
<h4>📋 Command Breakdown:</h4>
|
||||
<ul>
|
||||
<li><strong>Creates temporary file:</strong> <code>${tempFilePath}</code></li>
|
||||
<li><strong>Changes to work directory:</strong> <code>${workDir}</code></li>
|
||||
<li><strong>Runs export script:</strong> <code>python export_plots.py</code></li>
|
||||
<li><strong>Output file:</strong> Will be saved in the work directory</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="export-actions">
|
||||
<button onclick="this.parentElement.parentElement.parentElement.remove()">Close</button>
|
||||
<button onclick="navigator.clipboard.writeText('${requestData.replace(/'/g, "\\'")}')">Copy JSON</button>
|
||||
|
||||
<div class="export-tips">
|
||||
<h4>💡 Tips:</h4>
|
||||
<ul>
|
||||
<li>The temporary JSON file will be automatically cleaned up after successful export</li>
|
||||
<li>Use <kbd>Esc</kbd> to exit selection mode</li>
|
||||
<li>Press <kbd>Ctrl+E</kbd> to toggle selection mode</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
@@ -280,7 +324,59 @@ export class ExportManager {
|
||||
const overlay = document.createElement('div');
|
||||
overlay.className = 'export-overlay';
|
||||
overlay.innerHTML = instructions;
|
||||
|
||||
// Add methods to the overlay for button handlers
|
||||
overlay.copyCommand = function() {
|
||||
navigator.clipboard.writeText(fullCommand).then(() => {
|
||||
this.showCopyFeedback('Command copied to clipboard!');
|
||||
}).catch(() => {
|
||||
this.showCopyFeedback('Failed to copy. Please select and copy manually.', 'error');
|
||||
});
|
||||
};
|
||||
|
||||
overlay.copyJSON = function() {
|
||||
navigator.clipboard.writeText(requestData).then(() => {
|
||||
this.showCopyFeedback('JSON copied to clipboard!');
|
||||
}).catch(() => {
|
||||
this.showCopyFeedback('Failed to copy. Please select and copy manually.', 'error');
|
||||
});
|
||||
};
|
||||
|
||||
overlay.close = function() {
|
||||
this.remove();
|
||||
};
|
||||
|
||||
overlay.showCopyFeedback = function(message, type = 'success') {
|
||||
const feedback = document.createElement('div');
|
||||
feedback.className = `copy-feedback copy-feedback-${type}`;
|
||||
feedback.textContent = message;
|
||||
this.appendChild(feedback);
|
||||
|
||||
setTimeout(() => {
|
||||
if (feedback.parentNode) {
|
||||
feedback.parentNode.removeChild(feedback);
|
||||
}
|
||||
}, 2000);
|
||||
};
|
||||
|
||||
document.body.appendChild(overlay);
|
||||
|
||||
// Close on ESC key
|
||||
const handleEscape = (e) => {
|
||||
if (e.key === 'Escape') {
|
||||
overlay.remove();
|
||||
document.removeEventListener('keydown', handleEscape);
|
||||
}
|
||||
};
|
||||
document.addEventListener('keydown', handleEscape);
|
||||
|
||||
// Close on clicking outside
|
||||
overlay.addEventListener('click', (e) => {
|
||||
if (e.target === overlay) {
|
||||
overlay.remove();
|
||||
document.removeEventListener('keydown', handleEscape);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -318,3 +414,35 @@ export class ExportManager {
|
||||
}, 3000);
|
||||
}
|
||||
}
|
||||
|
||||
// Add these methods to ExportManager if not present
|
||||
ExportManager.prototype.isSelectionModeActive = function() {
|
||||
return document.body.classList.contains('selection-mode');
|
||||
};
|
||||
ExportManager.prototype.exitSelectionMode = function() {
|
||||
document.body.classList.remove('selection-mode');
|
||||
if (typeof this.clearSelection === 'function') {
|
||||
this.clearSelection();
|
||||
}
|
||||
};
|
||||
|
||||
// Ensure a single global instance
|
||||
window.exportManager = window.exportManager || new ExportManager();
|
||||
|
||||
// Listen for ESC key globally to exit selection mode
|
||||
// (This will work even if focus is not on a plot)
|
||||
document.addEventListener('keydown', function(e) {
|
||||
if (e.key === 'Escape' && window.exportManager && window.exportManager.isSelectionModeActive()) {
|
||||
window.exportManager.exitSelectionMode();
|
||||
}
|
||||
});
|
||||
|
||||
// Attach improved export logic to export button
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const exportBtn = document.getElementById('exportBtn');
|
||||
if (exportBtn) {
|
||||
exportBtn.addEventListener('click', function() {
|
||||
window.exportManager.exportSelectedPlots();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
+9
-1
@@ -105,8 +105,16 @@ export class Utils {
|
||||
refreshBtn.disabled = true;
|
||||
refreshBtn.textContent = '⏳';
|
||||
|
||||
// Use the CGI script path from config if available
|
||||
let cgiPath = '/cgi-bin/refresh_gallery.py';
|
||||
if (window.galleryConfig && window.galleryConfig.paths && window.galleryConfig.paths.cgi_script) {
|
||||
cgiPath = window.galleryConfig.paths.cgi_script;
|
||||
// Ensure it starts with a slash for fetch
|
||||
if (!cgiPath.startsWith('/')) cgiPath = '/' + cgiPath;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch('/cgi-bin/refresh_gallery.py', {
|
||||
const response = await fetch(cgiPath, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
|
||||
Reference in New Issue
Block a user