Complete library cleanup and documentation overhaul
## Library Code Cleanup (~1,200+ lines removed) - Remove legacy markdown system (markdown.js, previewer.js) - Delete unused EditContext code from ui/editor.js (~400 lines) - Remove version history UI components from form-renderer.js (~180 lines) - Clean unused CSS styles from insertr.css (~120 lines) - Update package.json dependencies (remove marked, turndown) ## Documentation Updates - README.md: Update from markdown to HTML-first approach - AGENTS.md: Add current architecture guidance and HTML-first principles - TODO.md: Complete rewrite with realistic roadmap and current status - demos/README.md: Update for development demo server usage ## System Reality Alignment - All documentation now reflects current working system - Removed aspirational features in favor of actual capabilities - Clear separation between development and production workflows - Accurate description of style-aware editor with HTML preservation ## Code Cleanup Benefits - Simplified codebase focused on HTML-first approach - Removed markdown conversion complexity - Cleaner build process without unused dependencies - Better alignment between frontend capabilities and documentation Ready for Phase 3a server updates with clean foundation.
This commit is contained in:
@@ -1,288 +0,0 @@
|
||||
/**
|
||||
* Markdown conversion utilities using Marked and Turndown
|
||||
*/
|
||||
import { marked } from 'marked';
|
||||
import TurndownService from 'turndown';
|
||||
|
||||
/**
|
||||
* MarkdownConverter - Handles bidirectional HTML ↔ Markdown conversion
|
||||
*/
|
||||
export class MarkdownConverter {
|
||||
constructor() {
|
||||
this.initializeMarked();
|
||||
this.initializeTurndown();
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure marked for HTML output - MINIMAL MODE
|
||||
* Only supports: **bold**, *italic*, and [links](url)
|
||||
* Matches server-side goldmark configuration
|
||||
*/
|
||||
initializeMarked() {
|
||||
marked.setOptions({
|
||||
gfm: false, // Disable GFM to match server minimal mode
|
||||
breaks: true, // Convert \n to <br> (matches server)
|
||||
pedantic: false, // Don't be overly strict
|
||||
sanitize: false, // Allow HTML (we control the input)
|
||||
smartLists: false, // Disable lists (not supported on server)
|
||||
smartypants: false // Don't convert quotes/dashes
|
||||
});
|
||||
|
||||
// Override renderers to restrict to minimal feature set
|
||||
marked.use({
|
||||
renderer: {
|
||||
// Disable headings - treat as plain text
|
||||
heading(text, level) {
|
||||
return text;
|
||||
},
|
||||
// Disable lists - treat as plain text
|
||||
list(body, ordered, start) {
|
||||
return body.replace(/<\/?li>/g, '');
|
||||
},
|
||||
listitem(text) {
|
||||
return text + '\n';
|
||||
},
|
||||
// Disable code blocks - treat as plain text
|
||||
code(code, language) {
|
||||
return code;
|
||||
},
|
||||
blockquote(quote) {
|
||||
return quote; // Disable blockquotes - treat as plain text
|
||||
},
|
||||
// Disable horizontal rules
|
||||
hr() {
|
||||
return '';
|
||||
},
|
||||
// Disable tables
|
||||
table(header, body) {
|
||||
return header + body;
|
||||
},
|
||||
tablecell(content, flags) {
|
||||
return content;
|
||||
},
|
||||
tablerow(content) {
|
||||
return content;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure turndown for markdown output - MINIMAL MODE
|
||||
* Only supports: **bold**, *italic*, and [links](url)
|
||||
* Matches server-side goldmark configuration
|
||||
*/
|
||||
initializeTurndown() {
|
||||
this.turndown = new TurndownService({
|
||||
// Minimal configuration - only basic formatting
|
||||
headingStyle: 'atx', // # headers (but will be disabled)
|
||||
hr: '---', // horizontal rule (but will be disabled)
|
||||
bulletListMarker: '-', // bullet list (but will be disabled)
|
||||
codeBlockStyle: 'fenced', // code blocks (but will be disabled)
|
||||
fence: '```', // fence marker (but will be disabled)
|
||||
emDelimiter: '*', // *italic* - matches server
|
||||
strongDelimiter: '**', // **bold** - matches server
|
||||
linkStyle: 'inlined', // [text](url) - matches server
|
||||
linkReferenceStyle: 'full' // full reference links
|
||||
});
|
||||
|
||||
// Add custom rules for better conversion
|
||||
this.addTurndownRules();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add custom turndown rules - MINIMAL MODE
|
||||
* Only supports: **bold**, *italic*, and [links](url)
|
||||
* Disables all other formatting to match server
|
||||
*/
|
||||
addTurndownRules() {
|
||||
// Handle paragraph spacing properly - ensure double newlines between paragraphs
|
||||
this.turndown.addRule('paragraph', {
|
||||
filter: 'p',
|
||||
replacement: function (content) {
|
||||
if (!content.trim()) return '';
|
||||
return content.trim() + '\n\n';
|
||||
}
|
||||
});
|
||||
|
||||
// Handle bold text in markdown - keep this (supported)
|
||||
this.turndown.addRule('bold', {
|
||||
filter: ['strong', 'b'],
|
||||
replacement: function (content) {
|
||||
if (!content.trim()) return '';
|
||||
return '**' + content + '**';
|
||||
}
|
||||
});
|
||||
|
||||
// Handle italic text in markdown - keep this (supported)
|
||||
this.turndown.addRule('italic', {
|
||||
filter: ['em', 'i'],
|
||||
replacement: function (content) {
|
||||
if (!content.trim()) return '';
|
||||
return '*' + content + '*';
|
||||
}
|
||||
});
|
||||
|
||||
// DISABLE unsupported features - convert to plain text
|
||||
this.turndown.addRule('disableHeadings', {
|
||||
filter: ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'],
|
||||
replacement: function (content) {
|
||||
return content; // Just return text content, no # markup
|
||||
}
|
||||
});
|
||||
|
||||
this.turndown.addRule('disableLists', {
|
||||
filter: ['ul', 'ol', 'li'],
|
||||
replacement: function (content) {
|
||||
return content; // Just return text content, no list markup
|
||||
}
|
||||
});
|
||||
|
||||
this.turndown.addRule('disableCode', {
|
||||
filter: ['pre', 'code'],
|
||||
replacement: function (content) {
|
||||
return content; // Just return text content, no code markup
|
||||
}
|
||||
});
|
||||
|
||||
this.turndown.addRule('disableBlockquotes', {
|
||||
filter: 'blockquote',
|
||||
replacement: function (content) {
|
||||
return content; // Just return text content, no > markup
|
||||
}
|
||||
});
|
||||
|
||||
this.turndown.addRule('disableHR', {
|
||||
filter: 'hr',
|
||||
replacement: function () {
|
||||
return ''; // Remove horizontal rules entirely
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert HTML to Markdown
|
||||
* @param {string} html - HTML string to convert
|
||||
* @returns {string} - Markdown string
|
||||
*/
|
||||
htmlToMarkdown(html) {
|
||||
if (!html || html.trim() === '') {
|
||||
return '';
|
||||
}
|
||||
|
||||
try {
|
||||
const markdown = this.turndown.turndown(html);
|
||||
// Clean up and normalize newlines for proper paragraph separation
|
||||
return markdown
|
||||
.replace(/\n{3,}/g, '\n\n') // Replace 3+ newlines with 2
|
||||
.replace(/^\n+|\n+$/g, '') // Remove leading/trailing newlines
|
||||
.trim(); // Remove other whitespace
|
||||
} catch (error) {
|
||||
console.warn('HTML to Markdown conversion failed:', error);
|
||||
// Fallback: extract text content
|
||||
const tempDiv = document.createElement('div');
|
||||
tempDiv.innerHTML = html;
|
||||
return tempDiv.textContent || tempDiv.innerText || '';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert Markdown to HTML
|
||||
* @param {string} markdown - Markdown string to convert
|
||||
* @returns {string} - HTML string
|
||||
*/
|
||||
markdownToHtml(markdown) {
|
||||
if (!markdown || markdown.trim() === '') {
|
||||
return '';
|
||||
}
|
||||
|
||||
try {
|
||||
const html = marked(markdown);
|
||||
return html;
|
||||
} catch (error) {
|
||||
console.warn('Markdown to HTML conversion failed:', error);
|
||||
// Fallback: convert line breaks to paragraphs
|
||||
return markdown
|
||||
.split(/\n\s*\n/)
|
||||
.filter(p => p.trim())
|
||||
.map(p => `<p>${p.trim()}</p>`)
|
||||
.join('');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract HTML content from a group of elements
|
||||
* @param {HTMLElement[]} elements - Array of DOM elements
|
||||
* @returns {string} - Combined HTML content
|
||||
*/
|
||||
extractGroupHTML(elements) {
|
||||
const htmlParts = [];
|
||||
|
||||
elements.forEach(element => {
|
||||
// Wrap inner content in paragraph tags to preserve structure
|
||||
const html = element.innerHTML.trim();
|
||||
if (html) {
|
||||
// If element is already a paragraph, use its outer HTML
|
||||
if (element.tagName.toLowerCase() === 'p') {
|
||||
htmlParts.push(element.outerHTML);
|
||||
} else {
|
||||
// Wrap in paragraph tags
|
||||
htmlParts.push(`<p>${html}</p>`);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return htmlParts.join('\n');
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert HTML content from group elements to markdown
|
||||
* @param {HTMLElement[]} elements - Array of DOM elements
|
||||
* @returns {string} - Markdown representation
|
||||
*/
|
||||
extractGroupMarkdown(elements) {
|
||||
const html = this.extractGroupHTML(elements);
|
||||
const markdown = this.htmlToMarkdown(html);
|
||||
return markdown;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update group elements with markdown content
|
||||
* @param {HTMLElement[]} elements - Array of DOM elements to update
|
||||
* @param {string} markdown - Markdown content to render
|
||||
*/
|
||||
updateGroupElements(elements, markdown) {
|
||||
const html = this.markdownToHtml(markdown);
|
||||
|
||||
// Split HTML into paragraphs
|
||||
const tempDiv = document.createElement('div');
|
||||
tempDiv.innerHTML = html;
|
||||
|
||||
const paragraphs = Array.from(tempDiv.querySelectorAll('p, div, h1, h2, h3, h4, h5, h6'));
|
||||
|
||||
// Handle case where we have more/fewer paragraphs than elements
|
||||
const maxCount = Math.max(elements.length, paragraphs.length);
|
||||
|
||||
for (let i = 0; i < maxCount; i++) {
|
||||
if (i < elements.length && i < paragraphs.length) {
|
||||
// Update existing element with corresponding paragraph
|
||||
elements[i].innerHTML = paragraphs[i].innerHTML;
|
||||
} else if (i < elements.length) {
|
||||
// More elements than paragraphs - clear extra elements
|
||||
elements[i].innerHTML = '';
|
||||
} else if (i < paragraphs.length) {
|
||||
// More paragraphs than elements - create new element
|
||||
const newElement = document.createElement('p');
|
||||
newElement.innerHTML = paragraphs[i].innerHTML;
|
||||
|
||||
// Insert after the last existing element
|
||||
const lastElement = elements[elements.length - 1];
|
||||
lastElement.parentNode.insertBefore(newElement, lastElement.nextSibling);
|
||||
elements.push(newElement); // Add to our elements array for future updates
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Export singleton instance
|
||||
export const markdownConverter = new MarkdownConverter();
|
||||
Reference in New Issue
Block a user