fix: resolve critical JavaScript syntax errors in md-render --edit
Fix broken edit mode that prevented content rendering due to: - Unescaped newline literals causing JavaScript string syntax errors - Inconsistent brace escaping in f-string templates - Template literal syntax issues with variable interpolation The edit mode now properly renders content AND provides editing capabilities. Also added html-inject-editing command for standalone HTML enhancement with graceful fallback options. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -574,35 +574,35 @@ class DocumentManager:
|
|||||||
const sections = content.querySelectorAll('.markitect-section-editable');
|
const sections = content.querySelectorAll('.markitect-section-editable');
|
||||||
let reconstructed = '';
|
let reconstructed = '';
|
||||||
|
|
||||||
sections.forEach(section => {
|
sections.forEach(section => {{
|
||||||
const tagName = section.tagName.toLowerCase();
|
const tagName = section.tagName.toLowerCase();
|
||||||
const text = section.textContent.trim();
|
const text = section.textContent.trim();
|
||||||
|
|
||||||
if (tagName.startsWith('h')) {
|
if (tagName.startsWith('h')) {{
|
||||||
const level = parseInt(tagName.charAt(1));
|
const level = parseInt(tagName.charAt(1));
|
||||||
reconstructed += '#'.repeat(level) + ' ' + text + '\n\n';
|
reconstructed += '#'.repeat(level) + ' ' + text + '\\n\\n';
|
||||||
} else if (tagName === 'p') {
|
}} else if (tagName === 'p') {{
|
||||||
reconstructed += text + '\n\n';
|
reconstructed += text + '\\n\\n';
|
||||||
} else if (tagName === 'blockquote') {
|
}} else if (tagName === 'blockquote') {{
|
||||||
reconstructed += '> ' + text + '\n\n';
|
reconstructed += '> ' + text + '\\n\\n';
|
||||||
} else if (tagName === 'pre') {
|
}} else if (tagName === 'pre') {{
|
||||||
reconstructed += '```\n' + text + '\n```\n\n';
|
reconstructed += '```\\n' + text + '\\n```\\n\\n';
|
||||||
} else if (tagName === 'ul') {
|
}} else if (tagName === 'ul') {{
|
||||||
const items = section.querySelectorAll('li');
|
const items = section.querySelectorAll('li');
|
||||||
items.forEach(item => {
|
items.forEach(item => {{
|
||||||
reconstructed += '- ' + item.textContent.trim() + '\n';
|
reconstructed += '- ' + item.textContent.trim() + '\\n';
|
||||||
});
|
}});
|
||||||
reconstructed += '\n';
|
reconstructed += '\\n';
|
||||||
} else if (tagName === 'ol') {
|
}} else if (tagName === 'ol') {{
|
||||||
const items = section.querySelectorAll('li');
|
const items = section.querySelectorAll('li');
|
||||||
items.forEach((item, index) => {
|
items.forEach((item, index) => {{
|
||||||
reconstructed += `${index + 1}. ` + item.textContent.trim() + '\n';
|
reconstructed += (index + 1) + '. ' + item.textContent.trim() + '\\n';
|
||||||
});
|
}});
|
||||||
reconstructed += '\n';
|
reconstructed += '\\n';
|
||||||
} else {
|
}} else {{
|
||||||
reconstructed += text + '\n\n';
|
reconstructed += text + '\\n\\n';
|
||||||
}
|
}}
|
||||||
});
|
}});
|
||||||
|
|
||||||
return reconstructed.trim();
|
return reconstructed.trim();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1501,7 +1501,8 @@ class MarkdownCommandsPlugin(CommandPlugin):
|
|||||||
'md-explode': md_explode_command,
|
'md-explode': md_explode_command,
|
||||||
'md-implode': md_implode_command,
|
'md-implode': md_implode_command,
|
||||||
'md-package': md_package_command,
|
'md-package': md_package_command,
|
||||||
'md-transclude': md_transclude_command
|
'md-transclude': md_transclude_command,
|
||||||
|
'html-inject-editing': html_inject_editing
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -2981,3 +2982,544 @@ class FilenameDecoder:
|
|||||||
def decode_batch(self, filenames):
|
def decode_batch(self, filenames):
|
||||||
"""Process multiple filenames in batch."""
|
"""Process multiple filenames in batch."""
|
||||||
return [self.decode(filename) for filename in filenames]
|
return [self.decode(filename) for filename in filenames]
|
||||||
|
|
||||||
|
|
||||||
|
# ==============================================================================
|
||||||
|
# HTML Editing Injection Command - Graceful Enhancement System
|
||||||
|
# ==============================================================================
|
||||||
|
|
||||||
|
@click.command()
|
||||||
|
@click.argument('html_file', type=click.Path(exists=True))
|
||||||
|
@click.option('--output', '-o', type=click.Path(),
|
||||||
|
help='Output HTML file with editing capabilities (default: <input>-editable.html)')
|
||||||
|
@click.option('--editor-theme', default='github',
|
||||||
|
type=click.Choice(['github', 'monokai', 'tomorrow', 'dark']),
|
||||||
|
help='Editor theme for edit mode (default: github)')
|
||||||
|
@click.option('--keyboard-shortcuts', is_flag=True, default=True,
|
||||||
|
help='Enable keyboard shortcuts in edit mode')
|
||||||
|
@click.option('--fallback-mode', type=click.Choice(['graceful', 'minimal', 'none']),
|
||||||
|
default='graceful', help='Fallback strategy when JavaScript fails')
|
||||||
|
@click.option('--backup/--no-backup', default=True,
|
||||||
|
help='Create backup of original file')
|
||||||
|
@click.option('--dry-run', is_flag=True,
|
||||||
|
help='Show what would be done without making changes')
|
||||||
|
@click.pass_context
|
||||||
|
def html_inject_editing(ctx, html_file, output, editor_theme, keyboard_shortcuts,
|
||||||
|
fallback_mode, backup, dry_run):
|
||||||
|
"""
|
||||||
|
Inject editing capabilities into existing HTML files.
|
||||||
|
|
||||||
|
This command adds JavaScript editing functionality to any HTML file
|
||||||
|
containing markdown content. It provides graceful fallback when
|
||||||
|
JavaScript fails, ensuring the document remains readable.
|
||||||
|
|
||||||
|
HTML_FILE: Path to the HTML file to enhance with editing capabilities
|
||||||
|
|
||||||
|
Fallback modes:
|
||||||
|
graceful - Full fallback with basic editing via contenteditable
|
||||||
|
minimal - Fallback to read-only with error messages
|
||||||
|
none - No fallback, editing simply won't work if JS fails
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
markitect html-inject-editing document.html
|
||||||
|
markitect html-inject-editing doc.html --output doc-editable.html
|
||||||
|
markitect html-inject-editing page.html --fallback-mode minimal --no-backup
|
||||||
|
"""
|
||||||
|
config = ctx.obj or {}
|
||||||
|
|
||||||
|
try:
|
||||||
|
input_path = Path(html_file)
|
||||||
|
|
||||||
|
# Determine output path
|
||||||
|
if output:
|
||||||
|
output_path = Path(output)
|
||||||
|
else:
|
||||||
|
# Create name like "document-editable.html"
|
||||||
|
stem = input_path.stem
|
||||||
|
suffix = input_path.suffix
|
||||||
|
output_path = input_path.parent / f"{stem}-editable{suffix}"
|
||||||
|
|
||||||
|
if dry_run:
|
||||||
|
click.echo(f"🔍 Would inject editing capabilities into: {input_path}")
|
||||||
|
click.echo(f"📝 Would create enhanced file: {output_path}")
|
||||||
|
click.echo(f"🎨 Editor theme: {editor_theme}")
|
||||||
|
click.echo(f"⌨️ Keyboard shortcuts: {'enabled' if keyboard_shortcuts else 'disabled'}")
|
||||||
|
click.echo(f"🛡️ Fallback mode: {fallback_mode}")
|
||||||
|
if backup:
|
||||||
|
backup_path = input_path.parent / f"{input_path.stem}.backup{input_path.suffix}"
|
||||||
|
click.echo(f"💾 Would create backup: {backup_path}")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Create backup if requested
|
||||||
|
if backup and not output:
|
||||||
|
backup_path = input_path.parent / f"{input_path.stem}.backup{input_path.suffix}"
|
||||||
|
backup_path.write_text(input_path.read_text(encoding='utf-8'), encoding='utf-8')
|
||||||
|
click.echo(f"💾 Created backup: {backup_path}")
|
||||||
|
|
||||||
|
# Read original HTML
|
||||||
|
html_content = input_path.read_text(encoding='utf-8')
|
||||||
|
|
||||||
|
# Inject editing capabilities
|
||||||
|
enhanced_html = inject_editing_capabilities(
|
||||||
|
html_content=html_content,
|
||||||
|
editor_theme=editor_theme,
|
||||||
|
keyboard_shortcuts=keyboard_shortcuts,
|
||||||
|
fallback_mode=fallback_mode
|
||||||
|
)
|
||||||
|
|
||||||
|
# Write enhanced HTML
|
||||||
|
output_path.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
output_path.write_text(enhanced_html, encoding='utf-8')
|
||||||
|
|
||||||
|
click.echo(f"✨ Enhanced HTML with editing capabilities: {output_path}")
|
||||||
|
click.echo(f"🎨 Editor theme: {editor_theme}")
|
||||||
|
click.echo(f"🛡️ Fallback mode: {fallback_mode}")
|
||||||
|
|
||||||
|
if config.get('verbose', False):
|
||||||
|
click.echo(f"⌨️ Keyboard shortcuts: {'enabled' if keyboard_shortcuts else 'disabled'}")
|
||||||
|
click.echo(f"📄 Original size: {len(html_content)} chars")
|
||||||
|
click.echo(f"📄 Enhanced size: {len(enhanced_html)} chars")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
click.echo(f"Error injecting editing capabilities: {e}", err=True)
|
||||||
|
raise click.Abort()
|
||||||
|
|
||||||
|
|
||||||
|
def inject_editing_capabilities(html_content: str, editor_theme: str = 'github',
|
||||||
|
keyboard_shortcuts: bool = True,
|
||||||
|
fallback_mode: str = 'graceful') -> str:
|
||||||
|
"""
|
||||||
|
Inject editing capabilities into HTML content with graceful fallback.
|
||||||
|
|
||||||
|
This function adds editing functionality that degrades gracefully:
|
||||||
|
1. Full editing if JavaScript loads successfully
|
||||||
|
2. Basic contenteditable if CDN fails but DOM works
|
||||||
|
3. Read-only mode with clear error messages if everything fails
|
||||||
|
"""
|
||||||
|
import re
|
||||||
|
|
||||||
|
# Generate the editing enhancement script
|
||||||
|
enhancement_script = generate_editing_enhancement_script(
|
||||||
|
editor_theme=editor_theme,
|
||||||
|
keyboard_shortcuts=keyboard_shortcuts,
|
||||||
|
fallback_mode=fallback_mode
|
||||||
|
)
|
||||||
|
|
||||||
|
# Try to inject before closing </body> tag
|
||||||
|
body_close_pattern = r'</body>'
|
||||||
|
if re.search(body_close_pattern, html_content, re.IGNORECASE):
|
||||||
|
enhanced = re.sub(
|
||||||
|
body_close_pattern,
|
||||||
|
f'{enhancement_script}\n</body>',
|
||||||
|
html_content,
|
||||||
|
flags=re.IGNORECASE
|
||||||
|
)
|
||||||
|
return enhanced
|
||||||
|
|
||||||
|
# Fallback: inject before closing </html> tag
|
||||||
|
html_close_pattern = r'</html>'
|
||||||
|
if re.search(html_close_pattern, html_content, re.IGNORECASE):
|
||||||
|
enhanced = re.sub(
|
||||||
|
html_close_pattern,
|
||||||
|
f'{enhancement_script}\n</html>',
|
||||||
|
html_content,
|
||||||
|
flags=re.IGNORECASE
|
||||||
|
)
|
||||||
|
return enhanced
|
||||||
|
|
||||||
|
# Last fallback: append to end of content
|
||||||
|
return html_content + '\n' + enhancement_script
|
||||||
|
|
||||||
|
|
||||||
|
def generate_editing_enhancement_script(editor_theme: str = 'github',
|
||||||
|
keyboard_shortcuts: bool = True,
|
||||||
|
fallback_mode: str = 'graceful') -> str:
|
||||||
|
"""
|
||||||
|
Generate the JavaScript enhancement script with graceful fallback.
|
||||||
|
|
||||||
|
This creates a self-contained script that:
|
||||||
|
1. Attempts to load required libraries from CDN
|
||||||
|
2. Falls back gracefully if CDN fails
|
||||||
|
3. Provides basic editing even without external dependencies
|
||||||
|
"""
|
||||||
|
|
||||||
|
fallback_css = """
|
||||||
|
<style id="markitect-fallback-styles">
|
||||||
|
.markitect-edit-fallback {
|
||||||
|
border: 2px dashed #ffa500;
|
||||||
|
background-color: #fff3cd;
|
||||||
|
padding: 10px;
|
||||||
|
margin: 5px 0;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: text;
|
||||||
|
}
|
||||||
|
.markitect-edit-fallback:hover {
|
||||||
|
background-color: #ffeaa7;
|
||||||
|
border-color: #e17055;
|
||||||
|
}
|
||||||
|
.markitect-edit-fallback[contenteditable="true"] {
|
||||||
|
border-color: #00b894;
|
||||||
|
background-color: #d1f2eb;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
.markitect-fallback-header {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
background: #fd7f00;
|
||||||
|
color: white;
|
||||||
|
padding: 8px 15px;
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||||
|
font-size: 14px;
|
||||||
|
z-index: 10000;
|
||||||
|
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
||||||
|
}
|
||||||
|
.markitect-fallback-warning {
|
||||||
|
background: #e74c3c;
|
||||||
|
color: white;
|
||||||
|
padding: 10px;
|
||||||
|
border-radius: 4px;
|
||||||
|
margin: 10px 0;
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||||
|
}
|
||||||
|
body { margin-top: 50px !important; }
|
||||||
|
</style>
|
||||||
|
"""
|
||||||
|
|
||||||
|
script_content = f"""
|
||||||
|
{fallback_css}
|
||||||
|
|
||||||
|
<script>
|
||||||
|
(function() {{
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
// Configuration
|
||||||
|
const CONFIG = {{
|
||||||
|
editorTheme: '{editor_theme}',
|
||||||
|
keyboardShortcuts: {str(keyboard_shortcuts).lower()},
|
||||||
|
fallbackMode: '{fallback_mode}'
|
||||||
|
}};
|
||||||
|
|
||||||
|
// State management
|
||||||
|
let editingState = {{
|
||||||
|
isEditMode: false,
|
||||||
|
currentEditElement: null,
|
||||||
|
originalContent: new Map(),
|
||||||
|
hasErrors: false,
|
||||||
|
cdnLoaded: false
|
||||||
|
}};
|
||||||
|
|
||||||
|
// Graceful enhancement entry point
|
||||||
|
function initializeEditingEnhancement() {{
|
||||||
|
console.log('[MarkiTect] Initializing editing enhancement...');
|
||||||
|
|
||||||
|
// Step 1: Try to load external dependencies
|
||||||
|
loadExternalDependencies()
|
||||||
|
.then(() => {{
|
||||||
|
console.log('[MarkiTect] External libraries loaded successfully');
|
||||||
|
editingState.cdnLoaded = true;
|
||||||
|
initializeFullEditor();
|
||||||
|
}})
|
||||||
|
.catch((error) => {{
|
||||||
|
console.warn('[MarkiTect] CDN loading failed:', error);
|
||||||
|
editingState.hasErrors = true;
|
||||||
|
|
||||||
|
if (CONFIG.fallbackMode === 'graceful') {{
|
||||||
|
initializeFallbackEditor();
|
||||||
|
}} else if (CONFIG.fallbackMode === 'minimal') {{
|
||||||
|
showMinimalFallback();
|
||||||
|
}} else {{
|
||||||
|
showNoFallback();
|
||||||
|
}}
|
||||||
|
}});
|
||||||
|
}}
|
||||||
|
|
||||||
|
// Load external dependencies (marked.js, etc.)
|
||||||
|
function loadExternalDependencies() {{
|
||||||
|
return new Promise((resolve, reject) => {{
|
||||||
|
// Check if marked is already available
|
||||||
|
if (typeof marked !== 'undefined') {{
|
||||||
|
resolve();
|
||||||
|
return;
|
||||||
|
}}
|
||||||
|
|
||||||
|
// Try to load marked.js from CDN
|
||||||
|
const script = document.createElement('script');
|
||||||
|
script.src = 'https://cdn.jsdelivr.net/npm/marked/marked.min.js';
|
||||||
|
|
||||||
|
// Timeout after 5 seconds
|
||||||
|
const timeout = setTimeout(() => {{
|
||||||
|
reject(new Error('CDN loading timeout'));
|
||||||
|
}}, 5000);
|
||||||
|
|
||||||
|
script.onload = () => {{
|
||||||
|
clearTimeout(timeout);
|
||||||
|
if (typeof marked !== 'undefined') {{
|
||||||
|
resolve();
|
||||||
|
}} else {{
|
||||||
|
reject(new Error('marked.js loaded but not available'));
|
||||||
|
}}
|
||||||
|
}};
|
||||||
|
|
||||||
|
script.onerror = () => {{
|
||||||
|
clearTimeout(timeout);
|
||||||
|
reject(new Error('Failed to load marked.js'));
|
||||||
|
}};
|
||||||
|
|
||||||
|
document.head.appendChild(script);
|
||||||
|
}});
|
||||||
|
}}
|
||||||
|
|
||||||
|
// Full editor with all features
|
||||||
|
function initializeFullEditor() {{
|
||||||
|
console.log('[MarkiTect] Initializing full editor...');
|
||||||
|
|
||||||
|
addFloatingHeader('✨ Full Edit Mode Active - Click any section to edit');
|
||||||
|
makeContentEditable();
|
||||||
|
|
||||||
|
if (CONFIG.keyboardShortcuts) {{
|
||||||
|
setupKeyboardShortcuts();
|
||||||
|
}}
|
||||||
|
}}
|
||||||
|
|
||||||
|
// Fallback editor with basic functionality
|
||||||
|
function initializeFallbackEditor() {{
|
||||||
|
console.log('[MarkiTect] Initializing fallback editor...');
|
||||||
|
|
||||||
|
addFloatingHeader('⚠️ Fallback Edit Mode - Limited functionality (CDN failed)', 'warning');
|
||||||
|
makeContentEditableBasic();
|
||||||
|
}}
|
||||||
|
|
||||||
|
// Minimal mode with just error reporting
|
||||||
|
function showMinimalFallback() {{
|
||||||
|
console.log('[MarkiTect] Showing minimal fallback...');
|
||||||
|
|
||||||
|
addFloatingHeader('❌ Edit Mode Unavailable - Network issues detected', 'error');
|
||||||
|
addErrorMessage('Editing capabilities could not be loaded due to network restrictions. Document is read-only.');
|
||||||
|
}}
|
||||||
|
|
||||||
|
// No fallback mode
|
||||||
|
function showNoFallback() {{
|
||||||
|
console.log('[MarkiTect] No fallback mode - editing disabled');
|
||||||
|
// Silent failure - no editing capabilities
|
||||||
|
}}
|
||||||
|
|
||||||
|
// Add floating header for status
|
||||||
|
function addFloatingHeader(message, type = 'info') {{
|
||||||
|
const header = document.createElement('div');
|
||||||
|
header.className = 'markitect-fallback-header';
|
||||||
|
header.innerHTML = `
|
||||||
|
<span>${{message}}</span>
|
||||||
|
<button onclick="this.parentElement.style.display='none'" style="float: right; background: none; border: none; color: white; cursor: pointer;">×</button>
|
||||||
|
`;
|
||||||
|
document.body.insertBefore(header, document.body.firstChild);
|
||||||
|
}}
|
||||||
|
|
||||||
|
// Add error message to content
|
||||||
|
function addErrorMessage(message) {{
|
||||||
|
const errorDiv = document.createElement('div');
|
||||||
|
errorDiv.className = 'markitect-fallback-warning';
|
||||||
|
errorDiv.innerHTML = `
|
||||||
|
<strong>Editing Unavailable:</strong> ${{message}}
|
||||||
|
<br><small>This document remains fully readable. Editing requires JavaScript and network access.</small>
|
||||||
|
`;
|
||||||
|
|
||||||
|
const firstElement = document.body.firstElementChild;
|
||||||
|
if (firstElement) {{
|
||||||
|
document.body.insertBefore(errorDiv, firstElement.nextSibling);
|
||||||
|
}} else {{
|
||||||
|
document.body.appendChild(errorDiv);
|
||||||
|
}}
|
||||||
|
}}
|
||||||
|
|
||||||
|
// Make content editable with full markdown support
|
||||||
|
function makeContentEditable() {{
|
||||||
|
const sections = document.querySelectorAll('h1, h2, h3, h4, h5, h6, p, blockquote, pre, ul, ol, li');
|
||||||
|
|
||||||
|
sections.forEach((section, index) => {{
|
||||||
|
section.classList.add('markitect-section-editable');
|
||||||
|
section.setAttribute('data-section-id', index);
|
||||||
|
section.addEventListener('click', handleSectionClick);
|
||||||
|
section.style.cursor = 'pointer';
|
||||||
|
section.title = 'Click to edit this section';
|
||||||
|
}});
|
||||||
|
}}
|
||||||
|
|
||||||
|
// Basic contenteditable fallback
|
||||||
|
function makeContentEditableBasic() {{
|
||||||
|
const sections = document.querySelectorAll('h1, h2, h3, h4, h5, h6, p, blockquote');
|
||||||
|
|
||||||
|
sections.forEach((section, index) => {{
|
||||||
|
section.classList.add('markitect-edit-fallback');
|
||||||
|
section.setAttribute('contenteditable', 'true');
|
||||||
|
section.setAttribute('data-section-id', index);
|
||||||
|
section.title = 'Basic editing mode - formatting may be limited';
|
||||||
|
|
||||||
|
// Store original content
|
||||||
|
editingState.originalContent.set(index, section.innerHTML);
|
||||||
|
|
||||||
|
// Add simple save/restore on blur
|
||||||
|
section.addEventListener('blur', () => {{
|
||||||
|
// Basic validation - could be enhanced
|
||||||
|
if (section.innerText.trim() === '') {{
|
||||||
|
section.innerHTML = editingState.originalContent.get(index);
|
||||||
|
}}
|
||||||
|
}});
|
||||||
|
}});
|
||||||
|
}}
|
||||||
|
|
||||||
|
// Handle section click for full editor
|
||||||
|
function handleSectionClick(event) {{
|
||||||
|
const section = event.target.closest('.markitect-section-editable');
|
||||||
|
if (!section) return;
|
||||||
|
|
||||||
|
const sectionId = section.getAttribute('data-section-id');
|
||||||
|
|
||||||
|
if (editingState.currentEditElement && editingState.currentEditElement !== section) {{
|
||||||
|
// Cancel current edit if clicking on different section
|
||||||
|
const currentId = editingState.currentEditElement.getAttribute('data-section-id');
|
||||||
|
cancelEdit(currentId);
|
||||||
|
}}
|
||||||
|
|
||||||
|
if (!section.querySelector('textarea')) {{
|
||||||
|
startEditing(section, sectionId);
|
||||||
|
}}
|
||||||
|
}}
|
||||||
|
|
||||||
|
// Start editing a section
|
||||||
|
function startEditing(section, sectionId) {{
|
||||||
|
editingState.originalContent.set(sectionId, section.innerHTML);
|
||||||
|
editingState.currentEditElement = section;
|
||||||
|
|
||||||
|
const textarea = document.createElement('textarea');
|
||||||
|
textarea.value = htmlToMarkdown(section.innerHTML);
|
||||||
|
textarea.style.cssText = `
|
||||||
|
width: 100%;
|
||||||
|
min-height: 100px;
|
||||||
|
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
|
||||||
|
font-size: 14px;
|
||||||
|
border: 2px solid #007acc;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 8px;
|
||||||
|
background: #f8f9fa;
|
||||||
|
resize: vertical;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const controls = document.createElement('div');
|
||||||
|
controls.style.cssText = 'margin-top: 5px;';
|
||||||
|
controls.innerHTML = `
|
||||||
|
<button onclick="markitectApplyEdit('${{sectionId}}')" style="margin-right: 5px;">Apply</button>
|
||||||
|
<button onclick="markitectCancelEdit('${{sectionId}}')" style="margin-right: 5px;">Cancel</button>
|
||||||
|
<small style="color: #666;">Ctrl+Enter to apply, Esc to cancel</small>
|
||||||
|
`;
|
||||||
|
|
||||||
|
section.innerHTML = '';
|
||||||
|
section.appendChild(textarea);
|
||||||
|
section.appendChild(controls);
|
||||||
|
textarea.focus();
|
||||||
|
|
||||||
|
// Keyboard shortcuts for editing
|
||||||
|
textarea.addEventListener('keydown', (e) => {{
|
||||||
|
if (e.ctrlKey && e.key === 'Enter') {{
|
||||||
|
e.preventDefault();
|
||||||
|
applyEdit(sectionId);
|
||||||
|
}} else if (e.key === 'Escape') {{
|
||||||
|
e.preventDefault();
|
||||||
|
cancelEdit(sectionId);
|
||||||
|
}}
|
||||||
|
}});
|
||||||
|
}}
|
||||||
|
|
||||||
|
// Apply edit
|
||||||
|
function applyEdit(sectionId) {{
|
||||||
|
const section = document.querySelector(`[data-section-id="${{sectionId}}"]`);
|
||||||
|
const textarea = section.querySelector('textarea');
|
||||||
|
|
||||||
|
if (textarea && editingState.cdnLoaded && typeof marked !== 'undefined') {{
|
||||||
|
// Full markdown rendering
|
||||||
|
section.innerHTML = marked.parse(textarea.value);
|
||||||
|
}} else if (textarea) {{
|
||||||
|
// Basic text with line breaks
|
||||||
|
section.innerHTML = textarea.value.replace(/\\n/g, '<br>');
|
||||||
|
}}
|
||||||
|
|
||||||
|
editingState.currentEditElement = null;
|
||||||
|
makeContentEditable(); // Re-attach click handlers
|
||||||
|
}}
|
||||||
|
|
||||||
|
// Cancel edit
|
||||||
|
function cancelEdit(sectionId) {{
|
||||||
|
const section = document.querySelector(`[data-section-id="${{sectionId}}"]`);
|
||||||
|
section.innerHTML = editingState.originalContent.get(sectionId);
|
||||||
|
editingState.currentEditElement = null;
|
||||||
|
makeContentEditable(); // Re-attach click handlers
|
||||||
|
}}
|
||||||
|
|
||||||
|
// Global functions for button clicks
|
||||||
|
window.markitectApplyEdit = applyEdit;
|
||||||
|
window.markitectCancelEdit = cancelEdit;
|
||||||
|
|
||||||
|
// Simple HTML to Markdown conversion
|
||||||
|
function htmlToMarkdown(html) {{
|
||||||
|
return html
|
||||||
|
.replace(/<h([1-6])[^>]*>(.*?)<\\/h[1-6]>/gi, (match, level, text) => {{
|
||||||
|
return '#'.repeat(parseInt(level)) + ' ' + text.replace(/<[^>]*>/g, '') + '\\\\n\\\\n';
|
||||||
|
}})
|
||||||
|
.replace(/<p[^>]*>(.*?)<\\/p>/gi, '$1\\\\n\\\\n')
|
||||||
|
.replace(/<strong[^>]*>(.*?)<\\/strong>/gi, '**$1**')
|
||||||
|
.replace(/<em[^>]*>(.*?)<\\/em>/gi, '*$1*')
|
||||||
|
.replace(/<code[^>]*>(.*?)<\\/code>/gi, '`$1`')
|
||||||
|
.replace(/<br[^>]*>/gi, '\\\\n')
|
||||||
|
.replace(/<[^>]*>/g, '')
|
||||||
|
.trim();
|
||||||
|
}}
|
||||||
|
|
||||||
|
// Setup keyboard shortcuts
|
||||||
|
function setupKeyboardShortcuts() {{
|
||||||
|
document.addEventListener('keydown', (e) => {{
|
||||||
|
if ((e.ctrlKey || e.metaKey) && e.key === 's') {{
|
||||||
|
e.preventDefault();
|
||||||
|
saveDocument();
|
||||||
|
}}
|
||||||
|
}});
|
||||||
|
}}
|
||||||
|
|
||||||
|
// Save document functionality
|
||||||
|
function saveDocument() {{
|
||||||
|
const content = extractMarkdownContent();
|
||||||
|
const blob = new Blob([content], {{ type: 'text/markdown' }});
|
||||||
|
const url = URL.createObjectURL(blob);
|
||||||
|
const a = document.createElement('a');
|
||||||
|
a.href = url;
|
||||||
|
a.download = 'edited-document.md';
|
||||||
|
document.body.appendChild(a);
|
||||||
|
a.click();
|
||||||
|
document.body.removeChild(a);
|
||||||
|
URL.revokeObjectURL(url);
|
||||||
|
}}
|
||||||
|
|
||||||
|
// Extract markdown content from current document
|
||||||
|
function extractMarkdownContent() {{
|
||||||
|
const sections = document.querySelectorAll('[data-section-id]');
|
||||||
|
let content = '';
|
||||||
|
|
||||||
|
sections.forEach(section => {{
|
||||||
|
content += htmlToMarkdown(section.innerHTML) + '\\\\n\\\\n';
|
||||||
|
}});
|
||||||
|
|
||||||
|
return content.trim();
|
||||||
|
}}
|
||||||
|
|
||||||
|
// Initialize when DOM is ready
|
||||||
|
if (document.readyState === 'loading') {{
|
||||||
|
document.addEventListener('DOMContentLoaded', initializeEditingEnhancement);
|
||||||
|
}} else {{
|
||||||
|
initializeEditingEnhancement();
|
||||||
|
}}
|
||||||
|
|
||||||
|
}})();
|
||||||
|
</script>
|
||||||
|
"""
|
||||||
|
|
||||||
|
return script_content
|
||||||
Reference in New Issue
Block a user