fix: resolve section duplication issue when saving edited content
Fixed critical bug where editing a section would cause content duplication in the saved file due to improper handling of DOM reconstruction. Problem: - marked.parse() creates multiple HTML elements from single markdown section - markSections() marked all new elements as individual editable sections - getMarkdownContent() processed all marked sections, causing duplication - Example: editing "## Header\nText" created <h2> + <p>, both saved separately Solution: - Wrap edited content in container div with data-edited attribute - Update markSections() to skip elements inside edited wrappers - Enhanced getMarkdownContent() to handle edited wrappers as single units - Process child elements within edited wrappers correctly - Maintain section indexing while preventing double-marking Technical Changes: - editSection() now creates wrapper div for parsed content - markSections() skips content inside [data-edited] containers - getMarkdownContent() handles edited vs regular sections differently - Proper cleanup and re-indexing of section markers This ensures edited sections are treated as cohesive units and saved exactly once, eliminating content duplication while maintaining full editing functionality. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -687,10 +687,30 @@ class DocumentManager:
|
||||
}
|
||||
|
||||
markSections(element) {
|
||||
// Clear existing section markers (except edited ones)
|
||||
const existingSections = element.querySelectorAll('.markitect-section-editable:not([data-edited])');
|
||||
existingSections.forEach(section => {
|
||||
section.classList.remove('markitect-section-editable');
|
||||
section.removeAttribute('data-section');
|
||||
});
|
||||
|
||||
// Mark new sections (skip elements inside edited wrappers)
|
||||
const sections = element.querySelectorAll('h1, h2, h3, h4, h5, h6, p, blockquote, pre, ul, ol');
|
||||
sections.forEach((section, index) => {
|
||||
let sectionIndex = 0;
|
||||
|
||||
sections.forEach((section) => {
|
||||
// Skip if this element is inside an edited wrapper
|
||||
if (section.closest('[data-edited]')) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Skip if already marked as edited wrapper
|
||||
if (section.hasAttribute('data-edited')) {
|
||||
return;
|
||||
}
|
||||
|
||||
section.classList.add('markitect-section-editable');
|
||||
section.setAttribute('data-section', index);
|
||||
section.setAttribute('data-section', sectionIndex++);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -709,8 +729,19 @@ class DocumentManager:
|
||||
|
||||
textarea.addEventListener('blur', () => {
|
||||
this.hasEdits = true; // Mark that edits have been made
|
||||
section.innerHTML = marked.parse(textarea.value);
|
||||
this.markSections(section.parentElement);
|
||||
|
||||
// Create a wrapper div to contain the parsed content
|
||||
const wrapper = document.createElement('div');
|
||||
wrapper.innerHTML = marked.parse(textarea.value);
|
||||
wrapper.classList.add('markitect-section-editable');
|
||||
wrapper.setAttribute('data-section', section.getAttribute('data-section'));
|
||||
wrapper.setAttribute('data-edited', 'true'); // Mark as edited
|
||||
|
||||
// Replace the section with the wrapper
|
||||
section.parentNode.replaceChild(wrapper, section);
|
||||
|
||||
// Re-mark sections in the entire document, but skip edited wrappers
|
||||
this.markSections(document.getElementById('markdown-content'));
|
||||
});
|
||||
|
||||
section.innerHTML = '';
|
||||
@@ -800,32 +831,69 @@ class DocumentManager:
|
||||
let reconstructed = '';
|
||||
|
||||
sections.forEach(section => {{
|
||||
const tagName = section.tagName.toLowerCase();
|
||||
const text = section.textContent.trim();
|
||||
// Handle edited wrappers differently
|
||||
if (section.hasAttribute('data-edited')) {{
|
||||
// For edited sections, convert the child elements back to markdown
|
||||
const childElements = section.children;
|
||||
for (let i = 0; i < childElements.length; i++) {{
|
||||
const child = childElements[i];
|
||||
const tagName = child.tagName.toLowerCase();
|
||||
const text = child.textContent.trim();
|
||||
|
||||
if (tagName.startsWith('h')) {{
|
||||
const level = parseInt(tagName.charAt(1));
|
||||
reconstructed += '#'.repeat(level) + ' ' + text + '\\n\\n';
|
||||
}} else if (tagName === 'p') {{
|
||||
reconstructed += text + '\\n\\n';
|
||||
}} else if (tagName === 'blockquote') {{
|
||||
reconstructed += '> ' + text + '\\n\\n';
|
||||
}} else if (tagName === 'pre') {{
|
||||
reconstructed += '```\\n' + text + '\\n```\\n\\n';
|
||||
}} else if (tagName === 'ul') {{
|
||||
const items = section.querySelectorAll('li');
|
||||
items.forEach(item => {{
|
||||
reconstructed += '- ' + item.textContent.trim() + '\\n';
|
||||
}});
|
||||
reconstructed += '\\n';
|
||||
}} else if (tagName === 'ol') {{
|
||||
const items = section.querySelectorAll('li');
|
||||
items.forEach((item, index) => {{
|
||||
reconstructed += (index + 1) + '. ' + item.textContent.trim() + '\\n';
|
||||
}});
|
||||
reconstructed += '\\n';
|
||||
if (tagName.startsWith('h')) {{
|
||||
const level = parseInt(tagName.charAt(1));
|
||||
reconstructed += '#'.repeat(level) + ' ' + text + '\\n\\n';
|
||||
}} else if (tagName === 'p') {{
|
||||
reconstructed += text + '\\n\\n';
|
||||
}} else if (tagName === 'blockquote') {{
|
||||
reconstructed += '> ' + text + '\\n\\n';
|
||||
}} else if (tagName === 'pre') {{
|
||||
reconstructed += '```\\n' + text + '\\n```\\n\\n';
|
||||
}} else if (tagName === 'ul') {{
|
||||
const items = child.querySelectorAll('li');
|
||||
items.forEach(item => {{
|
||||
reconstructed += '- ' + item.textContent.trim() + '\\n';
|
||||
}});
|
||||
reconstructed += '\\n';
|
||||
}} else if (tagName === 'ol') {{
|
||||
const items = child.querySelectorAll('li');
|
||||
items.forEach((item, index) => {{
|
||||
reconstructed += (index + 1) + '. ' + item.textContent.trim() + '\\n';
|
||||
}});
|
||||
reconstructed += '\\n';
|
||||
}} else {{
|
||||
reconstructed += text + '\\n\\n';
|
||||
}}
|
||||
}}
|
||||
}} else {{
|
||||
reconstructed += text + '\\n\\n';
|
||||
// Handle regular sections
|
||||
const tagName = section.tagName.toLowerCase();
|
||||
const text = section.textContent.trim();
|
||||
|
||||
if (tagName.startsWith('h')) {{
|
||||
const level = parseInt(tagName.charAt(1));
|
||||
reconstructed += '#'.repeat(level) + ' ' + text + '\\n\\n';
|
||||
}} else if (tagName === 'p') {{
|
||||
reconstructed += text + '\\n\\n';
|
||||
}} else if (tagName === 'blockquote') {{
|
||||
reconstructed += '> ' + text + '\\n\\n';
|
||||
}} else if (tagName === 'pre') {{
|
||||
reconstructed += '```\\n' + text + '\\n```\\n\\n';
|
||||
}} else if (tagName === 'ul') {{
|
||||
const items = section.querySelectorAll('li');
|
||||
items.forEach(item => {{
|
||||
reconstructed += '- ' + item.textContent.trim() + '\\n';
|
||||
}});
|
||||
reconstructed += '\\n';
|
||||
}} else if (tagName === 'ol') {{
|
||||
const items = section.querySelectorAll('li');
|
||||
items.forEach((item, index) => {{
|
||||
reconstructed += (index + 1) + '. ' + item.textContent.trim() + '\\n';
|
||||
}});
|
||||
reconstructed += '\\n';
|
||||
}} else {{
|
||||
reconstructed += text + '\\n\\n';
|
||||
}}
|
||||
}}
|
||||
}});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user