Add HTML & Markdown preview in dashboard and revise pages
This commit is contained in:
@@ -101,12 +101,6 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
• Explanations of complex concepts
|
||||
• Creating study plans and schedules
|
||||
|
||||
I support **rich formatting** in my responses including:
|
||||
- **Bold text** and *italic text*
|
||||
- \`Code snippets\` and code blocks
|
||||
- Numbered lists and bullet points
|
||||
- Headers and structured content
|
||||
|
||||
How can I assist you today?`;
|
||||
const formattedWelcome = formatMessage(welcomeText, true);
|
||||
|
||||
|
||||
@@ -890,25 +890,100 @@ async function previewRevisedFile(fileId) {
|
||||
|
||||
const file = fileInfo.file;
|
||||
|
||||
// Download the file content
|
||||
const contentResponse = await fetch(`/uploads/revised-notes/${file.filename}`);
|
||||
// Get file content using the new API endpoint
|
||||
const contentResponse = await fetch(`/api/revised-files/${fileId}/content`);
|
||||
if (!contentResponse.ok) {
|
||||
throw new Error('Failed to load file content');
|
||||
}
|
||||
|
||||
const content = await contentResponse.text();
|
||||
const contentResult = await contentResponse.json();
|
||||
if (!contentResult.success) {
|
||||
throw new Error('Failed to load file content');
|
||||
}
|
||||
|
||||
const content = contentResult.content;
|
||||
const modal = new bootstrap.Modal(document.getElementById('previewModal'));
|
||||
|
||||
// Create preview content with display mode toggle
|
||||
document.getElementById('preview-content').innerHTML = `
|
||||
<div class="alert alert-info">
|
||||
<i class="fas fa-brain me-2"></i>
|
||||
<strong>AI-Revised Content</strong> • ${file.revisionType} • From: ${file.originalFileName || 'Unknown'}
|
||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||
<div class="alert alert-info mb-0 flex-grow-1 me-3">
|
||||
<i class="fas fa-brain me-2"></i>
|
||||
<strong>AI-Revised Content</strong> • ${file.revisionType} • From: ${file.originalFileName || 'Unknown'}
|
||||
</div>
|
||||
<div class="btn-group btn-group-sm" role="group" id="preview-display-mode">
|
||||
<input type="radio" class="btn-check" name="previewDisplayMode" id="preview-mode-markdown" value="markdown" checked>
|
||||
<label class="btn btn-outline-secondary" for="preview-mode-markdown" title="Show as Markdown">
|
||||
<i class="fab fa-markdown"></i>
|
||||
</label>
|
||||
|
||||
<input type="radio" class="btn-check" name="previewDisplayMode" id="preview-mode-html" value="html">
|
||||
<label class="btn btn-outline-secondary" for="preview-mode-html" title="Render as HTML">
|
||||
<i class="fas fa-code"></i>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="border p-3 bg-light rounded" style="max-height: 400px; overflow-y: auto;">
|
||||
<pre style="white-space: pre-wrap; word-wrap: break-word;">${escapeHtml(content)}</pre>
|
||||
<div id="preview-content-container" class="border p-3 bg-light rounded" style="max-height: 400px; overflow-y: auto;">
|
||||
<pre style="white-space: pre-wrap; word-wrap: break-word; margin: 0;">${escapeHtml(content)}</pre>
|
||||
</div>
|
||||
`;
|
||||
|
||||
// Add event listeners for display mode toggle
|
||||
const modeMarkdown = document.getElementById('preview-mode-markdown');
|
||||
const modeHtml = document.getElementById('preview-mode-html');
|
||||
const contentContainer = document.getElementById('preview-content-container');
|
||||
|
||||
async function updatePreviewMode() {
|
||||
const selectedMode = document.querySelector('input[name="previewDisplayMode"]:checked').value;
|
||||
|
||||
try {
|
||||
const renderResponse = await fetch('/api/render-revised-content', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
content: content,
|
||||
displayMode: selectedMode,
|
||||
autoDetect: true
|
||||
})
|
||||
});
|
||||
|
||||
const renderResult = await renderResponse.json();
|
||||
|
||||
if (renderResult.success) {
|
||||
if (selectedMode === 'html') {
|
||||
let htmlContent = `<div class="rendered-markdown">${renderResult.renderedContent}</div>`;
|
||||
|
||||
if (renderResult.isMarkdownContent) {
|
||||
htmlContent = `
|
||||
<div class="alert alert-info alert-sm mb-2">
|
||||
<small><i class="fas fa-info-circle me-1"></i>Markdown formatting detected and rendered</small>
|
||||
</div>
|
||||
${htmlContent}
|
||||
`;
|
||||
}
|
||||
|
||||
contentContainer.innerHTML = htmlContent;
|
||||
contentContainer.style.backgroundColor = '#ffffff';
|
||||
} else {
|
||||
contentContainer.innerHTML = `<pre style="white-space: pre-wrap; word-wrap: break-word; margin: 0;">${escapeHtml(renderResult.renderedContent)}</pre>`;
|
||||
contentContainer.style.backgroundColor = '#f8f9fa';
|
||||
}
|
||||
} else {
|
||||
throw new Error(renderResult.error || 'Failed to render content');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error rendering preview:', error);
|
||||
// Fallback to escaped text
|
||||
contentContainer.innerHTML = `<pre style="white-space: pre-wrap; word-wrap: break-word; margin: 0;">${escapeHtml(content)}</pre>`;
|
||||
contentContainer.style.backgroundColor = '#f8f9fa';
|
||||
}
|
||||
}
|
||||
|
||||
modeMarkdown.addEventListener('change', updatePreviewMode);
|
||||
modeHtml.addEventListener('change', updatePreviewMode);
|
||||
|
||||
modal.show();
|
||||
} catch (error) {
|
||||
console.error('Error previewing revised file:', error);
|
||||
|
||||
@@ -35,7 +35,20 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<h5>AI-Revised Notes</h5>
|
||||
<div class="d-flex justify-content-between align-items-center mb-2">
|
||||
<h5>AI-Revised Notes</h5>
|
||||
<div class="btn-group btn-group-sm" role="group" id="display-mode-toggle" style="display: none;">
|
||||
<input type="radio" class="btn-check" name="displayMode" id="mode-markdown" value="markdown" checked>
|
||||
<label class="btn btn-outline-secondary" for="mode-markdown" title="Show as Markdown">
|
||||
<i class="fab fa-markdown"></i>
|
||||
</label>
|
||||
|
||||
<input type="radio" class="btn-check" name="displayMode" id="mode-html" value="html">
|
||||
<label class="btn btn-outline-secondary" for="mode-html" title="Render as HTML">
|
||||
<i class="fas fa-code"></i>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div id="revised-content" class="border p-3 bg-white rounded" style="height: 400px; overflow-y: auto;">
|
||||
<p class="text-muted text-center mt-5">Select a revision type and click "Revise" to see AI-enhanced notes here.</p>
|
||||
</div>
|
||||
@@ -139,11 +152,72 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
const revisionType = document.getElementById('revision-type');
|
||||
const saveBtn = document.getElementById('save-btn');
|
||||
const downloadBtn = document.getElementById('download-btn');
|
||||
const displayModeToggle = document.getElementById('display-mode-toggle');
|
||||
const modeMarkdown = document.getElementById('mode-markdown');
|
||||
const modeHtml = document.getElementById('mode-html');
|
||||
|
||||
const fileId = '<%= file.id %>';
|
||||
const content = <%- JSON.stringify(content) %>;
|
||||
let currentRevisedContent = '';
|
||||
let currentRevisionType = '';
|
||||
let currentDisplayMode = 'markdown';
|
||||
|
||||
// Handle display mode changes
|
||||
function updateDisplayMode() {
|
||||
if (!currentRevisedContent) return;
|
||||
|
||||
const selectedMode = document.querySelector('input[name="displayMode"]:checked').value;
|
||||
currentDisplayMode = selectedMode;
|
||||
|
||||
renderRevisedContent(currentRevisedContent, selectedMode);
|
||||
}
|
||||
|
||||
// Render revised content based on display mode
|
||||
async function renderRevisedContent(content, displayMode = 'markdown') {
|
||||
try {
|
||||
const response = await fetch('/api/render-revised-content', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
content: content,
|
||||
displayMode: displayMode,
|
||||
autoDetect: true
|
||||
})
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (result.success) {
|
||||
if (displayMode === 'html') {
|
||||
// Render as HTML
|
||||
revisedContent.innerHTML = `<div class="rendered-markdown">${result.renderedContent}</div>`;
|
||||
|
||||
// Show format detection info if available
|
||||
if (result.isMarkdownContent) {
|
||||
const formatInfo = document.createElement('div');
|
||||
formatInfo.className = 'alert alert-info alert-sm mb-2';
|
||||
formatInfo.innerHTML = '<small><i class="fas fa-info-circle me-1"></i>Markdown formatting detected and rendered</small>';
|
||||
revisedContent.insertBefore(formatInfo, revisedContent.firstChild);
|
||||
}
|
||||
} else {
|
||||
// Show as raw markdown/text
|
||||
revisedContent.innerHTML = `<pre class="mb-0" style="white-space: pre-wrap; word-wrap: break-word;">${escapeHtml(result.renderedContent)}</pre>`;
|
||||
}
|
||||
} else {
|
||||
throw new Error(result.error || 'Failed to render content');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error rendering content:', error);
|
||||
// Fallback to escaped text
|
||||
revisedContent.innerHTML = `<pre class="mb-0" style="white-space: pre-wrap; word-wrap: break-word;">${escapeHtml(content)}</pre>`;
|
||||
}
|
||||
}
|
||||
|
||||
// Add event listeners for display mode toggle
|
||||
modeMarkdown.addEventListener('change', updateDisplayMode);
|
||||
modeHtml.addEventListener('change', updateDisplayMode);
|
||||
|
||||
reviseBtn.addEventListener('click', async function() {
|
||||
const type = revisionType.value;
|
||||
@@ -153,6 +227,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
|
||||
reviseBtn.disabled = true;
|
||||
revisionProgress.classList.remove('d-none');
|
||||
displayModeToggle.style.display = 'none'; // Hide toggle during processing
|
||||
revisedContent.innerHTML = '<div class="text-center mt-5"><div class="spinner-border text-primary" role="status"><span class="visually-hidden">Processing...</span></div><p class="mt-2">AI is processing your notes...</p></div>';
|
||||
|
||||
try {
|
||||
@@ -175,9 +250,15 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
console.log('Revision result:', result);
|
||||
|
||||
if (result.success) {
|
||||
revisedContent.innerHTML = '<pre class="mb-0" style="white-space: pre-wrap; word-wrap: break-word;">' + escapeHtml(result.revisedContent) + '</pre>';
|
||||
currentRevisedContent = result.revisedContent;
|
||||
currentRevisionType = type;
|
||||
|
||||
// Show display mode toggle
|
||||
displayModeToggle.style.display = 'block';
|
||||
|
||||
// Render content based on current display mode
|
||||
await renderRevisedContent(currentRevisedContent, currentDisplayMode);
|
||||
|
||||
saveBtn.disabled = false;
|
||||
downloadBtn.disabled = false;
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user