`;
iterationsDiv.innerHTML += iterationHTML;
// Update editor with new markdown if available
if (data.markdown && data.markdown !== this.editor.innerText) {
this.editor.innerText = data.markdown;
this.generateTOC();
}
// Scroll to bottom of feedback to see new iteration
const feedback = document.getElementById('ai-assistant-feedback');
feedback.scrollTop = feedback.scrollHeight;
}
if (data.completed) {
// Finalize display
if (progressFill) {
progressFill.style.width = '100%';
}
if (data.finalMarkdown) {
// Ensure final content is in editor
this.editor.innerText = data.finalMarkdown;
this.generateTOC();
// Save final state
this.saveState(true);
}
// Completion message
const completedHTML = `
`;
// Scroll to top of feedback to see result
feedback.scrollTop = 0;
}
validateRephrase() {
if (!this.lastRephraseData) return;
try {
// Save state before rephrasing to allow undo
this.saveState(true);
// Replace text in editor
const range = this.lastRephraseData.selection.getRangeAt(0);
range.deleteContents();
range.insertNode(document.createTextNode(this.lastRephraseData.rephrased));
// Clear selection and regenerate TOC
window.getSelection().removeAllRanges();
this.generateTOC();
// Save state after rephrasing
this.saveState(true);
// Show success message
this.showNotification('Rephrasing applied successfully', 'success');
this.clearFeedback();
// Clean up data
this.lastRephraseData = null;
} catch (error) {
console.error('Error applying rephrasing:', error);
this.showNotification('Error applying rephrasing', 'error');
}
}
clearFeedback() {
const feedback = document.getElementById('ai-assistant-feedback');
feedback.innerHTML = `
AI Assistant ready
Select text in the editor and click an action to begin.
`;
}
ensureEditMode() {
// If in preview mode, force return to edit mode
if (this.isPreviewMode) {
const previewBtn = document.getElementById('preview-toggle');
// Return to edit mode without using originalContent because we want new content
this.editor.contentEditable = true;
this.editor.style.background = '';
this.editor.style.border = '';
this.editor.style.borderRadius = '';
// Change button
if (previewBtn) {
previewBtn.innerHTML = 'Preview';
previewBtn.classList.remove('secondary');
previewBtn.classList.add('primary');
}
this.isPreviewMode = false;
}
// Ensure editor is always editable
this.editor.contentEditable = true;
}
showNotification(message, type = 'success') {
const notification = document.createElement('div');
notification.className = `notification ${type}`;
notification.textContent = message;
document.body.appendChild(notification);
setTimeout(() => notification.classList.add('show'), 100);
setTimeout(() => {
notification.classList.remove('show');
setTimeout(() => document.body.removeChild(notification), 300);
}, 3000);
}
async togglePreview() {
const previewBtn = document.getElementById('preview-toggle');
if (!this.isPreviewMode) {
// Switch to preview mode
this.originalContent = this.editor.innerHTML;
const markdownContent = this.editor.innerText;
// Configure Marked for GitHub-compatible rendering
if (typeof marked !== 'undefined') {
marked.setOptions({
breaks: true,
gfm: true,
headerIds: true,
sanitize: false,
smartypants: false
});
}
// Convert Markdown to HTML with Marked (GitHub-compatible)
let previewHTML = '';
if (typeof marked !== 'undefined') {
previewHTML = marked.parse(markdownContent);
} else {
// Fallback to our custom parser if Marked is not loaded
previewHTML = this.parseMarkdown(markdownContent);
}
// Disable editing and apply GitHub preview style
this.editor.contentEditable = false;
this.editor.innerHTML = `
${previewHTML}
`;
this.editor.style.background = '';
this.editor.style.border = '';
this.editor.style.borderRadius = '';
// Process Mermaid diagrams after rendering
if (typeof mermaid !== 'undefined') {
try {
mermaid.initialize({
startOnLoad: false,
theme: document.body.classList.contains('dark-theme') ? 'dark' : 'default',
securityLevel: 'loose',
fontFamily: 'system-ui, -apple-system, "Segoe UI", Roboto, sans-serif'
});
// Wait for DOM to be ready to process Mermaid diagrams
setTimeout(() => {
const preview = this.editor.querySelector('.markdown-preview');
if (preview) {
// Find all code blocks with 'mermaid' language
const mermaidBlocks = preview.querySelectorAll('code.language-mermaid, pre code.language-mermaid');
mermaidBlocks.forEach((block, index) => {
try {
const mermaidCode = block.textContent;
const uniqueId = `mermaid-${Date.now()}-${index}`;
// Create div with unique ID for Mermaid
const mermaidDiv = document.createElement('div');
mermaidDiv.id = uniqueId;
mermaidDiv.className = 'mermaid';
mermaidDiv.textContent = mermaidCode;
// Replace code block with Mermaid div
const pre = block.closest('pre') || block;
pre.parentNode.replaceChild(mermaidDiv, pre);
// Render diagram
mermaid.render(uniqueId + '-svg', mermaidCode).then(({ svg }) => {
mermaidDiv.innerHTML = svg;
}).catch(err => {
console.warn('Mermaid rendering error:', err);
mermaidDiv.innerHTML = `
Mermaid rendering error: ${err.message}
`;
});
} catch (error) {
console.warn('Mermaid processing error:', error);
}
});
}
}, 200);
} catch (error) {
console.warn('Mermaid initialization error:', error);
}
}
// Change button
previewBtn.innerHTML = 'Edit';
previewBtn.classList.remove('primary');
previewBtn.classList.add('secondary');
this.isPreviewMode = true;
this.showNotification('Preview mode activated', 'success');
} else {
// Return to edit mode
this.editor.contentEditable = true;
this.editor.innerHTML = this.originalContent;
this.editor.style.background = '';
this.editor.style.border = '';
this.editor.style.borderRadius = '';
// Change button
previewBtn.innerHTML = 'Preview';
previewBtn.classList.remove('secondary');
previewBtn.classList.add('primary');
this.isPreviewMode = false;
this.showNotification('Edit mode activated', 'success');
}
}
}
// Side panel management
function togglePanel(side) {
const panel = document.getElementById(`${side}-panel`);
const overlay = document.getElementById('panel-overlay');
const otherPanel = document.getElementById(side === 'left' ? 'right-panel' : 'left-panel');
// Close the other panel if it's open
if (otherPanel && otherPanel.classList.contains('open')) {
otherPanel.classList.remove('open');
}
// Toggle current panel
if (panel.classList.contains('open')) {
panel.classList.remove('open');
overlay.classList.remove('active');
} else {
panel.classList.add('open');
overlay.classList.add('active');
}
}
function closeAllPanels() {
const leftPanel = document.getElementById('left-panel');
const rightPanel = document.getElementById('right-panel');
const overlay = document.getElementById('panel-overlay');
if (leftPanel) leftPanel.classList.remove('open');
if (rightPanel) rightPanel.classList.remove('open');
if (overlay) overlay.classList.remove('active');
}
// Close panels with Escape
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape') {
closeAllPanels();
}
});
// Application initialization
let app;
document.addEventListener('DOMContentLoaded', () => {
app = new ConceptionAssistant();
// Load saved theme
const savedTheme = localStorage.getItem('theme');
if (savedTheme === 'dark') {
document.body.classList.add('dark-theme');
}
// Initialize panels
initializePanels();
});
function initializePanels() {
// Initialize template management
initializeTemplateForm();
}
function initializeTemplateForm() {
const loadTemplateBtn = document.getElementById('load-template');
// Handle template loading
if (loadTemplateBtn) {
loadTemplateBtn.addEventListener('click', async () => {
try {
loadTemplateBtn.classList.add('loading');
const response = await fetch('/api/templates/default');
const result = await response.json();
if (result.success) {
// Load template into editor
app.editor.innerText = result.data.content;
app.generateTOC();
app.currentJournalId = null; // New journal
// Reset history for new template
app.undoStack = [result.data.content];
app.redoStack = [];
// Ensure editor is in edit mode
app.ensureEditMode();
app.showNotification('Template loaded successfully', 'success');
closeAllPanels();
} else {
app.showNotification('Error loading template', 'error');
}
} catch (error) {
console.error('Error:', error);
app.showNotification('Error loading template', 'error');
} finally {
loadTemplateBtn.classList.remove('loading');
}
});
}
}
// Ensure togglePanel is globally accessible
window.togglePanel = togglePanel;