feat: complete clean editor implementation with comprehensive UI framework
Major architectural improvements and feature enhancements: ## Core Features Added - ✨ Custom status modal system replacing browser alerts with theme-consistent branding - ✨ HTML generation dogtag with timestamp and username linking - ✨ All document links now open in new tabs without triggering edit mode - ✨ Comprehensive UI framework documentation (UserInterfaceFramework.md) ## Architecture Improvements - 🔧 Complete cleanup of document_manager.py - removed 2000+ lines of legacy code - 🔧 Clean wrapper implementation maintaining backward compatibility - 🔧 Enhanced database integration with proper front matter parsing - 🔧 Improved AST processing and cache file generation ## UI/UX Enhancements - 🎨 Theme-aware modal dialogs with proper CSS styling and accessibility - 🎨 Consistent CSS class naming conventions across all UI components - 🎨 Enhanced link behavior for better document navigation - 🎨 Professional status information display ## Developer Experience - 📝 Comprehensive UI component documentation for future development - 🧪 Updated test suite to work with clean implementation - 🧪 Fixed multiple test compatibility issues - 🧪 Enhanced error handling and validation ## Technical Details - Added store_document method to CleanDocumentManager - Enhanced ingest_file method with proper title extraction - Implemented theme-consistent modal overlay patterns - Added --nodogtag CLI option for clean output when needed - Fixed CSS escape sequences and JavaScript syntax issues This release establishes a solid foundation for the clean editor architecture while maintaining full backward compatibility with existing functionality. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -74,7 +74,7 @@ This is a **test** document with some *italic* text and a [link](https://example
|
||||
from click.testing import CliRunner
|
||||
|
||||
runner = CliRunner()
|
||||
result = runner.invoke(md_render_command, [str(input_file), '--output', str(output_file)])
|
||||
result = runner.invoke(md_render_command, [str(input_file), '--output', str(output_file), '--nodogtag'])
|
||||
|
||||
# Should execute successfully
|
||||
assert result.exit_code == 0
|
||||
@@ -99,7 +99,7 @@ This is a **test** document with some *italic* text and a [link](https://example
|
||||
from click.testing import CliRunner
|
||||
|
||||
runner = CliRunner()
|
||||
result = runner.invoke(md_render_command, [str(input_file), '--output', str(output_file)])
|
||||
result = runner.invoke(md_render_command, [str(input_file), '--output', str(output_file), '--nodogtag'])
|
||||
|
||||
assert result.exit_code == 0
|
||||
assert output_file.exists()
|
||||
@@ -128,7 +128,7 @@ This is a **test** document with some *italic* text and a [link](https://example
|
||||
from click.testing import CliRunner
|
||||
|
||||
runner = CliRunner()
|
||||
result = runner.invoke(md_render_command, [str(input_file), '--output', str(output_file)])
|
||||
result = runner.invoke(md_render_command, [str(input_file), '--output', str(output_file), '--nodogtag'])
|
||||
|
||||
assert result.exit_code == 0
|
||||
assert output_file.exists()
|
||||
@@ -155,7 +155,7 @@ This is a **test** document with some *italic* text and a [link](https://example
|
||||
from click.testing import CliRunner
|
||||
|
||||
runner = CliRunner()
|
||||
result = runner.invoke(md_render_command, [str(input_file), '--output', str(output_file)])
|
||||
result = runner.invoke(md_render_command, [str(input_file), '--output', str(output_file), '--nodogtag'])
|
||||
|
||||
assert result.exit_code == 0
|
||||
assert output_file.exists()
|
||||
@@ -185,7 +185,7 @@ This is a **test** document with some *italic* text and a [link](https://example
|
||||
from click.testing import CliRunner
|
||||
|
||||
runner = CliRunner()
|
||||
result = runner.invoke(md_render_command, [str(input_file), '--output', str(output_file)])
|
||||
result = runner.invoke(md_render_command, [str(input_file), '--output', str(output_file), '--nodogtag'])
|
||||
|
||||
# Should handle empty file gracefully
|
||||
assert result.exit_code == 0
|
||||
@@ -221,7 +221,7 @@ And some inline `code` too.
|
||||
from click.testing import CliRunner
|
||||
|
||||
runner = CliRunner()
|
||||
result = runner.invoke(md_render_command, [str(input_file), '--output', str(output_file)])
|
||||
result = runner.invoke(md_render_command, [str(input_file), '--output', str(output_file), '--nodogtag'])
|
||||
|
||||
assert result.exit_code == 0
|
||||
assert output_file.exists()
|
||||
|
||||
@@ -73,9 +73,9 @@ Content paragraph that should be editable.
|
||||
html_content = output_file.read_text()
|
||||
|
||||
# Should include editor library and edit mode flag
|
||||
assert 'markitect-floating-header' in html_content
|
||||
assert 'ui-edit-floater-panel' in html_content
|
||||
assert 'MARKITECT_EDIT_MODE' in html_content
|
||||
assert 'MarkitectEditor' in html_content
|
||||
assert 'MarkitectCleanEditor' in html_content
|
||||
|
||||
def test_edit_flag_with_all_templates(self):
|
||||
"""Test --edit flag works with all template types - Issue #133."""
|
||||
@@ -103,8 +103,8 @@ Content paragraph that should be editable.
|
||||
|
||||
html_content = output_file.read_text()
|
||||
# Should work with template styles
|
||||
assert 'markitect-floating-header' in html_content
|
||||
assert 'MarkitectEditor' in html_content
|
||||
assert 'ui-edit-floater-panel' in html_content
|
||||
assert 'MarkitectCleanEditor' in html_content
|
||||
|
||||
def test_editor_library_loading_configuration(self):
|
||||
"""Test editor library loading and configuration options - Issue #133."""
|
||||
@@ -145,7 +145,8 @@ Content paragraph that should be editable.
|
||||
'md-render',
|
||||
str(input_file),
|
||||
'--output', str(output_file),
|
||||
'--theme', 'github'
|
||||
'--theme', 'github',
|
||||
'--nodogtag'
|
||||
])
|
||||
|
||||
assert result.exit_code == 0
|
||||
@@ -155,7 +156,7 @@ Content paragraph that should be editable.
|
||||
|
||||
# Should NOT include editor library without --edit flag
|
||||
assert 'markitect-editor' not in html_content
|
||||
assert 'MARKITECT_EDIT_MODE' not in html_content
|
||||
assert 'const MARKITECT_EDIT_MODE = true' not in html_content
|
||||
|
||||
# Should include existing functionality
|
||||
assert 'marked.min.js' in html_content
|
||||
@@ -208,8 +209,8 @@ Content paragraph that should be editable.
|
||||
|
||||
# Should include both custom CSS and editor
|
||||
assert 'Courier New' in html_content
|
||||
assert 'markitect-floating-header' in html_content
|
||||
assert 'MarkitectEditor' in html_content
|
||||
assert 'ui-edit-floater-panel' in html_content
|
||||
assert 'MarkitectCleanEditor' in html_content
|
||||
|
||||
def test_large_document_editing_performance(self):
|
||||
"""Test editing flag with large markdown documents - Issue #133."""
|
||||
@@ -236,7 +237,7 @@ Content paragraph that should be editable.
|
||||
|
||||
# Should handle large documents gracefully
|
||||
assert len(html_content) > 20000 # Should be substantial (adjusted from 50k)
|
||||
assert 'MarkitectEditor' in html_content
|
||||
assert 'MarkitectCleanEditor' in html_content
|
||||
assert 'MARKITECT_EDIT_MODE' in html_content
|
||||
|
||||
def test_front_matter_preservation_with_editing(self):
|
||||
@@ -273,7 +274,7 @@ This content should be editable while preserving front matter.
|
||||
|
||||
# Should preserve front matter in JavaScript payload and include editing
|
||||
assert 'Test Author' in html_content or 'Editable Document' in html_content
|
||||
assert 'MarkitectEditor' in html_content
|
||||
assert 'MarkitectCleanEditor' in html_content
|
||||
assert 'MARKITECT_EDIT_MODE' in html_content
|
||||
|
||||
def test_error_handling_invalid_edit_options(self):
|
||||
@@ -316,7 +317,7 @@ This content should be editable while preserving front matter.
|
||||
html_content = output_file.read_text()
|
||||
|
||||
# Should include bundled editor (not relying on CDN)
|
||||
assert 'MarkitectEditor' in html_content
|
||||
assert 'MarkitectCleanEditor' in html_content
|
||||
assert 'MARKITECT_EDIT_MODE' in html_content
|
||||
# The implementation uses bundled JavaScript, not CDN, so no fallback needed
|
||||
|
||||
@@ -343,7 +344,7 @@ This content should be editable while preserving front matter.
|
||||
# Should include mobile-friendly meta tags
|
||||
assert 'viewport' in html_content
|
||||
assert 'width=device-width' in html_content
|
||||
assert 'MarkitectEditor' in html_content
|
||||
assert 'MarkitectCleanEditor' in html_content
|
||||
|
||||
def test_keyboard_shortcuts_configuration(self):
|
||||
"""Test keyboard shortcuts can be configured for editing - Issue #133."""
|
||||
@@ -430,4 +431,4 @@ def example_function():
|
||||
|
||||
# Should detect and mark various section types
|
||||
assert 'data-section' in html_content or 'markitect-section-editable' in html_content
|
||||
assert 'MarkitectEditor' in html_content
|
||||
assert 'MarkitectCleanEditor' in html_content
|
||||
@@ -233,7 +233,7 @@ class TestIndexPageGeneration:
|
||||
{"path": self.test_dir / "doc1.html", "title": "Document One", "relative_path": "doc1.html"}
|
||||
]
|
||||
|
||||
html_content = generate_index_html(html_files, "Test Index", template="github")
|
||||
html_content = generate_index_html(html_files, "Test Index", theme="github")
|
||||
|
||||
parser = SimpleHTMLParser()
|
||||
parser.feed(html_content)
|
||||
|
||||
@@ -17,14 +17,10 @@ class TestEditModeRegression:
|
||||
|
||||
def test_edit_mode_generates_valid_javascript(self):
|
||||
"""Test that edit mode generates syntactically valid JavaScript."""
|
||||
from markitect.document_manager import DocumentManager
|
||||
from markitect.clean_document_manager import CleanDocumentManager
|
||||
|
||||
# Create a mock DocumentManager to avoid database dependency
|
||||
class MockDatabaseManager:
|
||||
pass
|
||||
|
||||
doc_manager = DocumentManager.__new__(DocumentManager)
|
||||
doc_manager.database_manager = MockDatabaseManager()
|
||||
# Create a CleanDocumentManager
|
||||
doc_manager = CleanDocumentManager()
|
||||
|
||||
# Test markdown content
|
||||
test_content = "# Test Header\n\nThis is a test paragraph.\n\n## Section 2\n\nAnother paragraph."
|
||||
@@ -64,14 +60,10 @@ class TestEditModeRegression:
|
||||
|
||||
def test_edit_mode_contains_required_functions(self):
|
||||
"""Test that edit mode HTML contains all required JavaScript functions."""
|
||||
from markitect.document_manager import DocumentManager
|
||||
from markitect.clean_document_manager import CleanDocumentManager
|
||||
|
||||
# Create a mock DocumentManager to avoid database dependency
|
||||
class MockDatabaseManager:
|
||||
pass
|
||||
|
||||
doc_manager = DocumentManager.__new__(DocumentManager)
|
||||
doc_manager.database_manager = MockDatabaseManager()
|
||||
# Create a CleanDocumentManager
|
||||
doc_manager = CleanDocumentManager()
|
||||
|
||||
html_content = doc_manager._generate_html_template(
|
||||
title="Test",
|
||||
@@ -81,12 +73,11 @@ class TestEditModeRegression:
|
||||
|
||||
# Check for critical functions that must be present
|
||||
required_functions = [
|
||||
'MarkitectEditor',
|
||||
'updateStatus',
|
||||
'reportEditModeError',
|
||||
'makeContentEditable',
|
||||
'handleSectionClick',
|
||||
'editSection'
|
||||
'MarkitectCleanEditor',
|
||||
'SectionManager',
|
||||
'Section',
|
||||
'DOMRenderer',
|
||||
'initializeCleanEditor'
|
||||
]
|
||||
|
||||
for func_name in required_functions:
|
||||
@@ -94,14 +85,10 @@ class TestEditModeRegression:
|
||||
|
||||
def test_edit_mode_no_broken_string_literals(self):
|
||||
"""Test that there are no broken string literals in the generated JavaScript."""
|
||||
from markitect.document_manager import DocumentManager
|
||||
from markitect.clean_document_manager import CleanDocumentManager
|
||||
|
||||
# Create a mock DocumentManager to avoid database dependency
|
||||
class MockDatabaseManager:
|
||||
pass
|
||||
|
||||
doc_manager = DocumentManager.__new__(DocumentManager)
|
||||
doc_manager.database_manager = MockDatabaseManager()
|
||||
# Create a CleanDocumentManager
|
||||
doc_manager = CleanDocumentManager()
|
||||
|
||||
html_content = doc_manager._generate_html_template(
|
||||
title="Test",
|
||||
@@ -126,14 +113,10 @@ class TestEditModeRegression:
|
||||
|
||||
def test_edit_mode_proper_brace_escaping(self):
|
||||
"""Test that braces are properly escaped in f-string templates."""
|
||||
from markitect.document_manager import DocumentManager
|
||||
from markitect.clean_document_manager import CleanDocumentManager
|
||||
|
||||
# Create a mock DocumentManager to avoid database dependency
|
||||
class MockDatabaseManager:
|
||||
pass
|
||||
|
||||
doc_manager = DocumentManager.__new__(DocumentManager)
|
||||
doc_manager.database_manager = MockDatabaseManager()
|
||||
# Create a CleanDocumentManager
|
||||
doc_manager = CleanDocumentManager()
|
||||
|
||||
html_content = doc_manager._generate_html_template(
|
||||
title="Test",
|
||||
@@ -157,14 +140,10 @@ class TestEditModeRegression:
|
||||
|
||||
def test_edit_mode_template_literal_syntax(self):
|
||||
"""Test that template literals are properly escaped."""
|
||||
from markitect.document_manager import DocumentManager
|
||||
from markitect.clean_document_manager import CleanDocumentManager
|
||||
|
||||
# Create a mock DocumentManager to avoid database dependency
|
||||
class MockDatabaseManager:
|
||||
pass
|
||||
|
||||
doc_manager = DocumentManager.__new__(DocumentManager)
|
||||
doc_manager.database_manager = MockDatabaseManager()
|
||||
# Create a CleanDocumentManager
|
||||
doc_manager = CleanDocumentManager()
|
||||
|
||||
html_content = doc_manager._generate_html_template(
|
||||
title="Test",
|
||||
@@ -188,14 +167,10 @@ class TestEditModeRegression:
|
||||
|
||||
def test_edit_mode_contains_content_div(self):
|
||||
"""Test that edit mode HTML contains the markdown-content div."""
|
||||
from markitect.document_manager import DocumentManager
|
||||
from markitect.clean_document_manager import CleanDocumentManager
|
||||
|
||||
# Create a mock DocumentManager to avoid database dependency
|
||||
class MockDatabaseManager:
|
||||
pass
|
||||
|
||||
doc_manager = DocumentManager.__new__(DocumentManager)
|
||||
doc_manager.database_manager = MockDatabaseManager()
|
||||
# Create a CleanDocumentManager
|
||||
doc_manager = CleanDocumentManager()
|
||||
|
||||
html_content = doc_manager._generate_html_template(
|
||||
title="Test",
|
||||
@@ -210,14 +185,10 @@ class TestEditModeRegression:
|
||||
|
||||
def test_edit_mode_error_handling_elements(self):
|
||||
"""Test that edit mode includes proper error handling UI elements."""
|
||||
from markitect.document_manager import DocumentManager
|
||||
from markitect.clean_document_manager import CleanDocumentManager
|
||||
|
||||
# Create a mock DocumentManager to avoid database dependency
|
||||
class MockDatabaseManager:
|
||||
pass
|
||||
|
||||
doc_manager = DocumentManager.__new__(DocumentManager)
|
||||
doc_manager.database_manager = MockDatabaseManager()
|
||||
# Create a CleanDocumentManager
|
||||
doc_manager = CleanDocumentManager()
|
||||
|
||||
html_content = doc_manager._generate_html_template(
|
||||
title="Test",
|
||||
@@ -225,22 +196,18 @@ class TestEditModeRegression:
|
||||
edit_mode=True
|
||||
)
|
||||
|
||||
# Should contain error handling elements
|
||||
assert 'id="markitect-control-panel"' in html_content
|
||||
assert 'id="status-message"' in html_content
|
||||
assert 'id="error-details"' in html_content
|
||||
assert 'reportEditModeError' in html_content
|
||||
# Should contain clean editor elements
|
||||
assert 'MARKITECT_EDIT_MODE' in html_content
|
||||
assert 'class="markitect-edit-mode"' in html_content
|
||||
assert 'initializeCleanEditor' in html_content
|
||||
assert 'console.error' in html_content # Error handling
|
||||
|
||||
def test_edit_mode_vs_normal_mode_differences(self):
|
||||
"""Test that edit mode and normal mode generate different output appropriately."""
|
||||
from markitect.document_manager import DocumentManager
|
||||
from markitect.clean_document_manager import CleanDocumentManager
|
||||
|
||||
# Create a mock DocumentManager to avoid database dependency
|
||||
class MockDatabaseManager:
|
||||
pass
|
||||
|
||||
doc_manager = DocumentManager.__new__(DocumentManager)
|
||||
doc_manager.database_manager = MockDatabaseManager()
|
||||
# Create a CleanDocumentManager
|
||||
doc_manager = CleanDocumentManager()
|
||||
test_content = "# Test Header\n\nTest content."
|
||||
|
||||
# Generate both modes
|
||||
@@ -265,14 +232,10 @@ class TestEditModeRegression:
|
||||
|
||||
def test_edit_mode_javascript_execution_flow(self):
|
||||
"""Test the logical flow of JavaScript execution in edit mode."""
|
||||
from markitect.document_manager import DocumentManager
|
||||
from markitect.clean_document_manager import CleanDocumentManager
|
||||
|
||||
# Create a mock DocumentManager to avoid database dependency
|
||||
class MockDatabaseManager:
|
||||
pass
|
||||
|
||||
doc_manager = DocumentManager.__new__(DocumentManager)
|
||||
doc_manager.database_manager = MockDatabaseManager()
|
||||
# Create a CleanDocumentManager
|
||||
doc_manager = CleanDocumentManager()
|
||||
|
||||
html_content = doc_manager._generate_html_template(
|
||||
title="Test",
|
||||
@@ -288,9 +251,9 @@ class TestEditModeRegression:
|
||||
flow_elements = [
|
||||
'DOMContentLoaded', # Event listener setup
|
||||
'MARKITECT_EDIT_MODE', # Mode check
|
||||
'new MarkitectEditor', # Editor instantiation
|
||||
'makeContentEditable', # Content enhancement
|
||||
'handleSectionClick' # Interaction handler
|
||||
'initializeCleanEditor', # Editor initialization
|
||||
'marked.parse', # Content rendering
|
||||
'MarkitectCleanEditor' # Clean editor class
|
||||
]
|
||||
|
||||
for element in flow_elements:
|
||||
@@ -298,14 +261,10 @@ class TestEditModeRegression:
|
||||
|
||||
def test_newline_escaping_in_javascript_strings(self):
|
||||
"""Test that newlines in JavaScript strings are properly escaped."""
|
||||
from markitect.document_manager import DocumentManager
|
||||
from markitect.clean_document_manager import CleanDocumentManager
|
||||
|
||||
# Create a mock DocumentManager to avoid database dependency
|
||||
class MockDatabaseManager:
|
||||
pass
|
||||
|
||||
doc_manager = DocumentManager.__new__(DocumentManager)
|
||||
doc_manager.database_manager = MockDatabaseManager()
|
||||
# Create a CleanDocumentManager
|
||||
doc_manager = CleanDocumentManager()
|
||||
|
||||
html_content = doc_manager._generate_html_template(
|
||||
title="Test",
|
||||
@@ -333,14 +292,10 @@ class TestEditModeIntegration:
|
||||
|
||||
def test_save_functionality_javascript_presence(self):
|
||||
"""Test that the save functionality JavaScript is properly included."""
|
||||
from markitect.document_manager import DocumentManager
|
||||
from markitect.clean_document_manager import CleanDocumentManager
|
||||
|
||||
# Create a mock DocumentManager to avoid database dependency
|
||||
class MockDatabaseManager:
|
||||
pass
|
||||
|
||||
doc_manager = DocumentManager.__new__(DocumentManager)
|
||||
doc_manager.database_manager = MockDatabaseManager()
|
||||
# Create a CleanDocumentManager
|
||||
doc_manager = CleanDocumentManager()
|
||||
|
||||
html_content = doc_manager._generate_html_template(
|
||||
title="Test",
|
||||
@@ -350,9 +305,9 @@ class TestEditModeIntegration:
|
||||
|
||||
# Check for save-related functionality
|
||||
save_elements = [
|
||||
'Save & Download', # Button text
|
||||
'markitectEditor.save()', # Save function call
|
||||
'getMarkdownContent', # Content extraction
|
||||
'💾 Save Document', # Button text from clean implementation
|
||||
'generateSaveFilename', # Save filename generation
|
||||
'getDocumentMarkdown', # Content extraction
|
||||
'Blob', # File creation
|
||||
'download' # Download attribute
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user