feat: implement insert mode with heading protection and fix content display bugs
Some checks failed
Test Suite / unit-tests (3.11) (push) Has been cancelled
Test Suite / unit-tests (3.12) (push) Has been cancelled
Test Suite / integration-tests (push) Has been cancelled
Test Suite / e2e-tests (push) Has been cancelled
Test Suite / performance-tests (push) Has been cancelled
Test Suite / code-quality (push) Has been cancelled
Test Suite / security-scan (push) Has been cancelled
Test Suite / test-summary (push) Has been cancelled
Some checks failed
Test Suite / unit-tests (3.11) (push) Has been cancelled
Test Suite / unit-tests (3.12) (push) Has been cancelled
Test Suite / integration-tests (push) Has been cancelled
Test Suite / e2e-tests (push) Has been cancelled
Test Suite / performance-tests (push) Has been cancelled
Test Suite / code-quality (push) Has been cancelled
Test Suite / security-scan (push) Has been cancelled
Test Suite / test-summary (push) Has been cancelled
This commit implements a comprehensive insert mode that preserves document structure
by protecting heading levels 1-3 from modification while allowing full content editing.
## Insert Mode Features
- CLI integration with --insert flag for md-render command
- Protected heading display (read-only) for levels 1-3
- Content-only editing for sections with protected headings
- Full editing capability for heading levels 4-6
- Theme-aware CSS styling for all UI themes
- Modal confirmation dialogs with proper positioning
- Section splitting with automatic protection inheritance
- Validation to prevent protected heading modifications
## Implementation Details
- Added MARKITECT_INSERT_MODE JavaScript flag and configuration
- Enhanced Section class with heading level detection and protection methods
- Added getHeadingText() and getHeadingContent() methods for content separation
- Implemented insert mode UI with protected heading display above content editor
- Added comprehensive CSS styling for insert mode components and modals
- Updated CLI with --insert option and mutual exclusion with --edit
## Bug Fixes
- Fixed JavaScript syntax errors caused by unescaped newline characters in string literals
- Corrected split('\n') and join('\n') calls to use proper escaping for Python string context
- Fixed heading level 3 display showing "null" by improving regex pattern matching
- Resolved content not displaying in edit/insert modes due to JavaScript parsing failures
## Documentation
- Updated UserInterfaceFramework.md with complete Insert Mode Editor section
- Added behavioral comparison table between edit and insert modes
- Updated Component Integration Matrix to reflect new capabilities
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -56,7 +56,7 @@ class CleanDocumentManager:
|
||||
}
|
||||
|
||||
def render_file(self, input_file: str, output_file: str, template: str = None, css: str = None,
|
||||
edit_mode: bool = False, editor_theme: str = 'github', keyboard_shortcuts: bool = True, nodogtag: bool = False) -> Dict[str, Any]:
|
||||
edit_mode: bool = False, insert_mode: bool = False, editor_theme: str = 'github', keyboard_shortcuts: bool = True, nodogtag: bool = False) -> Dict[str, Any]:
|
||||
"""
|
||||
Render a markdown file to HTML with optional clean editing capabilities.
|
||||
"""
|
||||
@@ -85,6 +85,7 @@ class CleanDocumentManager:
|
||||
css=css,
|
||||
template=template,
|
||||
edit_mode=edit_mode,
|
||||
insert_mode=insert_mode,
|
||||
editor_theme=editor_theme,
|
||||
keyboard_shortcuts=keyboard_shortcuts,
|
||||
original_filename=original_filename,
|
||||
@@ -619,6 +620,236 @@ class CleanDocumentManager:
|
||||
.ui-scroll-indicator.disabled.ui-scroll-indicator-down::before {{
|
||||
border-top-color: {props.get('editor_secondary_button', '#6c757d')};
|
||||
}}
|
||||
|
||||
/* Insert Mode Specific Styles */
|
||||
.markitect-insert-mode .ui-edit-floater-panel {{
|
||||
background: {props['editor_panel_bg']};
|
||||
border: 1px solid {props.get('editor_panel_border', '#dee2e6')};
|
||||
box-shadow: 0 4px 12px {props.get('editor_shadow', 'rgba(0,0,0,0.1)')};
|
||||
color: {props.get('editor_text_color', '#212529')};
|
||||
}}
|
||||
.markitect-insert-mode .ui-edit-floater-header {{
|
||||
color: {props.get('editor_text_color', '#212529')};
|
||||
}}
|
||||
.markitect-insert-mode .ui-edit-floater-header h3 {{
|
||||
color: {props.get('editor_text_color', '#212529')};
|
||||
}}
|
||||
.markitect-insert-mode .ui-edit-inline-panel {{
|
||||
background: {props['editor_panel_bg']};
|
||||
border: 1px solid {props.get('editor_panel_border', '#dee2e6')};
|
||||
box-shadow: 0 2px 8px {props.get('editor_shadow', 'rgba(0,0,0,0.1)')};
|
||||
color: {props.get('editor_text_color', '#212529')};
|
||||
border-radius: 8px;
|
||||
padding: 12px;
|
||||
margin: 8px 0;
|
||||
}}
|
||||
.markitect-insert-mode .ui-insert-protected-panel {{
|
||||
border-left: 4px solid #ff9800;
|
||||
}}
|
||||
.markitect-insert-mode .ui-insert-heading-display {{
|
||||
font-family: 'SF Mono', Monaco, 'Cascadia Code', monospace;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
padding: 8px 12px;
|
||||
background: {props.get('editor_warning_bg', '#fff3cd')};
|
||||
border: 1px solid {props.get('editor_warning_border', '#ffeaa7')};
|
||||
border-radius: 4px;
|
||||
border-left: 4px solid #007bff;
|
||||
color: {props.get('editor_warning_text', '#856404')};
|
||||
margin-bottom: 8px;
|
||||
}}
|
||||
.markitect-insert-mode .ui-insert-content-editor {{
|
||||
border: 1px solid {props.get('editor_panel_border', '#dee2e6')};
|
||||
color: {props.get('editor_text_color', '#212529')};
|
||||
background: {props.get('editor_button_bg', '#ffffff')};
|
||||
}}
|
||||
.markitect-insert-mode .ui-insert-content-editor:focus {{
|
||||
border-color: {props.get('editor_focus_color', '#007bff')};
|
||||
box-shadow: 0 0 0 2px {props.get('editor_focus_color', '#007bff')}33;
|
||||
}}
|
||||
.markitect-insert-mode .ui-edit-button {{
|
||||
background: {props.get('editor_button_bg', '#ffffff')};
|
||||
color: {props.get('editor_text_color', '#212529')};
|
||||
border: 1px solid {props.get('editor_panel_border', '#dee2e6')};
|
||||
padding: 8px 12px;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
font-size: 12px;
|
||||
min-width: 70px;
|
||||
font-weight: 500;
|
||||
transition: all 0.2s;
|
||||
}}
|
||||
.markitect-insert-mode .ui-edit-button:hover {{
|
||||
background: {props.get('editor_button_hover', '#e9ecef')};
|
||||
}}
|
||||
.markitect-insert-mode .ui-edit-button:active,
|
||||
.markitect-insert-mode .ui-edit-button.active {{
|
||||
background: {props.get('editor_button_active', '#dee2e6')};
|
||||
}}
|
||||
.markitect-insert-mode .ui-edit-button-accept {{
|
||||
background: #4caf50;
|
||||
color: white;
|
||||
}}
|
||||
.markitect-insert-mode .ui-edit-button-accept:hover {{
|
||||
background: #388e3c;
|
||||
}}
|
||||
.markitect-insert-mode .ui-edit-button-cancel {{
|
||||
background: #f44336;
|
||||
color: white;
|
||||
}}
|
||||
.markitect-insert-mode .ui-edit-button-cancel:hover {{
|
||||
background: #d32f2f;
|
||||
}}
|
||||
.markitect-insert-mode .ui-edit-button-reset {{
|
||||
background: #ff9800;
|
||||
color: white;
|
||||
}}
|
||||
.markitect-insert-mode .ui-edit-button-reset:hover {{
|
||||
background: #f57c00;
|
||||
}}
|
||||
.markitect-insert-mode .ui-edit-section-frame {{
|
||||
border: 2px solid {props.get('editor_focus_color', '#007bff')};
|
||||
box-shadow: 0 0 0 3px {props.get('editor_focus_color', '#007bff')}33;
|
||||
}}
|
||||
|
||||
/* Modal Overlay and Dialog Styles for Insert Mode */
|
||||
.markitect-insert-mode .ui-edit-modal-overlay {{
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
z-index: 999;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
transition: opacity 0.3s, visibility 0.3s;
|
||||
}}
|
||||
.markitect-insert-mode .ui-edit-modal-overlay.active {{
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
}}
|
||||
.markitect-insert-mode .ui-edit-modal {{
|
||||
background: {props['editor_panel_bg']};
|
||||
border: 1px solid {props.get('editor_panel_border', '#dee2e6')};
|
||||
box-shadow: 0 8px 32px {props.get('editor_shadow', 'rgba(0,0,0,0.2)')};
|
||||
color: {props.get('editor_text_color', '#212529')};
|
||||
border-radius: 8px;
|
||||
max-width: 600px;
|
||||
max-height: 80vh;
|
||||
width: 90%;
|
||||
overflow: hidden;
|
||||
transform: scale(0.9) translateY(-20px);
|
||||
transition: transform 0.3s;
|
||||
}}
|
||||
.markitect-insert-mode .ui-edit-modal-overlay.active .ui-edit-modal {{
|
||||
transform: scale(1) translateY(0);
|
||||
}}
|
||||
.markitect-insert-mode .ui-edit-modal-header {{
|
||||
padding: 20px 24px 16px;
|
||||
border-bottom: 1px solid {props.get('editor_panel_border', '#dee2e6')};
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}}
|
||||
.markitect-insert-mode .ui-edit-modal-title {{
|
||||
margin: 0;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: {props.get('editor_text_color', '#212529')};
|
||||
}}
|
||||
.markitect-insert-mode .ui-edit-modal-close {{
|
||||
background: transparent;
|
||||
border: none;
|
||||
font-size: 24px;
|
||||
cursor: pointer;
|
||||
color: {props.get('editor_text_color', '#212529')};
|
||||
padding: 0;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 4px;
|
||||
transition: background-color 0.2s;
|
||||
}}
|
||||
.markitect-insert-mode .ui-edit-modal-close:hover {{
|
||||
background: {props.get('editor_button_hover', '#e9ecef')};
|
||||
}}
|
||||
.markitect-insert-mode .ui-edit-modal-body {{
|
||||
padding: 20px 24px;
|
||||
max-height: 400px;
|
||||
overflow-y: auto;
|
||||
color: {props.get('editor_text_color', '#212529')};
|
||||
}}
|
||||
.markitect-insert-mode .ui-edit-modal-section {{
|
||||
margin-bottom: 8px;
|
||||
color: {props.get('editor_text_color', '#212529')};
|
||||
}}
|
||||
.markitect-insert-mode .ui-edit-modal-footer {{
|
||||
padding: 16px 24px 20px;
|
||||
border-top: 1px solid {props.get('editor_panel_border', '#dee2e6')};
|
||||
text-align: right;
|
||||
}}
|
||||
|
||||
/* Confirmation Dialog Styles for Insert Mode */
|
||||
.markitect-insert-mode .ui-edit-confirmation-modal {{
|
||||
max-width: 500px;
|
||||
}}
|
||||
.markitect-insert-mode .ui-edit-confirmation-content {{
|
||||
font-size: 16px;
|
||||
line-height: 1.5;
|
||||
margin-bottom: 24px;
|
||||
color: {props.get('editor_text_color', '#212529')};
|
||||
}}
|
||||
.markitect-insert-mode .ui-edit-confirmation-warning {{
|
||||
background: {props.get('editor_warning_bg', '#fff3cd')};
|
||||
color: {props.get('editor_warning_text', '#856404')};
|
||||
border: 1px solid {props.get('editor_warning_border', '#ffeaa7')};
|
||||
border-radius: 6px;
|
||||
padding: 12px 16px;
|
||||
margin: 16px 0;
|
||||
font-size: 14px;
|
||||
line-height: 1.4;
|
||||
}}
|
||||
.markitect-insert-mode .ui-edit-confirmation-buttons {{
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
justify-content: flex-end;
|
||||
margin-top: 24px;
|
||||
}}
|
||||
.markitect-insert-mode .ui-edit-button-confirm {{
|
||||
background: #dc3545;
|
||||
color: white;
|
||||
border: 1px solid #dc3545;
|
||||
padding: 10px 20px;
|
||||
border-radius: 6px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
}}
|
||||
.markitect-insert-mode .ui-edit-button-confirm:hover {{
|
||||
background: #c82333;
|
||||
border-color: #bd2130;
|
||||
}}
|
||||
.markitect-insert-mode .ui-edit-button-cancel {{
|
||||
background: {props.get('editor_button_bg', '#ffffff')};
|
||||
color: {props.get('editor_text_color', '#212529')};
|
||||
border: 1px solid {props.get('editor_panel_border', '#dee2e6')};
|
||||
padding: 10px 20px;
|
||||
border-radius: 6px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
}}
|
||||
.markitect-insert-mode .ui-edit-button-cancel:hover {{
|
||||
background: {props.get('editor_button_hover', '#e9ecef')};
|
||||
}}
|
||||
"""
|
||||
|
||||
return f"<style>{base_css}{heading_css}{text_css}{element_css}{link_css}{accent_css}{ui_css}</style>"
|
||||
@@ -658,7 +889,7 @@ class CleanDocumentManager:
|
||||
return self._generate_layered_css(layered_props)
|
||||
|
||||
def _generate_html_template(self, markdown_content: str, title: str, css: str = None, template: str = None,
|
||||
edit_mode: bool = False, editor_theme: str = 'github', keyboard_shortcuts: bool = True, original_filename: str = 'document', version_info: dict = None, nodogtag: bool = False) -> str:
|
||||
edit_mode: bool = False, insert_mode: bool = False, editor_theme: str = 'github', keyboard_shortcuts: bool = True, original_filename: str = 'document', version_info: dict = None, nodogtag: bool = False) -> str:
|
||||
"""Generate clean HTML template."""
|
||||
|
||||
# Add dogtag to markdown content if not disabled
|
||||
@@ -716,6 +947,28 @@ class CleanDocumentManager:
|
||||
editor_config = f"""
|
||||
const MARKITECT_EDIT_MODE = true;
|
||||
const MARKITECT_EDITOR_CONFIG = {{
|
||||
mode: 'edit',
|
||||
theme: '{editor_theme}',
|
||||
keyboardShortcuts: {str(keyboard_shortcuts).lower()},
|
||||
autosave: false,
|
||||
sections: true,
|
||||
originalFilename: '{original_filename}',
|
||||
version: '{version_str}',
|
||||
repoName: '{version_info['repo_name'] if version_info else 'Markitect'}'
|
||||
}};
|
||||
|
||||
// Make config available globally
|
||||
window.editorConfig = MARKITECT_EDITOR_CONFIG;"""
|
||||
elif insert_mode:
|
||||
body_classes = ' class="markitect-insert-mode"'
|
||||
|
||||
# Configuration for insert mode editor
|
||||
version_str = f"{version_info['repo_name']} v{version_info['version']}{version_info['git_info']}" if version_info else "Markitect v0.5.0.dev"
|
||||
editor_config = f"""
|
||||
const MARKITECT_INSERT_MODE = true;
|
||||
const MARKITECT_EDITOR_CONFIG = {{
|
||||
mode: 'insert',
|
||||
restrictedHeadingLevels: [1, 2, 3],
|
||||
theme: '{editor_theme}',
|
||||
keyboardShortcuts: {str(keyboard_shortcuts).lower()},
|
||||
autosave: false,
|
||||
@@ -728,7 +981,8 @@ class CleanDocumentManager:
|
||||
// Make config available globally
|
||||
window.editorConfig = MARKITECT_EDITOR_CONFIG;"""
|
||||
|
||||
# Load clean editor architecture
|
||||
# Load clean editor architecture for both edit and insert modes
|
||||
if edit_mode or insert_mode:
|
||||
editor_scripts = self._get_clean_editor_scripts()
|
||||
|
||||
# Generate the complete HTML template
|
||||
@@ -793,15 +1047,21 @@ class CleanDocumentManager:
|
||||
}}
|
||||
}}
|
||||
|
||||
// Step 2: Initialize edit capabilities if enabled
|
||||
if (typeof MARKITECT_EDIT_MODE !== 'undefined' && MARKITECT_EDIT_MODE) {{
|
||||
console.log("Initializing clean edit capabilities...");
|
||||
// Step 2: Initialize edit/insert capabilities if enabled
|
||||
if ((typeof MARKITECT_EDIT_MODE !== 'undefined' && MARKITECT_EDIT_MODE) ||
|
||||
(typeof MARKITECT_INSERT_MODE !== 'undefined' && MARKITECT_INSERT_MODE)) {{
|
||||
const mode = (typeof MARKITECT_INSERT_MODE !== 'undefined' && MARKITECT_INSERT_MODE) ? 'insert' : 'edit';
|
||||
console.log(`Initializing clean ${{mode}} capabilities...`);
|
||||
try {{
|
||||
console.log("Creating clean editor instance...");
|
||||
initializeCleanEditor();
|
||||
console.log("✓ Clean edit mode active - click any section to edit");
|
||||
if (mode === 'insert') {{
|
||||
console.log("✓ Clean insert mode active - click any section to edit (headings 1-3 protected)");
|
||||
}} else {{
|
||||
console.log("✓ Clean edit mode active - click any section to edit");
|
||||
}}
|
||||
}} catch (error) {{
|
||||
console.error("Clean edit mode failed to initialize:", error);
|
||||
console.error(`Clean ${{mode}} mode failed to initialize:`, error);
|
||||
}}
|
||||
}}
|
||||
|
||||
@@ -863,6 +1123,7 @@ class Section {
|
||||
this.editingMarkdown = null;
|
||||
this.pendingMarkdown = null;
|
||||
this.sectionType = sectionType;
|
||||
this.headingLevel = Section.detectHeadingLevel(originalMarkdown);
|
||||
this.state = EditState.ORIGINAL;
|
||||
this.domElement = null;
|
||||
this.lastSaved = null;
|
||||
@@ -987,6 +1248,39 @@ class Section {
|
||||
}
|
||||
return SectionType.PARAGRAPH;
|
||||
}
|
||||
|
||||
static detectHeadingLevel(markdown) {
|
||||
const trimmed = markdown.trim();
|
||||
const match = trimmed.match(/^(#{1,6})\s/);
|
||||
return match ? match[1].length : null;
|
||||
}
|
||||
|
||||
isHeading() {
|
||||
return this.sectionType === SectionType.HEADING;
|
||||
}
|
||||
|
||||
isProtectedHeading() {
|
||||
if (!this.isHeading()) return false;
|
||||
// Check if we're in insert mode and if this heading level is protected
|
||||
const config = window.editorConfig || {};
|
||||
const restrictedLevels = config.restrictedHeadingLevels || [];
|
||||
return config.mode === 'insert' && restrictedLevels.includes(this.headingLevel);
|
||||
}
|
||||
|
||||
getHeadingText() {
|
||||
if (!this.isHeading()) return null;
|
||||
// Extract first line for heading text
|
||||
const firstLine = this.originalMarkdown.trim().split('\\n')[0];
|
||||
const match = firstLine.match(/^(#{1,6})\s+(.+)$/);
|
||||
return match ? match[2] : null;
|
||||
}
|
||||
|
||||
getHeadingContent() {
|
||||
if (!this.isHeading()) return this.currentMarkdown;
|
||||
const lines = this.currentMarkdown.split('\\n');
|
||||
// Return content after the heading line
|
||||
return lines.slice(1).join('\\n');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1020,7 +1314,7 @@ class SectionManager {
|
||||
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
const line = lines[i];
|
||||
const isHeading = /^#{1,6}\\s/.test(line);
|
||||
const isHeading = /^#{1,6}\s/.test(line);
|
||||
const isNewParagraph = line.trim() && i > 0 && !lines[i-1].trim();
|
||||
const isNewSection = isHeading || isNewParagraph;
|
||||
|
||||
@@ -1083,6 +1377,15 @@ class SectionManager {
|
||||
throw new Error(`Section ${sectionId} not found`);
|
||||
}
|
||||
|
||||
// For protected headings in insert mode, validate that heading hasn't changed
|
||||
if (section.isProtectedHeading()) {
|
||||
const originalHeadingLine = section.originalMarkdown.split('\\n')[0];
|
||||
const newHeadingLine = section.editingMarkdown.split('\\n')[0];
|
||||
if (originalHeadingLine !== newHeadingLine) {
|
||||
throw new Error(`Cannot modify protected heading in insert mode. Heading level ${section.headingLevel} is read-only.`);
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the edited content contains new headings that would create splits
|
||||
const newContent = section.editingMarkdown;
|
||||
const originalContent = section.originalMarkdown;
|
||||
@@ -1113,14 +1416,14 @@ class SectionManager {
|
||||
|
||||
// Count headings in new content
|
||||
for (const line of lines) {
|
||||
if (/^#{1,6}\\s/.test(line.trim())) {
|
||||
if (/^#{1,6}\s/.test(line.trim())) {
|
||||
newHeadingCount++;
|
||||
}
|
||||
}
|
||||
|
||||
// Count headings in original content
|
||||
for (const line of originalLines) {
|
||||
if (/^#{1,6}\\s/.test(line.trim())) {
|
||||
if (/^#{1,6}\s/.test(line.trim())) {
|
||||
originalHeadingCount++;
|
||||
}
|
||||
}
|
||||
@@ -1188,7 +1491,7 @@ class SectionManager {
|
||||
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
const line = lines[i];
|
||||
const isHeading = /^#{1,6}\\s/.test(line.trim());
|
||||
const isHeading = /^#{1,6}\s/.test(line.trim());
|
||||
|
||||
if (isHeading) {
|
||||
// When we encounter a heading, complete any previous section
|
||||
@@ -1373,18 +1676,54 @@ class DOMRenderer {
|
||||
|
||||
this.hideCurrentEditor();
|
||||
|
||||
const section = this.sectionManager.sections.get(sectionId);
|
||||
const isProtectedHeading = section && section.isProtectedHeading();
|
||||
|
||||
const editorContainer = document.createElement('div');
|
||||
editorContainer.className = 'ui-edit-inline-panel';
|
||||
editorContainer.className = isProtectedHeading ? 'ui-edit-inline-panel ui-insert-protected-panel' : 'ui-edit-inline-panel';
|
||||
editorContainer.style.cssText = `
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
align-items: flex-start;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
// If this is a protected heading, show the heading display
|
||||
if (isProtectedHeading) {
|
||||
const headingDisplay = document.createElement('div');
|
||||
headingDisplay.className = 'ui-insert-heading-display';
|
||||
const headingText = section.getHeadingText();
|
||||
const headingLevel = section.headingLevel;
|
||||
const headingMarkdown = '#'.repeat(headingLevel) + ' ' + headingText;
|
||||
headingDisplay.textContent = headingMarkdown;
|
||||
headingDisplay.style.cssText = `
|
||||
font-family: 'SF Mono', Monaco, 'Cascadia Code', monospace;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
padding: 8px 12px;
|
||||
background: rgba(0, 0, 0, 0.05);
|
||||
border-radius: 4px;
|
||||
border-left: 4px solid #007bff;
|
||||
color: #333;
|
||||
`;
|
||||
editorContainer.appendChild(headingDisplay);
|
||||
}
|
||||
|
||||
// Create content editing area
|
||||
const editingArea = document.createElement('div');
|
||||
editingArea.style.cssText = `
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
align-items: flex-start;
|
||||
`;
|
||||
|
||||
const textarea = document.createElement('textarea');
|
||||
textarea.className = 'ui-edit-textarea ui-edit-textarea-main';
|
||||
textarea.value = content;
|
||||
textarea.className = isProtectedHeading ? 'ui-edit-textarea ui-insert-content-editor' : 'ui-edit-textarea ui-edit-textarea-main';
|
||||
|
||||
// For protected headings, only show content after the heading
|
||||
const textareaContent = isProtectedHeading ? section.getHeadingContent() : content;
|
||||
textarea.value = textareaContent;
|
||||
|
||||
textarea.style.cssText = `
|
||||
flex: 1;
|
||||
min-height: 100px;
|
||||
@@ -1397,7 +1736,14 @@ class DOMRenderer {
|
||||
`;
|
||||
|
||||
textarea.addEventListener('input', () => {
|
||||
this.sectionManager.updateContent(sectionId, textarea.value);
|
||||
if (isProtectedHeading) {
|
||||
// Reconstruct full content with protected heading
|
||||
const headingLine = section.originalMarkdown.split('\\n')[0];
|
||||
const fullContent = headingLine + '\\n' + textarea.value;
|
||||
this.sectionManager.updateContent(sectionId, fullContent);
|
||||
} else {
|
||||
this.sectionManager.updateContent(sectionId, textarea.value);
|
||||
}
|
||||
});
|
||||
textarea.addEventListener('keydown', this.handleKeydown);
|
||||
|
||||
@@ -1420,8 +1766,9 @@ class DOMRenderer {
|
||||
controls.appendChild(createButton('✗ Cancel', 'ui-edit-button ui-edit-button-cancel', () => this.handleCancel(sectionId)));
|
||||
controls.appendChild(createButton('🔄 Reset', 'ui-edit-button ui-edit-button-reset', () => this.handleReset(sectionId)));
|
||||
|
||||
editorContainer.appendChild(textarea);
|
||||
editorContainer.appendChild(controls);
|
||||
editingArea.appendChild(textarea);
|
||||
editingArea.appendChild(controls);
|
||||
editorContainer.appendChild(editingArea);
|
||||
|
||||
element.innerHTML = '';
|
||||
element.appendChild(editorContainer);
|
||||
|
||||
Reference in New Issue
Block a user