feat: Implement HTML-first style preservation system

- Add StyleContext class for extracting and applying HTML attributes/styles
- Enhance MarkdownConverter with style-aware conversion methods
- Switch backend storage from markdown to HTML with 'html' content type
- Update editor workflow to preserve CSS classes, IDs, and attributes
- Maintain markdown editing UX while storing HTML for style preservation
- Support complex attributes like rel, data-*, aria-*, etc.

This enables editing styled content like <a class="fancy" rel="me">text</a>
while preserving all styling attributes through the markdown editing process.
This commit is contained in:
2025-09-19 16:03:05 +02:00
parent 00c2ba34e6
commit b7998a4b3c
7 changed files with 498 additions and 26 deletions

View File

@@ -1,4 +1,5 @@
import { InsertrFormRenderer } from '../ui/form-renderer.js';
import { markdownConverter } from '../utils/markdown.js';
/**
* InsertrEditor - Content editing workflow and business logic
@@ -114,16 +115,35 @@ export class InsertrEditor {
try {
// Extract content value based on type
let contentValue;
let markdownContent;
if (meta.element.tagName.toLowerCase() === 'a') {
// For links, save the text content (URL is handled separately if needed)
contentValue = formData.text || formData;
markdownContent = formData.text || formData;
} else {
contentValue = formData.text || formData;
markdownContent = formData.text || formData;
}
// Convert markdown to HTML with style preservation
let contentValue;
const contentType = this.determineContentType(meta.element);
if (contentType === 'html') {
// Extract style context from original element and convert markdown to HTML
const { markdown, styleContext } = markdownConverter.htmlToMarkdownWithContext(meta.element.innerHTML, meta.element);
if (styleContext && styleContext.hasPreservableContent) {
// Convert markdown back to HTML with style preservation
contentValue = markdownConverter.markdownToHtmlWithStyles(markdownContent, styleContext);
} else {
// No styles to preserve, simple conversion
contentValue = markdownConverter.markdownToHtml(markdownContent);
}
} else {
// For other content types (text, link), use markdown as-is
contentValue = markdownContent;
}
// Universal upsert - server handles ID extraction/generation from markup
const contentType = this.determineContentType(meta.element);
const result = await this.apiClient.createContent(
contentValue,
contentType,
@@ -155,8 +175,8 @@ export class InsertrEditor {
return 'link';
}
// ALL text elements use markdown for consistent editing experience
return 'markdown';
// ALL text elements use HTML storage with markdown editing interface
return 'html';
}
handleCancel(meta) {