2025-10-26 07:51:43 +01:00
"""
Clean Document Manager - Simplified version with only clean editor support
"""
import json
import re
from pathlib import Path
from typing import Dict , Any , Optional
class CleanDocumentManager :
"""
Simplified document manager that only supports the clean editor implementation .
All legacy code has been removed for clarity and maintainability .
"""
def __init__ ( self , db_manager = None ) :
self . db_manager = db_manager
2025-10-28 03:50:21 +01:00
def store_document ( self , file_path : str , content : str , ast : list = None , front_matter : dict = None ) :
""" Store a document in the database. """
if self . db_manager :
from pathlib import Path
filename = Path ( file_path ) . name
return self . db_manager . store_markdown_file ( filename , content )
def get_file ( self , file_path : str ) - > Dict [ str , Any ] :
"""
Retrieve a markdown file from the database .
Args :
file_path : Path to the markdown file to retrieve
Returns :
Dictionary containing file content and metadata
Raises :
FileNotFoundError : If file is not found in database
"""
if not self . db_manager :
raise ValueError ( " Database manager not initialized " )
# Get file from database
file_data = self . db_manager . get_markdown_file ( file_path )
if file_data is None :
raise FileNotFoundError ( f " File ' { file_path } ' not found in database " )
return {
' content ' : file_data . get ( ' content ' , ' ' ) ,
' metadata ' : {
' filename ' : file_data . get ( ' filename ' , file_path ) ,
' front_matter ' : file_data . get ( ' front_matter ' ) ,
' size ' : len ( file_data . get ( ' content ' , ' ' ) ) ,
' modified ' : file_data . get ( ' modified ' )
}
}
2025-10-26 07:51:43 +01:00
def render_file ( self , input_file : str , output_file : str , template : str = None , css : str = None ,
2025-11-04 09:11:50 +01:00
edit_mode : bool = False , insert_mode : bool = False , editor_theme : str = ' github ' , keyboard_shortcuts : bool = True , nodogtag : bool = False ,
image_max_width : str = ' 12cm ' , image_max_height : str = ' 20cm ' ) - > Dict [ str , Any ] :
2025-10-26 07:51:43 +01:00
"""
Render a markdown file to HTML with optional clean editing capabilities .
"""
input_path = Path ( input_file )
output_path = Path ( output_file )
if not input_path . exists ( ) :
raise FileNotFoundError ( f " Input file not found: { input_file } " )
# Read markdown content
2025-11-04 09:11:50 +01:00
raw_markdown_content = input_path . read_text ( encoding = ' utf-8 ' )
# Process base64 images - relocate payloads to document end
markdown_content , base64_references = self . _process_base64_images ( raw_markdown_content )
2025-10-26 07:51:43 +01:00
# Extract title from markdown (first h1 heading)
title = self . _extract_title_from_markdown ( markdown_content )
# Get original filename without extension
original_filename = input_path . stem
# Get version information
version_info = self . _get_version_info ( )
# Generate HTML content
html_content = self . _generate_html_template (
markdown_content = markdown_content ,
title = title ,
css = css ,
template = template ,
edit_mode = edit_mode ,
2025-10-28 23:55:21 +01:00
insert_mode = insert_mode ,
2025-10-26 07:51:43 +01:00
editor_theme = editor_theme ,
keyboard_shortcuts = keyboard_shortcuts ,
original_filename = original_filename ,
2025-10-28 03:50:21 +01:00
version_info = version_info ,
2025-11-04 09:11:50 +01:00
nodogtag = nodogtag ,
image_max_width = image_max_width ,
image_max_height = image_max_height ,
base64_references = base64_references
2025-10-26 07:51:43 +01:00
)
# Write HTML file
output_path . parent . mkdir ( parents = True , exist_ok = True )
output_path . write_text ( html_content , encoding = ' utf-8 ' )
return {
' success ' : True ,
' input_file ' : str ( input_path ) ,
' output_file ' : str ( output_path ) ,
' edit_mode ' : edit_mode ,
' editor_theme ' : editor_theme
}
def _extract_title_from_markdown ( self , markdown_content : str ) - > str :
""" Extract title from first h1 heading in markdown. """
match = re . search ( r ' ^# \ s+(.+) ' , markdown_content , re . MULTILINE )
if match :
return match . group ( 1 ) . strip ( )
return " Markdown Document "
2025-11-04 09:11:50 +01:00
def _process_base64_images ( self , markdown_content : str ) - > tuple :
"""
Process base64 encoded images in markdown content .
- Extracts base64 image data URLs
- Replaces them with reference links
- Returns processed content and reference mapping
Returns :
tuple : ( processed_markdown , base64_references_dict )
"""
import re
import uuid
# Pattern to match base64 image data URLs
base64_pattern = r ' ! \ [([^ \ ]]*) \ ] \ (data:image/([^;]+);base64,([^)]+) \ ) '
base64_references = { }
reference_definitions = [ ]
processed_content = markdown_content
# Find all base64 images
matches = list ( re . finditer ( base64_pattern , markdown_content ) )
for i , match in enumerate ( matches ) :
alt_text = match . group ( 1 )
image_type = match . group ( 2 ) # png, jpeg, svg+xml, etc.
base64_data = match . group ( 3 )
# Generate a unique reference ID
ref_id = f " base64-image- { i + 1 } "
# Store the mapping
base64_references [ ref_id ] = {
' alt ' : alt_text ,
' type ' : image_type ,
' data ' : base64_data ,
' full_data_url ' : f " data:image/ { image_type } ;base64, { base64_data } "
}
# Replace the inline base64 with reference
original_match = match . group ( 0 )
reference_link = f " ![ { alt_text } ][ { ref_id } ] "
processed_content = processed_content . replace ( original_match , reference_link , 1 )
# Create reference definition for the end of document
reference_definitions . append ( f " [ { ref_id } ]: data:image/ { image_type } ;base64, { base64_data } " )
# Add reference definitions to the end of the document if any base64 images were found
if reference_definitions :
# Ensure there's a blank line before the references
if not processed_content . endswith ( ' \n \n ' ) :
if processed_content . endswith ( ' \n ' ) :
processed_content + = ' \n '
else :
processed_content + = ' \n \n '
# Add a comment to indicate the base64 reference section
processed_content + = " <!-- Base64 Image References --> \n "
processed_content + = ' \n ' . join ( reference_definitions ) + ' \n '
return processed_content , base64_references
2025-10-26 07:51:43 +01:00
def _get_version_info ( self ) - > dict :
""" Get repository name and version information. """
2025-10-28 03:50:21 +01:00
from . __version__ import get_version_info
2025-10-26 07:51:43 +01:00
2025-10-28 03:50:21 +01:00
version_info = get_version_info ( )
2025-10-26 07:51:43 +01:00
2025-10-28 03:50:21 +01:00
# Transform to the format expected by the editor
return {
' repo_name ' : ' Markitect ' ,
' version ' : version_info [ ' full_version ' ] ,
' git_info ' : ' ' # Already included in full_version
}
2025-10-26 07:51:43 +01:00
2025-11-04 09:11:50 +01:00
def _get_template_css ( self , template : str = None , image_max_width : str = ' 12cm ' , image_max_height : str = ' 20cm ' ) - > str :
feat: implement sophisticated layered theme system for md-render
MAJOR FEATURES:
- **Layered Theme Architecture**: Combine themes across UI, document, and branding scopes
- **Advanced Theme Combinations**: Support complex themes like "dark,academic" or "light,github,corporate"
- **Legacy Compatibility**: Existing --template usage continues to work seamlessly
- **Enhanced CLI Validation**: Proper theme validation with helpful error messages
TECHNICAL IMPROVEMENTS:
- Replace DocumentManager with CleanDocumentManager throughout codebase
- Add ThemeType custom click parameter with comprehensive validation
- Implement parse_theme_string() and combine_theme_properties() functions
- Add _get_template_css() and _generate_layered_css() methods
- Support for UI themes (light/dark), document themes (basic/github/academic), and branding themes (corporate/startup)
THEME CAPABILITIES:
- **Single themes**: basic, github, dark, academic, light, corporate, startup
- **Layered themes**: dark,academic combines dark UI with academic typography
- **Complex combinations**: light,github,corporate for branded GitHub-style documents
- **Intelligent property merging**: Later themes override earlier theme properties
QUALITY ASSURANCE:
- All template system tests passing (12/12)
- Fixed import errors and missing dependencies
- Updated test expectations for new validation messages
- Comprehensive validation prevents unknown theme usage
Breaking Change: --template parameter renamed to --theme with enhanced functionality
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-27 21:31:08 +01:00
""" Generate layered theme CSS styles. """
# Import layered theme functions
from markitect . plugins . builtin . markdown_commands import (
parse_theme_string , combine_theme_properties , TEMPLATE_STYLES
)
2025-10-26 07:51:43 +01:00
feat: implement sophisticated layered theme system for md-render
MAJOR FEATURES:
- **Layered Theme Architecture**: Combine themes across UI, document, and branding scopes
- **Advanced Theme Combinations**: Support complex themes like "dark,academic" or "light,github,corporate"
- **Legacy Compatibility**: Existing --template usage continues to work seamlessly
- **Enhanced CLI Validation**: Proper theme validation with helpful error messages
TECHNICAL IMPROVEMENTS:
- Replace DocumentManager with CleanDocumentManager throughout codebase
- Add ThemeType custom click parameter with comprehensive validation
- Implement parse_theme_string() and combine_theme_properties() functions
- Add _get_template_css() and _generate_layered_css() methods
- Support for UI themes (light/dark), document themes (basic/github/academic), and branding themes (corporate/startup)
THEME CAPABILITIES:
- **Single themes**: basic, github, dark, academic, light, corporate, startup
- **Layered themes**: dark,academic combines dark UI with academic typography
- **Complex combinations**: light,github,corporate for branded GitHub-style documents
- **Intelligent property merging**: Later themes override earlier theme properties
QUALITY ASSURANCE:
- All template system tests passing (12/12)
- Fixed import errors and missing dependencies
- Updated test expectations for new validation messages
- Comprehensive validation prevents unknown theme usage
Breaking Change: --template parameter renamed to --theme with enhanced functionality
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-27 21:31:08 +01:00
# Handle layered themes or fall back to legacy
if template and ' , ' in template :
# New layered theme system
theme_list = parse_theme_string ( template )
combined_props = combine_theme_properties ( theme_list )
2025-11-04 09:11:50 +01:00
return self . _generate_layered_css ( combined_props , image_max_width , image_max_height )
feat: implement sophisticated layered theme system for md-render
MAJOR FEATURES:
- **Layered Theme Architecture**: Combine themes across UI, document, and branding scopes
- **Advanced Theme Combinations**: Support complex themes like "dark,academic" or "light,github,corporate"
- **Legacy Compatibility**: Existing --template usage continues to work seamlessly
- **Enhanced CLI Validation**: Proper theme validation with helpful error messages
TECHNICAL IMPROVEMENTS:
- Replace DocumentManager with CleanDocumentManager throughout codebase
- Add ThemeType custom click parameter with comprehensive validation
- Implement parse_theme_string() and combine_theme_properties() functions
- Add _get_template_css() and _generate_layered_css() methods
- Support for UI themes (light/dark), document themes (basic/github/academic), and branding themes (corporate/startup)
THEME CAPABILITIES:
- **Single themes**: basic, github, dark, academic, light, corporate, startup
- **Layered themes**: dark,academic combines dark UI with academic typography
- **Complex combinations**: light,github,corporate for branded GitHub-style documents
- **Intelligent property merging**: Later themes override earlier theme properties
QUALITY ASSURANCE:
- All template system tests passing (12/12)
- Fixed import errors and missing dependencies
- Updated test expectations for new validation messages
- Comprehensive validation prevents unknown theme usage
Breaking Change: --template parameter renamed to --theme with enhanced functionality
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-27 21:31:08 +01:00
else :
# Legacy single theme or fallback
if not template or template not in TEMPLATE_STYLES :
2025-10-27 21:56:38 +01:00
# Use default layered themes or the specified theme
theme_list = parse_theme_string ( template or ' basic ' )
feat: implement sophisticated layered theme system for md-render
MAJOR FEATURES:
- **Layered Theme Architecture**: Combine themes across UI, document, and branding scopes
- **Advanced Theme Combinations**: Support complex themes like "dark,academic" or "light,github,corporate"
- **Legacy Compatibility**: Existing --template usage continues to work seamlessly
- **Enhanced CLI Validation**: Proper theme validation with helpful error messages
TECHNICAL IMPROVEMENTS:
- Replace DocumentManager with CleanDocumentManager throughout codebase
- Add ThemeType custom click parameter with comprehensive validation
- Implement parse_theme_string() and combine_theme_properties() functions
- Add _get_template_css() and _generate_layered_css() methods
- Support for UI themes (light/dark), document themes (basic/github/academic), and branding themes (corporate/startup)
THEME CAPABILITIES:
- **Single themes**: basic, github, dark, academic, light, corporate, startup
- **Layered themes**: dark,academic combines dark UI with academic typography
- **Complex combinations**: light,github,corporate for branded GitHub-style documents
- **Intelligent property merging**: Later themes override earlier theme properties
QUALITY ASSURANCE:
- All template system tests passing (12/12)
- Fixed import errors and missing dependencies
- Updated test expectations for new validation messages
- Comprehensive validation prevents unknown theme usage
Breaking Change: --template parameter renamed to --theme with enhanced functionality
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-27 21:31:08 +01:00
combined_props = combine_theme_properties ( theme_list )
2025-11-04 09:11:50 +01:00
return self . _generate_layered_css ( combined_props , image_max_width , image_max_height )
feat: implement sophisticated layered theme system for md-render
MAJOR FEATURES:
- **Layered Theme Architecture**: Combine themes across UI, document, and branding scopes
- **Advanced Theme Combinations**: Support complex themes like "dark,academic" or "light,github,corporate"
- **Legacy Compatibility**: Existing --template usage continues to work seamlessly
- **Enhanced CLI Validation**: Proper theme validation with helpful error messages
TECHNICAL IMPROVEMENTS:
- Replace DocumentManager with CleanDocumentManager throughout codebase
- Add ThemeType custom click parameter with comprehensive validation
- Implement parse_theme_string() and combine_theme_properties() functions
- Add _get_template_css() and _generate_layered_css() methods
- Support for UI themes (light/dark), document themes (basic/github/academic), and branding themes (corporate/startup)
THEME CAPABILITIES:
- **Single themes**: basic, github, dark, academic, light, corporate, startup
- **Layered themes**: dark,academic combines dark UI with academic typography
- **Complex combinations**: light,github,corporate for branded GitHub-style documents
- **Intelligent property merging**: Later themes override earlier theme properties
QUALITY ASSURANCE:
- All template system tests passing (12/12)
- Fixed import errors and missing dependencies
- Updated test expectations for new validation messages
- Comprehensive validation prevents unknown theme usage
Breaking Change: --template parameter renamed to --theme with enhanced functionality
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-27 21:31:08 +01:00
else :
# Legacy theme - convert to layered
theme_list = parse_theme_string ( template )
combined_props = combine_theme_properties ( theme_list )
2025-11-04 09:11:50 +01:00
return self . _generate_layered_css ( combined_props , image_max_width , image_max_height )
2025-10-26 07:51:43 +01:00
2025-11-04 09:11:50 +01:00
def _generate_layered_css ( self , properties : dict , image_max_width : str = ' 12cm ' , image_max_height : str = ' 20cm ' ) - > str :
feat: implement sophisticated layered theme system for md-render
MAJOR FEATURES:
- **Layered Theme Architecture**: Combine themes across UI, document, and branding scopes
- **Advanced Theme Combinations**: Support complex themes like "dark,academic" or "light,github,corporate"
- **Legacy Compatibility**: Existing --template usage continues to work seamlessly
- **Enhanced CLI Validation**: Proper theme validation with helpful error messages
TECHNICAL IMPROVEMENTS:
- Replace DocumentManager with CleanDocumentManager throughout codebase
- Add ThemeType custom click parameter with comprehensive validation
- Implement parse_theme_string() and combine_theme_properties() functions
- Add _get_template_css() and _generate_layered_css() methods
- Support for UI themes (light/dark), document themes (basic/github/academic), and branding themes (corporate/startup)
THEME CAPABILITIES:
- **Single themes**: basic, github, dark, academic, light, corporate, startup
- **Layered themes**: dark,academic combines dark UI with academic typography
- **Complex combinations**: light,github,corporate for branded GitHub-style documents
- **Intelligent property merging**: Later themes override earlier theme properties
QUALITY ASSURANCE:
- All template system tests passing (12/12)
- Fixed import errors and missing dependencies
- Updated test expectations for new validation messages
- Comprehensive validation prevents unknown theme usage
Breaking Change: --template parameter renamed to --theme with enhanced functionality
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-27 21:31:08 +01:00
""" Generate CSS from combined theme properties. """
2025-10-26 07:51:43 +01:00
feat: implement sophisticated layered theme system for md-render
MAJOR FEATURES:
- **Layered Theme Architecture**: Combine themes across UI, document, and branding scopes
- **Advanced Theme Combinations**: Support complex themes like "dark,academic" or "light,github,corporate"
- **Legacy Compatibility**: Existing --template usage continues to work seamlessly
- **Enhanced CLI Validation**: Proper theme validation with helpful error messages
TECHNICAL IMPROVEMENTS:
- Replace DocumentManager with CleanDocumentManager throughout codebase
- Add ThemeType custom click parameter with comprehensive validation
- Implement parse_theme_string() and combine_theme_properties() functions
- Add _get_template_css() and _generate_layered_css() methods
- Support for UI themes (light/dark), document themes (basic/github/academic), and branding themes (corporate/startup)
THEME CAPABILITIES:
- **Single themes**: basic, github, dark, academic, light, corporate, startup
- **Layered themes**: dark,academic combines dark UI with academic typography
- **Complex combinations**: light,github,corporate for branded GitHub-style documents
- **Intelligent property merging**: Later themes override earlier theme properties
QUALITY ASSURANCE:
- All template system tests passing (12/12)
- Fixed import errors and missing dependencies
- Updated test expectations for new validation messages
- Comprehensive validation prevents unknown theme usage
Breaking Change: --template parameter renamed to --theme with enhanced functionality
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-27 21:31:08 +01:00
# Set defaults for missing properties (properties override defaults)
defaults = {
' body_background ' : ' #ffffff ' ,
' body_color ' : ' #333333 ' ,
' font_family ' : ' -apple-system, BlinkMacSystemFont, Segoe UI, Helvetica, Arial, sans-serif ' ,
' max_width ' : ' 800px ' ,
' heading_color ' : ' #333333 ' , # Use same as body color by default
' heading_style ' : ' simple ' ,
' text_align ' : ' left ' ,
' code_background ' : ' #f6f8fa ' ,
' code_color ' : ' #333333 ' ,
' border_color ' : ' #d0d7de ' ,
' blockquote_border ' : ' #dfe2e5 ' ,
' blockquote_color ' : ' #6a737d ' ,
' table_border ' : ' #d0d7de ' ,
' table_header_bg ' : ' #f6f8fa ' ,
' accent_color ' : None ,
' secondary_color ' : None
}
# Merge defaults first, then override with theme properties
props = { * * defaults , * * properties }
# Base CSS
base_css = f """
body { {
font - family : { props [ ' font_family ' ] } ;
max - width : { props [ ' max_width ' ] } ;
2025-10-26 07:51:43 +01:00
margin : 0 auto ;
padding : 2 rem ;
line - height : 1.6 ;
feat: implement sophisticated layered theme system for md-render
MAJOR FEATURES:
- **Layered Theme Architecture**: Combine themes across UI, document, and branding scopes
- **Advanced Theme Combinations**: Support complex themes like "dark,academic" or "light,github,corporate"
- **Legacy Compatibility**: Existing --template usage continues to work seamlessly
- **Enhanced CLI Validation**: Proper theme validation with helpful error messages
TECHNICAL IMPROVEMENTS:
- Replace DocumentManager with CleanDocumentManager throughout codebase
- Add ThemeType custom click parameter with comprehensive validation
- Implement parse_theme_string() and combine_theme_properties() functions
- Add _get_template_css() and _generate_layered_css() methods
- Support for UI themes (light/dark), document themes (basic/github/academic), and branding themes (corporate/startup)
THEME CAPABILITIES:
- **Single themes**: basic, github, dark, academic, light, corporate, startup
- **Layered themes**: dark,academic combines dark UI with academic typography
- **Complex combinations**: light,github,corporate for branded GitHub-style documents
- **Intelligent property merging**: Later themes override earlier theme properties
QUALITY ASSURANCE:
- All template system tests passing (12/12)
- Fixed import errors and missing dependencies
- Updated test expectations for new validation messages
- Comprehensive validation prevents unknown theme usage
Breaking Change: --template parameter renamed to --theme with enhanced functionality
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-27 21:31:08 +01:00
color : { props [ ' body_color ' ] } ;
background - color : { props [ ' body_background ' ] } ;
} }
#markdown-content {{
2025-10-26 07:51:43 +01:00
min - height : 200 px ;
feat: implement sophisticated layered theme system for md-render
MAJOR FEATURES:
- **Layered Theme Architecture**: Combine themes across UI, document, and branding scopes
- **Advanced Theme Combinations**: Support complex themes like "dark,academic" or "light,github,corporate"
- **Legacy Compatibility**: Existing --template usage continues to work seamlessly
- **Enhanced CLI Validation**: Proper theme validation with helpful error messages
TECHNICAL IMPROVEMENTS:
- Replace DocumentManager with CleanDocumentManager throughout codebase
- Add ThemeType custom click parameter with comprehensive validation
- Implement parse_theme_string() and combine_theme_properties() functions
- Add _get_template_css() and _generate_layered_css() methods
- Support for UI themes (light/dark), document themes (basic/github/academic), and branding themes (corporate/startup)
THEME CAPABILITIES:
- **Single themes**: basic, github, dark, academic, light, corporate, startup
- **Layered themes**: dark,academic combines dark UI with academic typography
- **Complex combinations**: light,github,corporate for branded GitHub-style documents
- **Intelligent property merging**: Later themes override earlier theme properties
QUALITY ASSURANCE:
- All template system tests passing (12/12)
- Fixed import errors and missing dependencies
- Updated test expectations for new validation messages
- Comprehensive validation prevents unknown theme usage
Breaking Change: --template parameter renamed to --theme with enhanced functionality
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-27 21:31:08 +01:00
} } """
# Heading styles
2025-11-10 10:53:37 +01:00
heading_font_style = " "
if ' heading_font_family ' in props and props [ ' heading_font_family ' ] :
heading_font_style = f " font-family: { props [ ' heading_font_family ' ] } ; "
feat: implement sophisticated layered theme system for md-render
MAJOR FEATURES:
- **Layered Theme Architecture**: Combine themes across UI, document, and branding scopes
- **Advanced Theme Combinations**: Support complex themes like "dark,academic" or "light,github,corporate"
- **Legacy Compatibility**: Existing --template usage continues to work seamlessly
- **Enhanced CLI Validation**: Proper theme validation with helpful error messages
TECHNICAL IMPROVEMENTS:
- Replace DocumentManager with CleanDocumentManager throughout codebase
- Add ThemeType custom click parameter with comprehensive validation
- Implement parse_theme_string() and combine_theme_properties() functions
- Add _get_template_css() and _generate_layered_css() methods
- Support for UI themes (light/dark), document themes (basic/github/academic), and branding themes (corporate/startup)
THEME CAPABILITIES:
- **Single themes**: basic, github, dark, academic, light, corporate, startup
- **Layered themes**: dark,academic combines dark UI with academic typography
- **Complex combinations**: light,github,corporate for branded GitHub-style documents
- **Intelligent property merging**: Later themes override earlier theme properties
QUALITY ASSURANCE:
- All template system tests passing (12/12)
- Fixed import errors and missing dependencies
- Updated test expectations for new validation messages
- Comprehensive validation prevents unknown theme usage
Breaking Change: --template parameter renamed to --theme with enhanced functionality
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-27 21:31:08 +01:00
heading_css = " "
if props [ ' heading_style ' ] == ' underlined ' :
heading_css = f """
h1 , h2 , h3 , h4 , h5 , h6 { {
color : { props [ ' heading_color ' ] } ;
2025-11-10 10:53:37 +01:00
{ heading_font_style }
feat: implement sophisticated layered theme system for md-render
MAJOR FEATURES:
- **Layered Theme Architecture**: Combine themes across UI, document, and branding scopes
- **Advanced Theme Combinations**: Support complex themes like "dark,academic" or "light,github,corporate"
- **Legacy Compatibility**: Existing --template usage continues to work seamlessly
- **Enhanced CLI Validation**: Proper theme validation with helpful error messages
TECHNICAL IMPROVEMENTS:
- Replace DocumentManager with CleanDocumentManager throughout codebase
- Add ThemeType custom click parameter with comprehensive validation
- Implement parse_theme_string() and combine_theme_properties() functions
- Add _get_template_css() and _generate_layered_css() methods
- Support for UI themes (light/dark), document themes (basic/github/academic), and branding themes (corporate/startup)
THEME CAPABILITIES:
- **Single themes**: basic, github, dark, academic, light, corporate, startup
- **Layered themes**: dark,academic combines dark UI with academic typography
- **Complex combinations**: light,github,corporate for branded GitHub-style documents
- **Intelligent property merging**: Later themes override earlier theme properties
QUALITY ASSURANCE:
- All template system tests passing (12/12)
- Fixed import errors and missing dependencies
- Updated test expectations for new validation messages
- Comprehensive validation prevents unknown theme usage
Breaking Change: --template parameter renamed to --theme with enhanced functionality
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-27 21:31:08 +01:00
border - bottom : 1 px solid { props [ ' border_color ' ] } ;
padding - bottom : 0.3 em ;
} } """
elif props [ ' heading_style ' ] == ' centered ' :
heading_css = f """
h1 , h2 , h3 , h4 , h5 , h6 { {
color : { props [ ' heading_color ' ] } ;
2025-11-10 10:53:37 +01:00
{ heading_font_style }
feat: implement sophisticated layered theme system for md-render
MAJOR FEATURES:
- **Layered Theme Architecture**: Combine themes across UI, document, and branding scopes
- **Advanced Theme Combinations**: Support complex themes like "dark,academic" or "light,github,corporate"
- **Legacy Compatibility**: Existing --template usage continues to work seamlessly
- **Enhanced CLI Validation**: Proper theme validation with helpful error messages
TECHNICAL IMPROVEMENTS:
- Replace DocumentManager with CleanDocumentManager throughout codebase
- Add ThemeType custom click parameter with comprehensive validation
- Implement parse_theme_string() and combine_theme_properties() functions
- Add _get_template_css() and _generate_layered_css() methods
- Support for UI themes (light/dark), document themes (basic/github/academic), and branding themes (corporate/startup)
THEME CAPABILITIES:
- **Single themes**: basic, github, dark, academic, light, corporate, startup
- **Layered themes**: dark,academic combines dark UI with academic typography
- **Complex combinations**: light,github,corporate for branded GitHub-style documents
- **Intelligent property merging**: Later themes override earlier theme properties
QUALITY ASSURANCE:
- All template system tests passing (12/12)
- Fixed import errors and missing dependencies
- Updated test expectations for new validation messages
- Comprehensive validation prevents unknown theme usage
Breaking Change: --template parameter renamed to --theme with enhanced functionality
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-27 21:31:08 +01:00
margin - top : 2 rem ;
margin - bottom : 1 rem ;
} }
h1 { {
text - align : center ;
font - size : 2.2 em ;
border - bottom : 2 px solid { props [ ' heading_color ' ] } ;
padding - bottom : 0.5 rem ;
} } """
else : # simple
heading_css = f """
h1 , h2 , h3 , h4 , h5 , h6 { {
color : { props [ ' heading_color ' ] } ;
2025-11-10 10:53:37 +01:00
{ heading_font_style }
feat: implement sophisticated layered theme system for md-render
MAJOR FEATURES:
- **Layered Theme Architecture**: Combine themes across UI, document, and branding scopes
- **Advanced Theme Combinations**: Support complex themes like "dark,academic" or "light,github,corporate"
- **Legacy Compatibility**: Existing --template usage continues to work seamlessly
- **Enhanced CLI Validation**: Proper theme validation with helpful error messages
TECHNICAL IMPROVEMENTS:
- Replace DocumentManager with CleanDocumentManager throughout codebase
- Add ThemeType custom click parameter with comprehensive validation
- Implement parse_theme_string() and combine_theme_properties() functions
- Add _get_template_css() and _generate_layered_css() methods
- Support for UI themes (light/dark), document themes (basic/github/academic), and branding themes (corporate/startup)
THEME CAPABILITIES:
- **Single themes**: basic, github, dark, academic, light, corporate, startup
- **Layered themes**: dark,academic combines dark UI with academic typography
- **Complex combinations**: light,github,corporate for branded GitHub-style documents
- **Intelligent property merging**: Later themes override earlier theme properties
QUALITY ASSURANCE:
- All template system tests passing (12/12)
- Fixed import errors and missing dependencies
- Updated test expectations for new validation messages
- Comprehensive validation prevents unknown theme usage
Breaking Change: --template parameter renamed to --theme with enhanced functionality
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-27 21:31:08 +01:00
} } """
# Text alignment
text_css = " "
if props [ ' text_align ' ] == ' justify ' :
text_css = """
p {
text - align : justify ;
margin - bottom : 1.2 rem ;
} """
# Element styling
element_css = f """
pre { {
background - color : { props [ ' code_background ' ] } ;
color : { props [ ' code_color ' ] } ;
2025-10-26 07:51:43 +01:00
padding : 1 rem ;
border - radius : 6 px ;
overflow - x : auto ;
feat: implement sophisticated layered theme system for md-render
MAJOR FEATURES:
- **Layered Theme Architecture**: Combine themes across UI, document, and branding scopes
- **Advanced Theme Combinations**: Support complex themes like "dark,academic" or "light,github,corporate"
- **Legacy Compatibility**: Existing --template usage continues to work seamlessly
- **Enhanced CLI Validation**: Proper theme validation with helpful error messages
TECHNICAL IMPROVEMENTS:
- Replace DocumentManager with CleanDocumentManager throughout codebase
- Add ThemeType custom click parameter with comprehensive validation
- Implement parse_theme_string() and combine_theme_properties() functions
- Add _get_template_css() and _generate_layered_css() methods
- Support for UI themes (light/dark), document themes (basic/github/academic), and branding themes (corporate/startup)
THEME CAPABILITIES:
- **Single themes**: basic, github, dark, academic, light, corporate, startup
- **Layered themes**: dark,academic combines dark UI with academic typography
- **Complex combinations**: light,github,corporate for branded GitHub-style documents
- **Intelligent property merging**: Later themes override earlier theme properties
QUALITY ASSURANCE:
- All template system tests passing (12/12)
- Fixed import errors and missing dependencies
- Updated test expectations for new validation messages
- Comprehensive validation prevents unknown theme usage
Breaking Change: --template parameter renamed to --theme with enhanced functionality
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-27 21:31:08 +01:00
border : 1 px solid { props [ ' border_color ' ] } ;
} }
code { {
background - color : { props [ ' code_background ' ] } ;
color : { props [ ' code_color ' ] } ;
2025-10-26 07:51:43 +01:00
padding : 0.2 em 0.4 em ;
border - radius : 3 px ;
font - size : 0.9 em ;
feat: implement sophisticated layered theme system for md-render
MAJOR FEATURES:
- **Layered Theme Architecture**: Combine themes across UI, document, and branding scopes
- **Advanced Theme Combinations**: Support complex themes like "dark,academic" or "light,github,corporate"
- **Legacy Compatibility**: Existing --template usage continues to work seamlessly
- **Enhanced CLI Validation**: Proper theme validation with helpful error messages
TECHNICAL IMPROVEMENTS:
- Replace DocumentManager with CleanDocumentManager throughout codebase
- Add ThemeType custom click parameter with comprehensive validation
- Implement parse_theme_string() and combine_theme_properties() functions
- Add _get_template_css() and _generate_layered_css() methods
- Support for UI themes (light/dark), document themes (basic/github/academic), and branding themes (corporate/startup)
THEME CAPABILITIES:
- **Single themes**: basic, github, dark, academic, light, corporate, startup
- **Layered themes**: dark,academic combines dark UI with academic typography
- **Complex combinations**: light,github,corporate for branded GitHub-style documents
- **Intelligent property merging**: Later themes override earlier theme properties
QUALITY ASSURANCE:
- All template system tests passing (12/12)
- Fixed import errors and missing dependencies
- Updated test expectations for new validation messages
- Comprehensive validation prevents unknown theme usage
Breaking Change: --template parameter renamed to --theme with enhanced functionality
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-27 21:31:08 +01:00
} }
pre code { {
2025-10-26 07:51:43 +01:00
background : none ;
padding : 0 ;
feat: implement sophisticated layered theme system for md-render
MAJOR FEATURES:
- **Layered Theme Architecture**: Combine themes across UI, document, and branding scopes
- **Advanced Theme Combinations**: Support complex themes like "dark,academic" or "light,github,corporate"
- **Legacy Compatibility**: Existing --template usage continues to work seamlessly
- **Enhanced CLI Validation**: Proper theme validation with helpful error messages
TECHNICAL IMPROVEMENTS:
- Replace DocumentManager with CleanDocumentManager throughout codebase
- Add ThemeType custom click parameter with comprehensive validation
- Implement parse_theme_string() and combine_theme_properties() functions
- Add _get_template_css() and _generate_layered_css() methods
- Support for UI themes (light/dark), document themes (basic/github/academic), and branding themes (corporate/startup)
THEME CAPABILITIES:
- **Single themes**: basic, github, dark, academic, light, corporate, startup
- **Layered themes**: dark,academic combines dark UI with academic typography
- **Complex combinations**: light,github,corporate for branded GitHub-style documents
- **Intelligent property merging**: Later themes override earlier theme properties
QUALITY ASSURANCE:
- All template system tests passing (12/12)
- Fixed import errors and missing dependencies
- Updated test expectations for new validation messages
- Comprehensive validation prevents unknown theme usage
Breaking Change: --template parameter renamed to --theme with enhanced functionality
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-27 21:31:08 +01:00
} }
blockquote { {
border - left : 4 px solid { props [ ' blockquote_border ' ] } ;
2025-10-26 07:51:43 +01:00
margin : 0 ;
padding - left : 1 rem ;
feat: implement sophisticated layered theme system for md-render
MAJOR FEATURES:
- **Layered Theme Architecture**: Combine themes across UI, document, and branding scopes
- **Advanced Theme Combinations**: Support complex themes like "dark,academic" or "light,github,corporate"
- **Legacy Compatibility**: Existing --template usage continues to work seamlessly
- **Enhanced CLI Validation**: Proper theme validation with helpful error messages
TECHNICAL IMPROVEMENTS:
- Replace DocumentManager with CleanDocumentManager throughout codebase
- Add ThemeType custom click parameter with comprehensive validation
- Implement parse_theme_string() and combine_theme_properties() functions
- Add _get_template_css() and _generate_layered_css() methods
- Support for UI themes (light/dark), document themes (basic/github/academic), and branding themes (corporate/startup)
THEME CAPABILITIES:
- **Single themes**: basic, github, dark, academic, light, corporate, startup
- **Layered themes**: dark,academic combines dark UI with academic typography
- **Complex combinations**: light,github,corporate for branded GitHub-style documents
- **Intelligent property merging**: Later themes override earlier theme properties
QUALITY ASSURANCE:
- All template system tests passing (12/12)
- Fixed import errors and missing dependencies
- Updated test expectations for new validation messages
- Comprehensive validation prevents unknown theme usage
Breaking Change: --template parameter renamed to --theme with enhanced functionality
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-27 21:31:08 +01:00
color : { props [ ' blockquote_color ' ] } ;
} }
table { {
2025-10-27 11:59:16 +01:00
font - size : 0.85 em ;
border - collapse : collapse ;
margin : 1 rem 0 ;
width : 100 % ;
feat: implement sophisticated layered theme system for md-render
MAJOR FEATURES:
- **Layered Theme Architecture**: Combine themes across UI, document, and branding scopes
- **Advanced Theme Combinations**: Support complex themes like "dark,academic" or "light,github,corporate"
- **Legacy Compatibility**: Existing --template usage continues to work seamlessly
- **Enhanced CLI Validation**: Proper theme validation with helpful error messages
TECHNICAL IMPROVEMENTS:
- Replace DocumentManager with CleanDocumentManager throughout codebase
- Add ThemeType custom click parameter with comprehensive validation
- Implement parse_theme_string() and combine_theme_properties() functions
- Add _get_template_css() and _generate_layered_css() methods
- Support for UI themes (light/dark), document themes (basic/github/academic), and branding themes (corporate/startup)
THEME CAPABILITIES:
- **Single themes**: basic, github, dark, academic, light, corporate, startup
- **Layered themes**: dark,academic combines dark UI with academic typography
- **Complex combinations**: light,github,corporate for branded GitHub-style documents
- **Intelligent property merging**: Later themes override earlier theme properties
QUALITY ASSURANCE:
- All template system tests passing (12/12)
- Fixed import errors and missing dependencies
- Updated test expectations for new validation messages
- Comprehensive validation prevents unknown theme usage
Breaking Change: --template parameter renamed to --theme with enhanced functionality
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-27 21:31:08 +01:00
border : 1 px solid { props [ ' table_border ' ] } ;
} }
th , td { {
2025-10-27 11:59:16 +01:00
font - size : inherit ;
feat: implement sophisticated layered theme system for md-render
MAJOR FEATURES:
- **Layered Theme Architecture**: Combine themes across UI, document, and branding scopes
- **Advanced Theme Combinations**: Support complex themes like "dark,academic" or "light,github,corporate"
- **Legacy Compatibility**: Existing --template usage continues to work seamlessly
- **Enhanced CLI Validation**: Proper theme validation with helpful error messages
TECHNICAL IMPROVEMENTS:
- Replace DocumentManager with CleanDocumentManager throughout codebase
- Add ThemeType custom click parameter with comprehensive validation
- Implement parse_theme_string() and combine_theme_properties() functions
- Add _get_template_css() and _generate_layered_css() methods
- Support for UI themes (light/dark), document themes (basic/github/academic), and branding themes (corporate/startup)
THEME CAPABILITIES:
- **Single themes**: basic, github, dark, academic, light, corporate, startup
- **Layered themes**: dark,academic combines dark UI with academic typography
- **Complex combinations**: light,github,corporate for branded GitHub-style documents
- **Intelligent property merging**: Later themes override earlier theme properties
QUALITY ASSURANCE:
- All template system tests passing (12/12)
- Fixed import errors and missing dependencies
- Updated test expectations for new validation messages
- Comprehensive validation prevents unknown theme usage
Breaking Change: --template parameter renamed to --theme with enhanced functionality
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-27 21:31:08 +01:00
border : 1 px solid { props [ ' table_border ' ] } ;
2025-10-27 11:59:16 +01:00
padding : 0.5 rem ;
text - align : left ;
feat: implement sophisticated layered theme system for md-render
MAJOR FEATURES:
- **Layered Theme Architecture**: Combine themes across UI, document, and branding scopes
- **Advanced Theme Combinations**: Support complex themes like "dark,academic" or "light,github,corporate"
- **Legacy Compatibility**: Existing --template usage continues to work seamlessly
- **Enhanced CLI Validation**: Proper theme validation with helpful error messages
TECHNICAL IMPROVEMENTS:
- Replace DocumentManager with CleanDocumentManager throughout codebase
- Add ThemeType custom click parameter with comprehensive validation
- Implement parse_theme_string() and combine_theme_properties() functions
- Add _get_template_css() and _generate_layered_css() methods
- Support for UI themes (light/dark), document themes (basic/github/academic), and branding themes (corporate/startup)
THEME CAPABILITIES:
- **Single themes**: basic, github, dark, academic, light, corporate, startup
- **Layered themes**: dark,academic combines dark UI with academic typography
- **Complex combinations**: light,github,corporate for branded GitHub-style documents
- **Intelligent property merging**: Later themes override earlier theme properties
QUALITY ASSURANCE:
- All template system tests passing (12/12)
- Fixed import errors and missing dependencies
- Updated test expectations for new validation messages
- Comprehensive validation prevents unknown theme usage
Breaking Change: --template parameter renamed to --theme with enhanced functionality
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-27 21:31:08 +01:00
} }
th { {
background - color : { props [ ' table_header_bg ' ] } ;
2025-10-27 11:59:16 +01:00
font - weight : 600 ;
feat: implement sophisticated layered theme system for md-render
MAJOR FEATURES:
- **Layered Theme Architecture**: Combine themes across UI, document, and branding scopes
- **Advanced Theme Combinations**: Support complex themes like "dark,academic" or "light,github,corporate"
- **Legacy Compatibility**: Existing --template usage continues to work seamlessly
- **Enhanced CLI Validation**: Proper theme validation with helpful error messages
TECHNICAL IMPROVEMENTS:
- Replace DocumentManager with CleanDocumentManager throughout codebase
- Add ThemeType custom click parameter with comprehensive validation
- Implement parse_theme_string() and combine_theme_properties() functions
- Add _get_template_css() and _generate_layered_css() methods
- Support for UI themes (light/dark), document themes (basic/github/academic), and branding themes (corporate/startup)
THEME CAPABILITIES:
- **Single themes**: basic, github, dark, academic, light, corporate, startup
- **Layered themes**: dark,academic combines dark UI with academic typography
- **Complex combinations**: light,github,corporate for branded GitHub-style documents
- **Intelligent property merging**: Later themes override earlier theme properties
QUALITY ASSURANCE:
- All template system tests passing (12/12)
- Fixed import errors and missing dependencies
- Updated test expectations for new validation messages
- Comprehensive validation prevents unknown theme usage
Breaking Change: --template parameter renamed to --theme with enhanced functionality
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-27 21:31:08 +01:00
} } """
2025-10-27 21:56:38 +01:00
# Link styling
link_css = " "
if props . get ( ' link_color ' ) :
link_css = f """
a { {
color : { props [ ' link_color ' ] } ;
text - decoration : underline ;
} } """
if props . get ( ' link_hover_color ' ) :
link_css + = f """
a : hover { {
color : { props [ ' link_hover_color ' ] } ;
} } """
else :
link_css + = """
a : hover {
opacity : 0.8 ;
} """
# Branding accents (if specified and no link_color already set)
feat: implement sophisticated layered theme system for md-render
MAJOR FEATURES:
- **Layered Theme Architecture**: Combine themes across UI, document, and branding scopes
- **Advanced Theme Combinations**: Support complex themes like "dark,academic" or "light,github,corporate"
- **Legacy Compatibility**: Existing --template usage continues to work seamlessly
- **Enhanced CLI Validation**: Proper theme validation with helpful error messages
TECHNICAL IMPROVEMENTS:
- Replace DocumentManager with CleanDocumentManager throughout codebase
- Add ThemeType custom click parameter with comprehensive validation
- Implement parse_theme_string() and combine_theme_properties() functions
- Add _get_template_css() and _generate_layered_css() methods
- Support for UI themes (light/dark), document themes (basic/github/academic), and branding themes (corporate/startup)
THEME CAPABILITIES:
- **Single themes**: basic, github, dark, academic, light, corporate, startup
- **Layered themes**: dark,academic combines dark UI with academic typography
- **Complex combinations**: light,github,corporate for branded GitHub-style documents
- **Intelligent property merging**: Later themes override earlier theme properties
QUALITY ASSURANCE:
- All template system tests passing (12/12)
- Fixed import errors and missing dependencies
- Updated test expectations for new validation messages
- Comprehensive validation prevents unknown theme usage
Breaking Change: --template parameter renamed to --theme with enhanced functionality
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-27 21:31:08 +01:00
accent_css = " "
2025-10-27 21:56:38 +01:00
if props . get ( ' accent_color ' ) and not props . get ( ' link_color ' ) :
feat: implement sophisticated layered theme system for md-render
MAJOR FEATURES:
- **Layered Theme Architecture**: Combine themes across UI, document, and branding scopes
- **Advanced Theme Combinations**: Support complex themes like "dark,academic" or "light,github,corporate"
- **Legacy Compatibility**: Existing --template usage continues to work seamlessly
- **Enhanced CLI Validation**: Proper theme validation with helpful error messages
TECHNICAL IMPROVEMENTS:
- Replace DocumentManager with CleanDocumentManager throughout codebase
- Add ThemeType custom click parameter with comprehensive validation
- Implement parse_theme_string() and combine_theme_properties() functions
- Add _get_template_css() and _generate_layered_css() methods
- Support for UI themes (light/dark), document themes (basic/github/academic), and branding themes (corporate/startup)
THEME CAPABILITIES:
- **Single themes**: basic, github, dark, academic, light, corporate, startup
- **Layered themes**: dark,academic combines dark UI with academic typography
- **Complex combinations**: light,github,corporate for branded GitHub-style documents
- **Intelligent property merging**: Later themes override earlier theme properties
QUALITY ASSURANCE:
- All template system tests passing (12/12)
- Fixed import errors and missing dependencies
- Updated test expectations for new validation messages
- Comprehensive validation prevents unknown theme usage
Breaking Change: --template parameter renamed to --theme with enhanced functionality
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-27 21:31:08 +01:00
accent_css = f """
a { {
color : { props [ ' accent_color ' ] } ;
} }
a : hover { {
opacity : 0.8 ;
} } """
2025-10-27 22:33:51 +01:00
# UI theme styling for editor interface elements
ui_css = " "
if props . get ( ' editor_panel_bg ' ) :
ui_css = f """
2025-10-27 23:00:42 +01:00
. markitect - edit - mode . ui - edit - floater - panel { {
2025-10-27 22:33:51 +01:00
background : { props [ ' editor_panel_bg ' ] } ;
border : 1 px solid { props . get ( ' editor_panel_border ' , ' #dee2e6 ' ) } ;
box - shadow : 0 4 px 12 px { props . get ( ' editor_shadow ' , ' rgba(0,0,0,0.1) ' ) } ;
color : { props . get ( ' editor_text_color ' , ' #212529 ' ) } ;
} }
2025-10-27 23:00:42 +01:00
. markitect - edit - mode . ui - edit - floater - header { {
color : { props . get ( ' editor_text_color ' , ' #212529 ' ) } ;
} }
2025-10-28 03:50:21 +01:00
. markitect - edit - mode . ui - edit - floater - header h3 { {
color : { props . get ( ' editor_text_color ' , ' #212529 ' ) } ;
} }
. markitect - edit - mode . ui - edit - inline - panel { {
background : { props [ ' editor_panel_bg ' ] } ;
border : 1 px solid { props . get ( ' editor_panel_border ' , ' #dee2e6 ' ) } ;
box - shadow : 0 2 px 8 px { props . get ( ' editor_shadow ' , ' rgba(0,0,0,0.1) ' ) } ;
color : { props . get ( ' editor_text_color ' , ' #212529 ' ) } ;
border - radius : 8 px ;
padding : 12 px ;
margin : 8 px 0 ;
} }
2025-10-27 23:00:42 +01:00
. markitect - edit - mode . ui - edit - button { {
2025-10-27 22:33:51 +01:00
background : { props . get ( ' editor_button_bg ' , ' #ffffff ' ) } ;
color : { props . get ( ' editor_text_color ' , ' #212529 ' ) } ;
border : 1 px solid { props . get ( ' editor_panel_border ' , ' #dee2e6 ' ) } ;
2025-10-28 03:50:21 +01:00
padding : 8 px 12 px ;
border - radius : 4 px ;
cursor : pointer ;
font - size : 12 px ;
min - width : 70 px ;
font - weight : 500 ;
transition : all 0.2 s ;
2025-10-27 22:33:51 +01:00
} }
2025-10-27 23:00:42 +01:00
. markitect - edit - mode . ui - edit - button : hover { {
2025-10-27 22:33:51 +01:00
background : { props . get ( ' editor_button_hover ' , ' #e9ecef ' ) } ;
} }
2025-10-27 23:00:42 +01:00
. markitect - edit - mode . ui - edit - button : active ,
. markitect - edit - mode . ui - edit - button . active { {
2025-10-27 22:33:51 +01:00
background : { props . get ( ' editor_button_active ' , ' #dee2e6 ' ) } ;
} }
2025-10-27 23:00:42 +01:00
. markitect - edit - mode . ui - edit - button - accept { {
2025-10-28 03:50:21 +01:00
background : { props . get ( ' editor_accept_bg ' , ' #4caf50 ' ) } ;
color : white ;
} }
. markitect - edit - mode . ui - edit - button - accept : hover { {
background : { props . get ( ' editor_accept_hover ' , ' #388e3c ' ) } ;
2025-10-27 23:00:42 +01:00
} }
. markitect - edit - mode . ui - edit - button - cancel { {
2025-10-28 03:50:21 +01:00
background : { props . get ( ' editor_cancel_bg ' , ' #f44336 ' ) } ;
color : white ;
} }
. markitect - edit - mode . ui - edit - button - cancel : hover { {
background : { props . get ( ' editor_cancel_hover ' , ' #d32f2f ' ) } ;
2025-10-27 23:00:42 +01:00
} }
. markitect - edit - mode . ui - edit - button - reset { {
2025-10-28 03:50:21 +01:00
background : { props . get ( ' editor_reset_bg ' , ' #ff9800 ' ) } ;
color : white ;
} }
. markitect - edit - mode . ui - edit - button - reset : hover { {
background : { props . get ( ' editor_reset_hover ' , ' #f57c00 ' ) } ;
} }
. markitect - edit - mode . ui - edit - button - secondary { {
background : { props . get ( ' editor_secondary_bg ' , ' #6c757d ' ) } ;
color : white ;
} }
. markitect - edit - mode . ui - edit - button - secondary : hover { {
background : { props . get ( ' editor_secondary_hover ' , ' #545b62 ' ) } ;
2025-10-27 23:00:42 +01:00
} }
. markitect - edit - mode . ui - edit - section - frame { {
2025-10-28 03:50:21 +01:00
border : 2 px solid { props . get ( ' editor_focus_color ' , props . get ( ' editor_panel_border ' , ' #dee2e6 ' ) ) } ;
box - shadow : 0 0 0 3 px { props . get ( ' editor_focus_color ' , props . get ( ' editor_panel_border ' , ' #dee2e6 ' ) ) } 33 ;
2025-10-27 22:33:51 +01:00
} }
2025-10-27 23:00:42 +01:00
. markitect - edit - mode . ui - edit - textarea { {
2025-10-27 22:33:51 +01:00
border : 1 px solid { props . get ( ' editor_panel_border ' , ' #dee2e6 ' ) } ;
color : { props . get ( ' editor_text_color ' , ' #212529 ' ) } ;
2025-10-27 23:00:42 +01:00
background : { props . get ( ' editor_button_bg ' , ' #ffffff ' ) } ;
2025-10-27 22:33:51 +01:00
} }
2025-10-27 23:00:42 +01:00
. markitect - edit - mode . ui - edit - textarea : focus { {
2025-10-28 03:50:21 +01:00
border - color : { props . get ( ' editor_focus_color ' , props . get ( ' editor_panel_border ' , ' #dee2e6 ' ) ) } ;
box - shadow : 0 0 0 2 px { props . get ( ' editor_focus_color ' , props . get ( ' editor_panel_border ' , ' #dee2e6 ' ) ) } 33 ;
} }
. markitect - edit - 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.3 s , visibility 0.3 s ;
} }
. markitect - edit - mode . ui - edit - modal - overlay . active { {
opacity : 1 ;
visibility : visible ;
} }
. markitect - edit - mode . ui - edit - modal { {
background : { props [ ' editor_panel_bg ' ] } ;
border : 1 px solid { props . get ( ' editor_panel_border ' , ' #dee2e6 ' ) } ;
box - shadow : 0 8 px 32 px { props . get ( ' editor_shadow ' , ' rgba(0,0,0,0.2) ' ) } ;
color : { props . get ( ' editor_text_color ' , ' #212529 ' ) } ;
border - radius : 8 px ;
max - width : 600 px ;
max - height : 80 vh ;
width : 90 % ;
overflow : hidden ;
transform : scale ( 0.9 ) translateY ( - 20 px ) ;
transition : transform 0.3 s ;
} }
. markitect - edit - mode . ui - edit - modal - overlay . active . ui - edit - modal { {
transform : scale ( 1 ) translateY ( 0 ) ;
} }
. markitect - edit - mode . ui - edit - modal - header { {
padding : 20 px 24 px 16 px ;
border - bottom : 1 px solid { props . get ( ' editor_panel_border ' , ' #dee2e6 ' ) } ;
display : flex ;
justify - content : space - between ;
align - items : center ;
} }
. markitect - edit - mode . ui - edit - modal - title { {
margin : 0 ;
font - size : 18 px ;
font - weight : 600 ;
color : { props . get ( ' editor_text_color ' , ' #212529 ' ) } ;
} }
. markitect - edit - mode . ui - edit - modal - close { {
background : none ;
border : none ;
font - size : 24 px ;
cursor : pointer ;
color : { props . get ( ' editor_text_color ' , ' #212529 ' ) } ;
padding : 4 px ;
border - radius : 4 px ;
width : 32 px ;
height : 32 px ;
display : flex ;
align - items : center ;
justify - content : center ;
transition : background - color 0.2 s ;
} }
. markitect - edit - mode . ui - edit - modal - close : hover { {
background : { props . get ( ' editor_button_hover ' , ' #e9ecef ' ) } ;
} }
. markitect - edit - mode . ui - edit - modal - body { {
padding : 20 px 24 px ;
overflow - y : auto ;
max - height : 60 vh ;
} }
. markitect - edit - mode . ui - edit - modal - content { {
white - space : pre - line ;
line - height : 1.5 ;
font - family : ' Segoe UI ' , - apple - system , BlinkMacSystemFont , sans - serif ;
font - size : 14 px ;
} }
. markitect - edit - mode . ui - edit - modal - section { {
margin - bottom : 16 px ;
} }
. markitect - edit - mode . ui - edit - modal - section : last - child { {
margin - bottom : 0 ;
} }
. markitect - edit - mode . ui - edit - modal - section - title { {
font - weight : 600 ;
margin - bottom : 8 px ;
color : { props . get ( ' editor_text_color ' , ' #212529 ' ) } ;
} }
. markitect - edit - mode . ui - edit - modal - footer { {
padding : 16 px 24 px 20 px ;
border - top : 1 px solid { props . get ( ' editor_panel_border ' , ' #dee2e6 ' ) } ;
text - align : right ;
} }
2025-10-28 22:36:15 +01:00
/ * Confirmation Dialog Styles * /
. markitect - edit - mode . ui - edit - confirmation - modal { {
max - width : 500 px ;
} }
. markitect - edit - mode . ui - edit - confirmation - content { {
font - size : 16 px ;
line - height : 1.5 ;
margin - bottom : 24 px ;
color : { props . get ( ' editor_text_color ' , ' #212529 ' ) } ;
} }
. markitect - edit - mode . ui - edit - confirmation - warning { {
background : { props . get ( ' editor_warning_bg ' , ' #fff3cd ' ) } ;
border : 1 px solid { props . get ( ' editor_warning_border ' , ' #ffeaa7 ' ) } ;
color : { props . get ( ' editor_warning_text ' , ' #856404 ' ) } ;
padding : 12 px 16 px ;
border - radius : 6 px ;
margin : 16 px 0 ;
font - size : 14 px ;
} }
. markitect - edit - mode . ui - edit - confirmation - buttons { {
display : flex ;
gap : 12 px ;
justify - content : flex - end ;
} }
. markitect - edit - mode . ui - edit - button - confirm { {
background : { props . get ( ' editor_danger_button ' , ' #dc3545 ' ) } ;
color : white ;
border : none ;
padding : 10 px 20 px ;
border - radius : 6 px ;
font - size : 14 px ;
font - weight : 500 ;
cursor : pointer ;
transition : background - color 0.2 s , transform 0.1 s ;
} }
. markitect - edit - mode . ui - edit - button - confirm : hover { {
background : { props . get ( ' editor_danger_button_hover ' , ' #c82333 ' ) } ;
transform : translateY ( - 1 px ) ;
} }
. markitect - edit - mode . ui - edit - button - confirm : active { {
transform : translateY ( 0 ) ;
} }
. markitect - edit - mode . ui - edit - button - confirm : focus { {
outline : 2 px solid { props . get ( ' editor_focus_color ' , ' #007bff ' ) } ;
outline - offset : 2 px ;
} }
. markitect - edit - mode . ui - edit - button - cancel { {
background : { props . get ( ' editor_secondary_button ' , ' #6c757d ' ) } ;
color : white ;
border : none ;
padding : 10 px 20 px ;
border - radius : 6 px ;
font - size : 14 px ;
font - weight : 500 ;
cursor : pointer ;
transition : background - color 0.2 s , transform 0.1 s ;
} }
. markitect - edit - mode . ui - edit - button - cancel : hover { {
background : { props . get ( ' editor_secondary_button_hover ' , ' #545b62 ' ) } ;
transform : translateY ( - 1 px ) ;
} }
. markitect - edit - mode . ui - edit - button - cancel : active { {
transform : translateY ( 0 ) ;
} }
. markitect - edit - mode . ui - edit - button - cancel : focus { {
outline : 2 px solid { props . get ( ' editor_focus_color ' , ' #007bff ' ) } ;
outline - offset : 2 px ;
} }
/ * Document Scroll Indicators * /
. ui - scroll - indicator { {
position : fixed ;
left : 50 % ;
transform : translateX ( - 50 % ) ;
width : 60 px ;
height : 30 px ;
background : { props . get ( ' editor_panel_bg ' , ' #f8f9fa ' ) } ;
border : 1 px solid { props . get ( ' editor_panel_border ' , ' #dee2e6 ' ) } ;
border - radius : 15 px ;
display : flex ;
align - items : center ;
justify - content : center ;
cursor : pointer ;
opacity : 0 ;
visibility : hidden ;
transition : opacity 0.3 s ease , visibility 0.3 s ease , transform 0.2 s ease , background - color 0.2 s ease ;
z - index : 1000 ;
box - shadow : 0 4 px 12 px { props . get ( ' editor_shadow ' , ' rgba(0,0,0,0.15) ' ) } ;
} }
. ui - scroll - indicator : hover { {
transform : translateX ( - 50 % ) scale ( 1.05 ) ;
} }
. ui - scroll - indicator : not ( . disabled ) : hover { {
background : { props . get ( ' editor_button_hover ' , ' #e9ecef ' ) } ;
} }
. ui - scroll - indicator . active { {
opacity : 0.9 ;
visibility : visible ;
} }
. ui - scroll - indicator . disabled { {
background : { props . get ( ' editor_button_active ' , ' #dee2e6 ' ) } ;
cursor : not - allowed ;
opacity : 0.6 ;
} }
. ui - scroll - indicator . disabled : hover { {
transform : translateX ( - 50 % ) ;
background : { props . get ( ' editor_button_active ' , ' #dee2e6 ' ) } ;
} }
. ui - scroll - indicator - up { {
top : 20 px ;
} }
. ui - scroll - indicator - down { {
bottom : 20 px ;
} }
. ui - scroll - indicator : : before { {
content : ' ' ;
width : 0 ;
height : 0 ;
border - style : solid ;
transition : border - color 0.2 s ease ;
} }
. ui - scroll - indicator - up : : before { {
border - left : 8 px solid transparent ;
border - right : 8 px solid transparent ;
border - bottom : 12 px solid { props . get ( ' editor_text_color ' , ' #212529 ' ) } ;
} }
. ui - scroll - indicator - down : : before { {
border - left : 8 px solid transparent ;
border - right : 8 px solid transparent ;
border - top : 12 px solid { props . get ( ' editor_text_color ' , ' #212529 ' ) } ;
} }
. ui - scroll - indicator . disabled . ui - scroll - indicator - up : : before { {
border - bottom - color : { props . get ( ' editor_secondary_button ' , ' #6c757d ' ) } ;
} }
. ui - scroll - indicator . disabled . ui - scroll - indicator - down : : before { {
border - top - color : { props . get ( ' editor_secondary_button ' , ' #6c757d ' ) } ;
} }
2025-10-28 23:55:21 +01:00
/ * Insert Mode Specific Styles * /
. markitect - insert - mode . ui - edit - floater - panel { {
background : { props [ ' editor_panel_bg ' ] } ;
border : 1 px solid { props . get ( ' editor_panel_border ' , ' #dee2e6 ' ) } ;
box - shadow : 0 4 px 12 px { 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 : 1 px solid { props . get ( ' editor_panel_border ' , ' #dee2e6 ' ) } ;
box - shadow : 0 2 px 8 px { props . get ( ' editor_shadow ' , ' rgba(0,0,0,0.1) ' ) } ;
color : { props . get ( ' editor_text_color ' , ' #212529 ' ) } ;
border - radius : 8 px ;
padding : 12 px ;
margin : 8 px 0 ;
} }
. markitect - insert - mode . ui - insert - protected - panel { {
border - left : 4 px solid #ff9800;
} }
. markitect - insert - mode . ui - insert - heading - display { {
font - family : ' SF Mono ' , Monaco , ' Cascadia Code ' , monospace ;
font - size : 14 px ;
font - weight : bold ;
padding : 8 px 12 px ;
background : { props . get ( ' editor_warning_bg ' , ' #fff3cd ' ) } ;
border : 1 px solid { props . get ( ' editor_warning_border ' , ' #ffeaa7 ' ) } ;
border - radius : 4 px ;
border - left : 4 px solid #007bff;
color : { props . get ( ' editor_warning_text ' , ' #856404 ' ) } ;
margin - bottom : 8 px ;
} }
. markitect - insert - mode . ui - insert - content - editor { {
border : 1 px 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 2 px { 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 : 1 px solid { props . get ( ' editor_panel_border ' , ' #dee2e6 ' ) } ;
padding : 8 px 12 px ;
border - radius : 4 px ;
cursor : pointer ;
font - size : 12 px ;
min - width : 70 px ;
font - weight : 500 ;
transition : all 0.2 s ;
} }
. 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 : 2 px solid { props . get ( ' editor_focus_color ' , ' #007bff ' ) } ;
box - shadow : 0 0 0 3 px { 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.3 s , visibility 0.3 s ;
} }
. markitect - insert - mode . ui - edit - modal - overlay . active { {
opacity : 1 ;
visibility : visible ;
} }
. markitect - insert - mode . ui - edit - modal { {
background : { props [ ' editor_panel_bg ' ] } ;
border : 1 px solid { props . get ( ' editor_panel_border ' , ' #dee2e6 ' ) } ;
box - shadow : 0 8 px 32 px { props . get ( ' editor_shadow ' , ' rgba(0,0,0,0.2) ' ) } ;
color : { props . get ( ' editor_text_color ' , ' #212529 ' ) } ;
border - radius : 8 px ;
max - width : 600 px ;
max - height : 80 vh ;
width : 90 % ;
overflow : hidden ;
transform : scale ( 0.9 ) translateY ( - 20 px ) ;
transition : transform 0.3 s ;
} }
. markitect - insert - mode . ui - edit - modal - overlay . active . ui - edit - modal { {
transform : scale ( 1 ) translateY ( 0 ) ;
} }
. markitect - insert - mode . ui - edit - modal - header { {
padding : 20 px 24 px 16 px ;
border - bottom : 1 px 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 : 18 px ;
font - weight : 600 ;
color : { props . get ( ' editor_text_color ' , ' #212529 ' ) } ;
} }
. markitect - insert - mode . ui - edit - modal - close { {
background : transparent ;
border : none ;
font - size : 24 px ;
cursor : pointer ;
color : { props . get ( ' editor_text_color ' , ' #212529 ' ) } ;
padding : 0 ;
width : 32 px ;
height : 32 px ;
display : flex ;
align - items : center ;
justify - content : center ;
border - radius : 4 px ;
transition : background - color 0.2 s ;
} }
. markitect - insert - mode . ui - edit - modal - close : hover { {
background : { props . get ( ' editor_button_hover ' , ' #e9ecef ' ) } ;
} }
. markitect - insert - mode . ui - edit - modal - body { {
padding : 20 px 24 px ;
max - height : 400 px ;
overflow - y : auto ;
color : { props . get ( ' editor_text_color ' , ' #212529 ' ) } ;
} }
. markitect - insert - mode . ui - edit - modal - section { {
margin - bottom : 8 px ;
color : { props . get ( ' editor_text_color ' , ' #212529 ' ) } ;
} }
. markitect - insert - mode . ui - edit - modal - footer { {
padding : 16 px 24 px 20 px ;
border - top : 1 px 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 : 500 px ;
} }
. markitect - insert - mode . ui - edit - confirmation - content { {
font - size : 16 px ;
line - height : 1.5 ;
margin - bottom : 24 px ;
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 : 1 px solid { props . get ( ' editor_warning_border ' , ' #ffeaa7 ' ) } ;
border - radius : 6 px ;
padding : 12 px 16 px ;
margin : 16 px 0 ;
font - size : 14 px ;
line - height : 1.4 ;
} }
. markitect - insert - mode . ui - edit - confirmation - buttons { {
display : flex ;
gap : 12 px ;
justify - content : flex - end ;
margin - top : 24 px ;
} }
. markitect - insert - mode . ui - edit - button - confirm { {
background : #dc3545;
color : white ;
border : 1 px solid #dc3545;
padding : 10 px 20 px ;
border - radius : 6 px ;
font - size : 14 px ;
font - weight : 500 ;
cursor : pointer ;
transition : all 0.2 s ;
} }
. 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 : 1 px solid { props . get ( ' editor_panel_border ' , ' #dee2e6 ' ) } ;
padding : 10 px 20 px ;
border - radius : 6 px ;
font - size : 14 px ;
font - weight : 500 ;
cursor : pointer ;
transition : all 0.2 s ;
} }
. markitect - insert - mode . ui - edit - button - cancel : hover { {
background : { props . get ( ' editor_button_hover ' , ' #e9ecef ' ) } ;
} }
2025-10-28 22:36:15 +01:00
"""
2025-10-27 22:33:51 +01:00
2025-11-04 09:11:50 +01:00
# Image size CSS with configurable limits
image_css = f """
img { {
max - width : { image_max_width } ;
max - height : { image_max_height } ;
height : auto ;
display : block ;
margin : 1 rem auto ;
} } """
return f " <style> { base_css } { heading_css } { text_css } { element_css } { link_css } { accent_css } { ui_css } { image_css } </style> "
feat: implement sophisticated layered theme system for md-render
MAJOR FEATURES:
- **Layered Theme Architecture**: Combine themes across UI, document, and branding scopes
- **Advanced Theme Combinations**: Support complex themes like "dark,academic" or "light,github,corporate"
- **Legacy Compatibility**: Existing --template usage continues to work seamlessly
- **Enhanced CLI Validation**: Proper theme validation with helpful error messages
TECHNICAL IMPROVEMENTS:
- Replace DocumentManager with CleanDocumentManager throughout codebase
- Add ThemeType custom click parameter with comprehensive validation
- Implement parse_theme_string() and combine_theme_properties() functions
- Add _get_template_css() and _generate_layered_css() methods
- Support for UI themes (light/dark), document themes (basic/github/academic), and branding themes (corporate/startup)
THEME CAPABILITIES:
- **Single themes**: basic, github, dark, academic, light, corporate, startup
- **Layered themes**: dark,academic combines dark UI with academic typography
- **Complex combinations**: light,github,corporate for branded GitHub-style documents
- **Intelligent property merging**: Later themes override earlier theme properties
QUALITY ASSURANCE:
- All template system tests passing (12/12)
- Fixed import errors and missing dependencies
- Updated test expectations for new validation messages
- Comprehensive validation prevents unknown theme usage
Breaking Change: --template parameter renamed to --theme with enhanced functionality
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-27 21:31:08 +01:00
2025-11-04 09:11:50 +01:00
def _get_legacy_template_css ( self , template : str , image_max_width : str = ' 12cm ' , image_max_height : str = ' 20cm ' ) - > str :
feat: implement sophisticated layered theme system for md-render
MAJOR FEATURES:
- **Layered Theme Architecture**: Combine themes across UI, document, and branding scopes
- **Advanced Theme Combinations**: Support complex themes like "dark,academic" or "light,github,corporate"
- **Legacy Compatibility**: Existing --template usage continues to work seamlessly
- **Enhanced CLI Validation**: Proper theme validation with helpful error messages
TECHNICAL IMPROVEMENTS:
- Replace DocumentManager with CleanDocumentManager throughout codebase
- Add ThemeType custom click parameter with comprehensive validation
- Implement parse_theme_string() and combine_theme_properties() functions
- Add _get_template_css() and _generate_layered_css() methods
- Support for UI themes (light/dark), document themes (basic/github/academic), and branding themes (corporate/startup)
THEME CAPABILITIES:
- **Single themes**: basic, github, dark, academic, light, corporate, startup
- **Layered themes**: dark,academic combines dark UI with academic typography
- **Complex combinations**: light,github,corporate for branded GitHub-style documents
- **Intelligent property merging**: Later themes override earlier theme properties
QUALITY ASSURANCE:
- All template system tests passing (12/12)
- Fixed import errors and missing dependencies
- Updated test expectations for new validation messages
- Comprehensive validation prevents unknown theme usage
Breaking Change: --template parameter renamed to --theme with enhanced functionality
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-27 21:31:08 +01:00
""" Legacy CSS generation - kept for backward compatibility. """
# Import template styles
from markitect . plugins . builtin . markdown_commands import TEMPLATE_STYLES
# Use basic as default if no template specified
if not template or template not in TEMPLATE_STYLES :
template = ' basic '
style_config = TEMPLATE_STYLES [ template ]
# Base CSS that's common to all templates
base_css = f """
body { {
font - family : { style_config [ ' font_family ' ] } ;
max - width : { style_config [ ' max_width ' ] } ;
margin : 0 auto ;
padding : 2 rem ;
line - height : 1.6 ;
color : { style_config [ ' body_color ' ] } ;
} }
#markdown-content {{
min - height : 200 px ;
} } """
# Convert legacy template config to layered format
legacy_config = TEMPLATE_STYLES [ template ]
layered_props = {
' font_family ' : legacy_config [ ' font_family ' ] ,
' max_width ' : legacy_config [ ' max_width ' ] ,
' body_color ' : legacy_config [ ' body_color ' ] ,
}
2025-11-04 09:11:50 +01:00
return self . _generate_layered_css ( layered_props , image_max_width , image_max_height )
feat: implement sophisticated layered theme system for md-render
MAJOR FEATURES:
- **Layered Theme Architecture**: Combine themes across UI, document, and branding scopes
- **Advanced Theme Combinations**: Support complex themes like "dark,academic" or "light,github,corporate"
- **Legacy Compatibility**: Existing --template usage continues to work seamlessly
- **Enhanced CLI Validation**: Proper theme validation with helpful error messages
TECHNICAL IMPROVEMENTS:
- Replace DocumentManager with CleanDocumentManager throughout codebase
- Add ThemeType custom click parameter with comprehensive validation
- Implement parse_theme_string() and combine_theme_properties() functions
- Add _get_template_css() and _generate_layered_css() methods
- Support for UI themes (light/dark), document themes (basic/github/academic), and branding themes (corporate/startup)
THEME CAPABILITIES:
- **Single themes**: basic, github, dark, academic, light, corporate, startup
- **Layered themes**: dark,academic combines dark UI with academic typography
- **Complex combinations**: light,github,corporate for branded GitHub-style documents
- **Intelligent property merging**: Later themes override earlier theme properties
QUALITY ASSURANCE:
- All template system tests passing (12/12)
- Fixed import errors and missing dependencies
- Updated test expectations for new validation messages
- Comprehensive validation prevents unknown theme usage
Breaking Change: --template parameter renamed to --theme with enhanced functionality
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-27 21:31:08 +01:00
def _generate_html_template ( self , markdown_content : str , title : str , css : str = None , template : str = None ,
2025-11-04 09:11:50 +01:00
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 ,
image_max_width : str = ' 12cm ' , image_max_height : str = ' 20cm ' , base64_references : dict = None ) - > str :
feat: implement sophisticated layered theme system for md-render
MAJOR FEATURES:
- **Layered Theme Architecture**: Combine themes across UI, document, and branding scopes
- **Advanced Theme Combinations**: Support complex themes like "dark,academic" or "light,github,corporate"
- **Legacy Compatibility**: Existing --template usage continues to work seamlessly
- **Enhanced CLI Validation**: Proper theme validation with helpful error messages
TECHNICAL IMPROVEMENTS:
- Replace DocumentManager with CleanDocumentManager throughout codebase
- Add ThemeType custom click parameter with comprehensive validation
- Implement parse_theme_string() and combine_theme_properties() functions
- Add _get_template_css() and _generate_layered_css() methods
- Support for UI themes (light/dark), document themes (basic/github/academic), and branding themes (corporate/startup)
THEME CAPABILITIES:
- **Single themes**: basic, github, dark, academic, light, corporate, startup
- **Layered themes**: dark,academic combines dark UI with academic typography
- **Complex combinations**: light,github,corporate for branded GitHub-style documents
- **Intelligent property merging**: Later themes override earlier theme properties
QUALITY ASSURANCE:
- All template system tests passing (12/12)
- Fixed import errors and missing dependencies
- Updated test expectations for new validation messages
- Comprehensive validation prevents unknown theme usage
Breaking Change: --template parameter renamed to --theme with enhanced functionality
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-27 21:31:08 +01:00
""" Generate clean HTML template. """
2025-10-28 03:50:21 +01:00
# Add dogtag to markdown content if not disabled
if not nodogtag :
import datetime
import getpass
now = datetime . datetime . now ( )
datetime_str = now . strftime ( " % Y- % m- %d % H: % M: % S " )
try :
username = getpass . getuser ( )
except :
username = " user "
# Create username link only for 'worsch', otherwise just show username
if username == ' worsch ' :
username_link = f ' <a href= " https://coulomb.social/open/worsch " target= " _blank " > { username } </a> '
else :
username_link = username
dogtag = f ' \n \n --- \n *-- html from markdown by <a href= " https://coulomb.social/open/MarkiTect " target= " _blank " >MarkiTect</a> on { datetime_str } by { username_link } * '
markdown_content_with_dogtag = markdown_content + dogtag
else :
markdown_content_with_dogtag = markdown_content
2025-11-04 09:11:50 +01:00
dogtag = " "
2025-10-28 03:50:21 +01:00
2025-11-04 09:11:50 +01:00
# Pass original markdown content to editor (without dogtag for editing)
# But make dogtag available separately for protected display in editor
js_markdown_content = json . dumps ( markdown_content )
js_markdown_content_with_dogtag = json . dumps ( markdown_content_with_dogtag )
js_dogtag_content = json . dumps ( dogtag )
js_base64_references = json . dumps ( base64_references or { } )
feat: implement sophisticated layered theme system for md-render
MAJOR FEATURES:
- **Layered Theme Architecture**: Combine themes across UI, document, and branding scopes
- **Advanced Theme Combinations**: Support complex themes like "dark,academic" or "light,github,corporate"
- **Legacy Compatibility**: Existing --template usage continues to work seamlessly
- **Enhanced CLI Validation**: Proper theme validation with helpful error messages
TECHNICAL IMPROVEMENTS:
- Replace DocumentManager with CleanDocumentManager throughout codebase
- Add ThemeType custom click parameter with comprehensive validation
- Implement parse_theme_string() and combine_theme_properties() functions
- Add _get_template_css() and _generate_layered_css() methods
- Support for UI themes (light/dark), document themes (basic/github/academic), and branding themes (corporate/startup)
THEME CAPABILITIES:
- **Single themes**: basic, github, dark, academic, light, corporate, startup
- **Layered themes**: dark,academic combines dark UI with academic typography
- **Complex combinations**: light,github,corporate for branded GitHub-style documents
- **Intelligent property merging**: Later themes override earlier theme properties
QUALITY ASSURANCE:
- All template system tests passing (12/12)
- Fixed import errors and missing dependencies
- Updated test expectations for new validation messages
- Comprehensive validation prevents unknown theme usage
Breaking Change: --template parameter renamed to --theme with enhanced functionality
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-27 21:31:08 +01:00
# Handle CSS styles
css_content = " "
if css :
try :
css_path = Path ( css )
if css_path . exists ( ) :
css_file_content = css_path . read_text ( encoding = ' utf-8 ' )
css_content = f " <style> \n { css_file_content } \n </style> "
else :
css_content = f ' <link rel= " stylesheet " href= " { css } " > '
except Exception :
css_content = f ' <link rel= " stylesheet " href= " { css } " > '
# Generate template-specific CSS
2025-11-04 09:11:50 +01:00
default_css = self . _get_template_css ( template , image_max_width , image_max_height )
2025-10-26 07:51:43 +01:00
# Load clean editor JavaScript files
editor_scripts = " "
editor_config = " "
body_classes = " "
if edit_mode :
body_classes = ' class= " markitect-edit-mode " '
# Configuration for clean editor
2025-10-28 03:50:21 +01:00
version_str = f " { version_info [ ' repo_name ' ] } v { version_info [ ' version ' ] } { version_info [ ' git_info ' ] } " if version_info else " Markitect v0.5.0.dev "
2025-10-26 07:51:43 +01:00
editor_config = f """
const MARKITECT_EDIT_MODE = true ;
const MARKITECT_EDITOR_CONFIG = { {
2025-10-28 23:55:21 +01:00
mode : ' edit ' ,
2025-10-26 07:51:43 +01:00
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 ; """
2025-10-28 23:55:21 +01:00
elif insert_mode :
body_classes = ' class= " markitect-insert-mode " '
2025-10-26 07:51:43 +01:00
2025-10-28 23:55:21 +01:00
# 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 ,
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 ; """
# Load clean editor architecture for both edit and insert modes
if edit_mode or insert_mode :
2025-10-26 07:51:43 +01:00
editor_scripts = self . _get_clean_editor_scripts ( )
2025-11-04 09:11:50 +01:00
else :
editor_scripts = " "
2025-10-26 07:51:43 +01:00
# Generate the complete HTML template
html_template = f """ <!DOCTYPE html>
< html lang = " en " >
< head >
< meta charset = " utf-8 " >
< meta name = " viewport " content = " width=device-width, initial-scale=1.0 " >
< title > { title } < / title >
{ css_content }
{ default_css }
< script src = " https://cdn.jsdelivr.net/npm/marked/marked.min.js "
onload = " window.markitectMarkedLoaded = true "
onerror = " window.markitectMarkedError = true " > < / script >
< / head >
< body { body_classes } >
< div id = " markdown-content " > < / div >
< script >
const markdownContent = { js_markdown_content } ;
2025-11-04 09:11:50 +01:00
const markdownContentWithDogtag = { js_markdown_content_with_dogtag } ;
const dogtagContent = { js_dogtag_content } ;
window . markitectBase64References = { js_base64_references } ;
2025-10-26 07:51:43 +01:00
{ editor_config }
{ editor_scripts }
/ / Always render content first ( graceful degradation )
document . addEventListener ( ' DOMContentLoaded ' , function ( ) { {
console . log ( " Rendering content... " ) ;
2025-11-04 09:25:15 +01:00
/ / Check if modular components are being used
if ( typeof SectionManager != = ' undefined ' ) { {
console . log ( " ✓ Modular components detected - skipping direct content rendering " ) ;
console . log ( " ✓ Content will be rendered by modular architecture " ) ;
return ;
} }
2025-10-26 07:51:43 +01:00
const contentDiv = document . getElementById ( ' markdown-content ' ) ;
2025-11-04 09:25:15 +01:00
/ / Step 1 : Ensure content is always displayed ( fallback for non - modular mode )
2025-10-26 07:51:43 +01:00
if ( contentDiv ) { {
if ( typeof marked != = ' undefined ' ) { {
try { {
2025-11-04 09:11:50 +01:00
const html = marked . parse ( markdownContentWithDogtag ) ;
2025-10-28 03:50:21 +01:00
/ / Add target = " _blank " to all links
const htmlWithTargetBlank = html . replace ( / < a href = " ([^ " ] * ) " ([^>]*)>/g, ' <a href= " $ 1 " target= " _blank " $2> ' );
contentDiv . innerHTML = htmlWithTargetBlank ;
2025-10-26 07:51:43 +01:00
console . log ( " ✓ Content rendered successfully " ) ;
console . log ( ' ✓ Markdown rendered successfully ' ) ;
} } catch ( error ) { {
contentDiv . innerHTML = ' <p>Error rendering markdown: ' + error . message + ' </p> ' ;
console . error ( " Content rendered with errors " ) ;
console . error ( " Markdown parsing failed: " , error . message ) ;
} }
} } else { {
/ / Fallback : display raw markdown with basic formatting
const fallbackHtml = markdownContent
. replace ( / ^ # (.*$)/gim, '<h1>$1</h1>')
. replace ( / ^ ## (.*$)/gim, '<h2>$1</h2>')
. replace ( / ^ ### (.*$)/gim, '<h3>$1</h3>')
. replace ( / \\* \\* ( . * ? ) \\* \\* / g , ' <strong>$1</strong> ' )
. replace ( / \\* ( . * ? ) \\* / g , ' <em>$1</em> ' )
. replace ( / ^ - ( . * $ ) / gim , ' <li>$1</li> ' )
. replace ( / \\n \\n / g , ' <br><br> ' )
. replace ( / \\n / g , ' <br> ' ) ;
contentDiv . innerHTML = ' <div style= " white-space: pre-wrap; " > ' + fallbackHtml + ' </div> ' ;
console . warn ( " Content rendered with fallback parser " ) ;
console . warn ( " CDN library failed to load - using basic fallback rendering " ) ;
} }
} }
2025-10-28 23:55:21 +01:00
/ / 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 . . . ` ) ;
2025-10-26 07:51:43 +01:00
try { {
console . log ( " Creating clean editor instance... " ) ;
initializeCleanEditor ( ) ;
2025-10-28 23:55:21 +01:00
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 " ) ;
} }
2025-10-26 07:51:43 +01:00
} } catch ( error ) { {
2025-10-28 23:55:21 +01:00
console . error ( ` Clean $ { { mode } } mode failed to initialize : ` , error ) ;
2025-10-26 07:51:43 +01:00
} }
} }
2025-10-28 22:36:15 +01:00
/ / Step 3 : Initialize document scroll indicators ( always available )
try { {
initializeScrollIndicators ( ) ;
} } catch ( error ) { {
console . error ( " Scroll indicators failed to initialize: " , error ) ;
} }
2025-11-10 19:39:46 +01:00
2025-11-10 23:10:33 +01:00
/ / Step 4 : Define abstract Control class for UI controls
const Control = { {
/ / Abstract control properties
element : null ,
isExpanded : false ,
isDragging : false ,
dragOffset : { { x : 0 , y : 0 } } ,
originalPosition : { { top : ' 80px ' , left : ' 20px ' } } ,
/ / Configuration properties ( to be overridden by subclasses )
config : { {
icon : ' ? ' ,
title : ' Control ' ,
className : ' control ' ,
defaultContent : ' Template only ' ,
ariaLabel : ' Control ' ,
position : ' w ' / / Default compass position : west ( middle - left )
} } ,
/ / Compass positioning system ( top - aligned for proper expansion )
compassPositions : { {
/ / North positions ( top )
' n ' : { { top : ' 20px ' , left : ' 50 % ' , transform : ' translateX(-50 % ) ' } } ,
' nne ' : { { top : ' 20px ' , left : ' 65 % ' , transform : ' translateX(-50 % ) ' } } ,
' ne ' : { { top : ' 20px ' , right : ' 20px ' } } ,
' ene ' : { { top : ' 80px ' , right : ' 20px ' } } , / / Top - aligned instead of center
/ / East positions ( right )
' e ' : { { top : ' 50vh ' , right : ' 20px ' , transform : ' translateY(-20px) ' } } , / / Anchor at icon level
' ese ' : { { top : ' calc(65vh - 20px) ' , right : ' 20px ' } } , / / Top - aligned
' se ' : { { bottom : ' 20px ' , right : ' 20px ' } } ,
' sse ' : { { bottom : ' 20px ' , right : ' 35 % ' , transform : ' translateX(50 % ) ' } } ,
/ / South positions ( bottom )
' s ' : { { bottom : ' 20px ' , left : ' 50 % ' , transform : ' translateX(-50 % ) ' } } ,
' ssw ' : { { bottom : ' 20px ' , left : ' 35 % ' , transform : ' translateX(-50 % ) ' } } ,
' sw ' : { { bottom : ' 20px ' , left : ' 20px ' } } ,
' wsw ' : { { bottom : ' 80px ' , left : ' 20px ' } } , / / Top - aligned instead of center
/ / West positions ( left ) - top - aligned for proper expansion
' w ' : { { top : ' 50vh ' , left : ' 20px ' , transform : ' translateY(-20px) ' } } , / / Anchor at icon level
' wnw ' : { { top : ' 80px ' , left : ' 20px ' } } , / / Top - aligned instead of center
' nw ' : { { top : ' 20px ' , left : ' 20px ' } } ,
' nnw ' : { { top : ' 20px ' , left : ' 35 % ' , transform : ' translateX(-50 % ) ' } }
} } ,
/ / Get expansion direction based on compass position
getExpansionDirection : function ( ) { {
const pos = this . config . position ;
const rightBorderPositions = [ ' ne ' , ' ene ' , ' e ' , ' ese ' , ' se ' ] ;
const bottomBorderPositions = [ ' sw ' , ' ssw ' , ' s ' , ' sse ' , ' se ' ] ;
return { {
header : rightBorderPositions . includes ( pos ) ? ' left ' : ' right ' ,
body : bottomBorderPositions . includes ( pos ) ? ' up ' : ' down '
} } ;
} } ,
/ / Calculate position styles based on compass direction
getPositionStyles : function ( ) { {
const compassPos = this . compassPositions [ this . config . position ] | | this . compassPositions [ ' w ' ] ;
return { {
position : ' fixed ' ,
top : compassPos . top | | ' auto ' ,
right : compassPos . right | | ' auto ' ,
bottom : compassPos . bottom | | ' auto ' ,
left : compassPos . left | | ' auto ' ,
transform : compassPos . transform | | ' none ' ,
zIndex : 1000
} } ;
} } ,
/ / Abstract methods ( to be implemented by subclasses )
buildContent : function ( ) { {
const content = this . element . querySelector ( ' .control-content ' ) ;
content . innerHTML = ` < p style = " padding: 1rem; color: #666; " > $ { { this . config . defaultContent } } < / p > ` ;
} } ,
/ / Concrete methods ( shared by all controls )
createControl : function ( ) { {
console . log ( ` 🎛 ️ Creating $ { { this . config . title } } control . . . ` ) ;
this . element = document . createElement ( ' div ' ) ;
this . element . className = this . config . className ;
this . element . innerHTML = `
< button class = " control-toggle " aria - label = " $ {{ this.config.ariaLabel}} " > $ { { this . config . icon } } < / button >
< div class = " control-panel " style = " display: none; " >
< div class = " control-header " >
< span class = " control-icon " > $ { { this . config . icon } } < / span >
< h3 > $ { { this . config . title } } < / h3 >
< button class = " control-close " > ✕ < / button >
2025-11-10 19:39:46 +01:00
< / div >
2025-11-10 23:10:33 +01:00
< div class = " control-content " > Loading . . . < / div >
< / div >
` ;
2025-11-10 19:39:46 +01:00
2025-11-10 23:10:33 +01:00
/ / Position using compass direction
const positionStyles = this . getPositionStyles ( ) ;
this . element . style . cssText = `
position : $ { { positionStyles . position } } ;
top : $ { { positionStyles . top } } ;
right : $ { { positionStyles . right } } ;
bottom : $ { { positionStyles . bottom } } ;
left : $ { { positionStyles . left } } ;
transform : $ { { positionStyles . transform } } ;
z - index : $ { { positionStyles . zIndex } } ;
background : rgba ( 255 , 255 , 255 , 0.95 ) ;
border : 1 px solid #e1e5e9;
border - radius : 8 px ;
box - shadow : 0 2 px 8 px rgba ( 0 , 0 , 0 , 0.15 ) ;
backdrop - filter : blur ( 8 px ) ;
width : 40 px ;
transition : all 0.3 s ease ;
font - family : - apple - system , BlinkMacSystemFont , ' Segoe UI ' , sans - serif ;
` ;
2025-11-10 19:39:46 +01:00
2025-11-10 23:10:33 +01:00
/ / Store original position for reset
this . originalPosition = { {
top : positionStyles . top ,
right : positionStyles . right ,
bottom : positionStyles . bottom ,
left : positionStyles . left ,
transform : positionStyles . transform
} } ;
/ / Style toggle button
const toggleBtn = this . element . querySelector ( ' .control-toggle ' ) ;
toggleBtn . style . cssText = `
width : 100 % ;
height : 40 px ;
border : none ;
background : transparent ;
cursor : pointer ;
font - size : 16 px ;
color : #666;
transition : color 0.2 s ease ;
` ;
2025-11-10 19:39:46 +01:00
2025-11-10 23:10:33 +01:00
/ / Handle click to build content on - demand
toggleBtn . addEventListener ( ' click ' , ( ) = > { {
if ( this . isExpanded ) { {
2025-11-10 19:39:46 +01:00
this . collapse ( ) ;
2025-11-10 23:10:33 +01:00
} } else { {
console . log ( ` 🎛 ️ $ { { this . config . title } } toggle clicked - building content . . . ` ) ;
this . buildContent ( ) ;
} }
} } ) ;
2025-11-10 19:39:46 +01:00
2025-11-10 23:10:33 +01:00
/ / Close button handler
const closeBtn = this . element . querySelector ( ' .control-close ' ) ;
closeBtn . addEventListener ( ' click ' , ( ) = > { {
this . collapse ( ) ;
} } ) ;
2025-11-10 19:39:46 +01:00
2025-11-10 23:10:33 +01:00
/ / Responsive behavior
window . addEventListener ( ' resize ' , ( ) = > { {
2025-11-10 19:39:46 +01:00
if ( window . innerWidth < = 768 ) { {
2025-11-10 23:10:33 +01:00
this . element . style . display = ' none ' ;
} } else { {
this . element . style . display = ' ' ;
2025-11-10 19:39:46 +01:00
} }
2025-11-10 23:10:33 +01:00
} } ) ;
2025-11-10 19:39:46 +01:00
2025-11-10 23:10:33 +01:00
document . body . appendChild ( this . element ) ;
2025-11-10 19:39:46 +01:00
2025-11-10 23:10:33 +01:00
/ / Hide on mobile
if ( window . innerWidth < = 768 ) { {
this . element . style . display = ' none ' ;
} }
2025-11-10 20:10:11 +01:00
2025-11-10 23:10:33 +01:00
console . log ( ` 🎛 ️ $ { { this . config . title } } control created ` ) ;
} } ,
styleHeader : function ( ) { {
const header = this . element . querySelector ( ' .control-header ' ) ;
/ / Style the header to show icon , title , and close button in one line
/ / Match the height of the collapsed icon state ( 40 px )
header . style . cssText = `
display : flex ;
align - items : center ;
justify - content : space - between ;
height : 40 px ;
padding : 0 1 rem ;
border - bottom : 1 px solid #eee;
margin - bottom : 0 ;
` ;
2025-11-10 20:10:11 +01:00
2025-11-10 23:10:33 +01:00
const icon = header . querySelector ( ' .control-icon ' ) ;
if ( icon ) { {
icon . style . cssText = `
font - size : 16 px ;
color : #666;
margin - right : 0.5 rem ;
cursor : grab ;
user - select : none ;
` ;
2025-11-10 20:10:11 +01:00
2025-11-10 23:10:33 +01:00
/ / Make icon draggable
this . setupDragHandlers ( icon ) ;
} }
2025-11-10 20:10:11 +01:00
2025-11-10 23:10:33 +01:00
const title = header . querySelector ( ' h3 ' ) ;
if ( title ) { {
title . style . cssText = `
margin : 0 ;
font - size : 0.9 rem ;
font - weight : 600 ;
flex - grow : 1 ;
line - height : 1 ;
` ;
} }
2025-11-10 20:10:11 +01:00
2025-11-10 23:10:33 +01:00
const closeBtn = header . querySelector ( ' .control-close ' ) ;
if ( closeBtn ) { {
closeBtn . style . cssText = `
background : none ;
border : none ;
font - size : 14 px ;
cursor : pointer ;
color : #666;
padding : 0 ;
width : 20 px ;
height : 20 px ;
display : flex ;
align - items : center ;
justify - content : center ;
` ;
} }
} } ,
2025-11-10 19:39:46 +01:00
2025-11-10 23:10:33 +01:00
styleContent : function ( ) { {
const content = this . element . querySelector ( ' .control-content ' ) ;
const expansion = this . getExpansionDirection ( ) ;
2025-11-10 19:39:46 +01:00
2025-11-10 23:10:33 +01:00
/ / Style the content area based on expansion direction
let contentStyles = `
padding : 0.5 rem ;
overflow - y : auto ;
` ;
2025-11-10 19:39:46 +01:00
2025-11-10 23:10:33 +01:00
if ( expansion . body == = ' up ' ) { {
/ / Body expands upward ( for bottom border positions )
contentStyles + = `
2025-11-10 20:10:11 +01:00
max - height : calc ( 80 vh - 40 px ) ;
` ;
2025-11-10 23:10:33 +01:00
content . parentElement . style . flexDirection = ' column-reverse ' ;
} } else { {
/ / Body expands downward ( default )
contentStyles + = `
max - height : calc ( 80 vh - 40 px ) ;
` ;
content . parentElement . style . flexDirection = ' column ' ;
} }
2025-11-10 20:10:11 +01:00
2025-11-10 23:10:33 +01:00
content . style . cssText = contentStyles ;
} } ,
2025-11-10 20:10:11 +01:00
2025-11-10 23:10:33 +01:00
expand : function ( ) { {
this . isExpanded = true ;
const panel = this . element . querySelector ( ' .control-panel ' ) ;
const toggleBtn = this . element . querySelector ( ' .control-toggle ' ) ;
2025-11-10 20:10:11 +01:00
2025-11-10 23:10:33 +01:00
/ / Get expansion direction based on compass position
const expansion = this . getExpansionDirection ( ) ;
2025-11-10 20:10:11 +01:00
2025-11-10 23:10:33 +01:00
/ / Apply expansion styling based on direction
if ( expansion . header == = ' left ' ) { {
/ / Header expands to the left ( for right border positions )
this . element . style . width = ' 280px ' ;
this . element . style . transformOrigin = ' top right ' ;
} } else { {
/ / Header expands to the right ( default )
this . element . style . width = ' 280px ' ;
this . element . style . transformOrigin = ' top left ' ;
} }
2025-11-10 20:10:11 +01:00
2025-11-10 23:10:33 +01:00
panel . style . display = ' block ' ;
toggleBtn . style . display = ' none ' ;
this . styleHeader ( ) ;
this . styleContent ( ) ;
} } ,
collapse : function ( ) { {
this . isExpanded = false ;
const panel = this . element . querySelector ( ' .control-panel ' ) ;
const toggleBtn = this . element . querySelector ( ' .control-toggle ' ) ;
panel . style . display = ' none ' ;
this . element . style . width = ' 40px ' ;
toggleBtn . style . display = ' block ' ;
/ / Reset position to original compass location
this . element . style . top = this . originalPosition . top ;
this . element . style . right = this . originalPosition . right ;
this . element . style . bottom = this . originalPosition . bottom ;
this . element . style . left = this . originalPosition . left ;
this . element . style . transform = this . originalPosition . transform ;
} } ,
setupDragHandlers : function ( dragElement ) { {
dragElement . addEventListener ( ' mousedown ' , ( e ) = > { {
this . isDragging = true ;
const rect = this . element . getBoundingClientRect ( ) ;
const iconRect = dragElement . getBoundingClientRect ( ) ;
/ / Calculate offset relative to the icon position , not the element
this . dragOffset . x = e . clientX - rect . left ;
this . dragOffset . y = iconRect . top - rect . top + ( iconRect . height / 2 ) ; / / Keep mouse at icon center
dragElement . style . cursor = ' grabbing ' ;
e . preventDefault ( ) ;
} } ) ;
document . addEventListener ( ' mousemove ' , ( e ) = > { {
if ( ! this . isDragging | | ! this . isExpanded ) return ;
const newX = e . clientX - this . dragOffset . x ;
const newY = e . clientY - this . dragOffset . y ;
/ / Keep within viewport bounds
const maxX = window . innerWidth - this . element . offsetWidth ;
const maxY = window . innerHeight - this . element . offsetHeight ;
const boundedX = Math . max ( 0 , Math . min ( newX , maxX ) ) ;
const boundedY = Math . max ( 0 , Math . min ( newY , maxY ) ) ;
this . element . style . left = boundedX + ' px ' ;
this . element . style . top = boundedY + ' px ' ;
} } ) ;
document . addEventListener ( ' mouseup ' , ( ) = > { {
if ( this . isDragging ) { {
this . isDragging = false ;
dragElement . style . cursor = ' grab ' ;
} }
} } ) ;
} }
} } ;
2025-11-10 20:10:11 +01:00
2025-11-10 23:10:33 +01:00
/ / Step 5 : Initialize ContentsControl ( new implementation based on Control class )
try { {
const contentsControl = Object . create ( Control ) ;
/ / Configure for contents navigation
contentsControl . config = { {
icon : ' ☰ ' ,
title : ' Contents ' ,
className : ' contents-control ' ,
defaultContent : ' No headings found ' ,
ariaLabel : ' Document Navigation ' ,
position : ' wnw ' / / West - north - west positioning
} } ;
2025-11-10 20:10:11 +01:00
2025-11-10 23:10:33 +01:00
/ / Override buildContent method for navigation functionality
contentsControl . buildContent = function ( ) { {
const content = this . element . querySelector ( ' .control-content ' ) ;
/ / Build navigation content from current DOM
const allHeadings = document . querySelectorAll ( ' h1, h2, h3 ' ) ;
/ / Filter out headings that contain " Contents " or similar navigation - related text
const headings = Array . from ( allHeadings ) . filter ( heading = > { {
const text = heading . textContent . trim ( ) . toLowerCase ( ) ;
return ! text . includes ( ' contents ' ) & & ! text . includes ( ' table of contents ' ) & & ! text . includes ( ' navigation ' ) ;
} } ) ;
console . log ( " 📋 Found headings for navigation: " , headings . length ) ;
if ( headings . length == = 0 ) { {
content . innerHTML = ' <p style= " padding: 1rem; color: #666; " >No headings found</p> ' ;
} } else { {
let navHtml = ' ' ;
headings . forEach ( ( heading , index ) = > { {
if ( ! heading . id ) { {
heading . id = ` heading - $ { { index + 1 } } ` ;
2025-11-10 20:10:11 +01:00
} }
2025-11-10 23:10:33 +01:00
const level = parseInt ( heading . tagName . substring ( 1 ) ) ;
const indent = ( level - 1 ) * 1 ;
navHtml + = `
< a href = " #$ {{ heading.id}} "
style = " display: block; padding: 0.5rem; margin-left: $ {{ indent}}rem;
text - decoration : none ; color : #333; font-size: 0.9rem;
border - radius : 4 px ; cursor : pointer ; "
onmouseover = " this.style.backgroundColor= ' #f5f5f5 ' "
onmouseout = " this.style.backgroundColor= ' ' "
onclick = " event.preventDefault(); document.getElementById( ' $ {{ heading.id}} ' ).scrollIntoView( {{ behavior: ' smooth ' }}); if (window.innerWidth <= 768) setTimeout(() => contentsControl.collapse(), 500); " >
$ { { heading . textContent . trim ( ) } }
< / a >
` ;
2025-11-10 20:10:11 +01:00
} } ) ;
2025-11-10 23:10:33 +01:00
content . innerHTML = navHtml ;
2025-11-10 19:39:46 +01:00
} }
2025-11-10 23:10:33 +01:00
/ / Show panel
this . expand ( ) ;
2025-11-10 19:39:46 +01:00
} } ;
2025-11-10 23:10:33 +01:00
/ / Initialize the ContentsControl
contentsControl . createControl ( ) ;
2025-11-10 19:39:46 +01:00
/ / Make globally available for mobile collapse
2025-11-10 23:10:33 +01:00
window . contentsControl = contentsControl ;
2025-11-10 19:39:46 +01:00
} } catch ( error ) { {
2025-11-10 23:10:33 +01:00
console . error ( " ContentsControl failed to initialize: " , error ) ;
2025-11-10 19:39:46 +01:00
} }
2025-11-10 23:10:33 +01:00
2025-10-26 07:51:43 +01:00
} } ) ;
/ / Handle CDN loading errors
window . addEventListener ( ' load ' , function ( ) { {
if ( window . markitectMarkedError ) { {
console . error ( " CDN library failed to load - network or firewall blocking marked.js " ) ;
} }
} } ) ;
< / script >
< / body >
< / html > """
return html_template
def _get_clean_editor_scripts ( self ) - > str :
2025-11-04 09:11:50 +01:00
""" Load the modular editor JavaScript components from external files. """
from pathlib import Path
# Define the modular components to load in order
components = [
' js/core/section-manager.js ' ,
' js/components/debug-panel.js ' ,
' js/components/document-controls.js ' ,
' js/components/dom-renderer.js '
]
base_path = Path ( __file__ ) . parent / ' static '
combined_script = [ ]
# Load each component
for component_path in components :
script_path = base_path / component_path
if script_path . exists ( ) :
try :
content = script_path . read_text ( encoding = ' utf-8 ' )
combined_script . append ( f " // === { component_path } === " )
combined_script . append ( content )
combined_script . append ( " " ) # Add blank line between components
except Exception as e :
print ( f " Warning: Could not read { component_path } : { e } " )
combined_script . append ( f " console.error( ' Failed to load { component_path } ' ); " )
else :
print ( f " Warning: { component_path } not found at { script_path } " )
combined_script . append ( f " console.error( ' { component_path } file not found ' ); " )
# Add initialization script to wire up the components
initialization_script = """
/ / == = Component Initialization == =
document . addEventListener ( ' DOMContentLoaded ' , function ( ) {
/ / Create container for the markdown content
const container = document . getElementById ( ' markdown-content ' ) | | document . body ;
/ / Initialize components
const sectionManager = new SectionManager ( ) ;
const domRenderer = new DOMRenderer ( sectionManager , container ) ;
const debugPanel = new DebugPanel ( ) ;
const documentControls = new DocumentControls ( ) ;
/ / Create document controls
documentControls . create ( ) ;
2025-11-10 23:10:33 +01:00
/ / Define abstract Control class for UI controls ( same as viewing mode )
const Control = {
/ / Abstract control properties
element : null ,
2025-11-10 19:39:46 +01:00
isExpanded : false ,
2025-11-10 20:10:11 +01:00
isDragging : false ,
dragOffset : { x : 0 , y : 0 } ,
originalPosition : { top : ' 80px ' , left : ' 20px ' } ,
2025-11-10 19:39:46 +01:00
2025-11-10 23:10:33 +01:00
/ / Configuration properties ( to be overridden by subclasses )
config : {
icon : ' ? ' ,
title : ' Control ' ,
className : ' control ' ,
defaultContent : ' Template only ' ,
ariaLabel : ' Control ' ,
position : ' w ' / / Default compass position : west ( middle - left )
} ,
/ / Compass positioning system ( top - aligned for proper expansion )
compassPositions : {
/ / North positions ( top )
' n ' : { top : ' 20px ' , left : ' 50 % ' , transform : ' translateX(-50 % ) ' } ,
' nne ' : { top : ' 20px ' , left : ' 65 % ' , transform : ' translateX(-50 % ) ' } ,
' ne ' : { top : ' 20px ' , right : ' 20px ' } ,
' ene ' : { top : ' 80px ' , right : ' 20px ' } , / / Top - aligned instead of center
/ / East positions ( right )
' e ' : { top : ' 50vh ' , right : ' 20px ' , transform : ' translateY(-20px) ' } , / / Anchor at icon level
' ese ' : { top : ' calc(65vh - 20px) ' , right : ' 20px ' } , / / Top - aligned
' se ' : { bottom : ' 20px ' , right : ' 20px ' } ,
' sse ' : { bottom : ' 20px ' , right : ' 35 % ' , transform : ' translateX(50 % ) ' } ,
/ / South positions ( bottom )
' s ' : { bottom : ' 20px ' , left : ' 50 % ' , transform : ' translateX(-50 % ) ' } ,
' ssw ' : { bottom : ' 20px ' , left : ' 35 % ' , transform : ' translateX(-50 % ) ' } ,
' sw ' : { bottom : ' 20px ' , left : ' 20px ' } ,
' wsw ' : { bottom : ' 80px ' , left : ' 20px ' } , / / Top - aligned instead of center
/ / West positions ( left ) - top - aligned for proper expansion
' w ' : { top : ' 50vh ' , left : ' 20px ' , transform : ' translateY(-20px) ' } , / / Anchor at icon level
' wnw ' : { top : ' 80px ' , left : ' 20px ' } , / / Top - aligned instead of center
' nw ' : { top : ' 20px ' , left : ' 20px ' } ,
' nnw ' : { top : ' 20px ' , left : ' 35 % ' , transform : ' translateX(-50 % ) ' }
} ,
/ / Get expansion direction based on compass position
getExpansionDirection : function ( ) {
const pos = this . config . position ;
const rightBorderPositions = [ ' ne ' , ' ene ' , ' e ' , ' ese ' , ' se ' ] ;
const bottomBorderPositions = [ ' sw ' , ' ssw ' , ' s ' , ' sse ' , ' se ' ] ;
return {
header : rightBorderPositions . includes ( pos ) ? ' left ' : ' right ' ,
body : bottomBorderPositions . includes ( pos ) ? ' up ' : ' down '
} ;
} ,
/ / Calculate position styles based on compass direction
getPositionStyles : function ( ) {
const compassPos = this . compassPositions [ this . config . position ] | | this . compassPositions [ ' w ' ] ;
return {
position : ' fixed ' ,
top : compassPos . top | | ' auto ' ,
right : compassPos . right | | ' auto ' ,
bottom : compassPos . bottom | | ' auto ' ,
left : compassPos . left | | ' auto ' ,
transform : compassPos . transform | | ' none ' ,
zIndex : 1001
} ;
} ,
/ / Abstract methods ( to be implemented by subclasses )
buildContent : function ( ) {
const content = this . element . querySelector ( ' .control-content ' ) ;
content . innerHTML = ` < p style = " padding: 1rem; color: #666; " > $ { this . config . defaultContent } < / p > ` ;
} ,
/ / Concrete methods ( shared by all controls )
2025-11-10 19:39:46 +01:00
createControl : function ( ) {
2025-11-10 23:10:33 +01:00
console . log ( ` 🎛 ️ Creating $ { this . config . title } control . . . ` ) ;
this . element = document . createElement ( ' div ' ) ;
this . element . className = this . config . className ;
this . element . innerHTML = `
< button class = " control-toggle " aria - label = " $ {this.config.ariaLabel} " > $ { this . config . icon } < / button >
< div class = " control-panel " style = " display: none; " >
< div class = " control-header " >
< span class = " control-icon " > $ { this . config . icon } < / span >
< h3 > $ { this . config . title } < / h3 >
< button class = " control-close " > ✕ < / button >
2025-11-10 19:39:46 +01:00
< / div >
2025-11-10 23:10:33 +01:00
< div class = " control-content " > Loading . . . < / div >
2025-11-10 19:39:46 +01:00
< / div >
` ;
2025-11-10 23:10:33 +01:00
/ / Position using compass direction
const positionStyles = this . getPositionStyles ( ) ;
this . element . style . cssText = `
position : $ { positionStyles . position } ;
top : $ { positionStyles . top } ;
right : $ { positionStyles . right } ;
bottom : $ { positionStyles . bottom } ;
left : $ { positionStyles . left } ;
transform : $ { positionStyles . transform } ;
z - index : $ { positionStyles . zIndex } ;
2025-11-10 19:39:46 +01:00
background : rgba ( 255 , 255 , 255 , 0.95 ) ;
border : 1 px solid #e1e5e9;
border - radius : 8 px ;
box - shadow : 0 2 px 8 px rgba ( 0 , 0 , 0 , 0.15 ) ;
backdrop - filter : blur ( 8 px ) ;
width : 40 px ;
2025-11-10 23:10:33 +01:00
transition : all 0.3 s ease ;
2025-11-10 19:39:46 +01:00
font - family : - apple - system , BlinkMacSystemFont , ' Segoe UI ' , sans - serif ;
` ;
2025-11-10 23:10:33 +01:00
/ / Store original position for reset
this . originalPosition = {
top : positionStyles . top ,
right : positionStyles . right ,
bottom : positionStyles . bottom ,
left : positionStyles . left ,
transform : positionStyles . transform
} ;
2025-11-10 19:39:46 +01:00
/ / Style toggle button
2025-11-10 23:10:33 +01:00
const toggleBtn = this . element . querySelector ( ' .control-toggle ' ) ;
2025-11-10 19:39:46 +01:00
toggleBtn . style . cssText = `
width : 100 % ;
height : 40 px ;
border : none ;
background : transparent ;
cursor : pointer ;
font - size : 16 px ;
color : #666;
transition : color 0.2 s ease ;
` ;
2025-11-10 23:10:33 +01:00
/ / Handle click to build content on - demand
2025-11-10 19:39:46 +01:00
toggleBtn . addEventListener ( ' click ' , ( ) = > {
2025-11-10 20:10:11 +01:00
if ( this . isExpanded ) {
this . collapse ( ) ;
} else {
2025-11-10 23:10:33 +01:00
console . log ( ` 🎛 ️ $ { this . config . title } toggle clicked - building content . . . ` ) ;
this . buildContent ( ) ;
2025-11-10 20:10:11 +01:00
}
2025-11-10 19:39:46 +01:00
} ) ;
/ / Close button handler
2025-11-10 23:10:33 +01:00
const closeBtn = this . element . querySelector ( ' .control-close ' ) ;
2025-11-10 19:39:46 +01:00
closeBtn . addEventListener ( ' click ' , ( ) = > {
this . collapse ( ) ;
} ) ;
2025-11-10 23:10:33 +01:00
document . body . appendChild ( this . element ) ;
console . log ( ` 🎛 ️ $ { this . config . title } control created ` ) ;
2025-11-10 19:39:46 +01:00
} ,
2025-11-10 23:10:33 +01:00
styleHeader : function ( ) {
const header = this . element . querySelector ( ' .control-header ' ) ;
2025-11-10 20:10:11 +01:00
/ / Style the header to show icon , title , and close button in one line
/ / Match the height of the collapsed icon state ( 40 px )
header . style . cssText = `
display : flex ;
align - items : center ;
justify - content : space - between ;
height : 40 px ;
padding : 0 1 rem ;
border - bottom : 1 px solid #eee;
margin - bottom : 0 ;
` ;
2025-11-10 23:10:33 +01:00
const icon = header . querySelector ( ' .control-icon ' ) ;
2025-11-10 20:10:11 +01:00
if ( icon ) {
icon . style . cssText = `
font - size : 16 px ;
color : #666;
margin - right : 0.5 rem ;
cursor : grab ;
user - select : none ;
` ;
/ / Make icon draggable
this . setupDragHandlers ( icon ) ;
}
const title = header . querySelector ( ' h3 ' ) ;
if ( title ) {
title . style . cssText = `
margin : 0 ;
font - size : 0.9 rem ;
font - weight : 600 ;
flex - grow : 1 ;
line - height : 1 ;
` ;
}
2025-11-10 23:10:33 +01:00
const closeBtn = header . querySelector ( ' .control-close ' ) ;
2025-11-10 20:10:11 +01:00
if ( closeBtn ) {
closeBtn . style . cssText = `
background : none ;
border : none ;
font - size : 14 px ;
cursor : pointer ;
color : #666;
padding : 0 ;
width : 20 px ;
height : 20 px ;
display : flex ;
align - items : center ;
justify - content : center ;
` ;
}
2025-11-10 23:10:33 +01:00
} ,
2025-11-10 19:39:46 +01:00
2025-11-10 23:10:33 +01:00
styleContent : function ( ) {
const content = this . element . querySelector ( ' .control-content ' ) ;
const expansion = this . getExpansionDirection ( ) ;
2025-11-10 19:39:46 +01:00
2025-11-10 23:10:33 +01:00
/ / Style the content area based on expansion direction
let contentStyles = `
2025-11-10 20:10:11 +01:00
padding : 0.5 rem ;
overflow - y : auto ;
` ;
2025-11-10 23:10:33 +01:00
if ( expansion . body == = ' up ' ) {
/ / Body expands upward ( for bottom border positions )
contentStyles + = `
max - height : calc ( 80 vh - 40 px ) ;
` ;
content . parentElement . style . flexDirection = ' column-reverse ' ;
} else {
/ / Body expands downward ( default )
contentStyles + = `
max - height : calc ( 80 vh - 40 px ) ;
` ;
content . parentElement . style . flexDirection = ' column ' ;
}
content . style . cssText = contentStyles ;
2025-11-10 19:39:46 +01:00
} ,
expand : function ( ) {
this . isExpanded = true ;
2025-11-10 23:10:33 +01:00
const panel = this . element . querySelector ( ' .control-panel ' ) ;
const toggleBtn = this . element . querySelector ( ' .control-toggle ' ) ;
/ / Get expansion direction based on compass position
const expansion = this . getExpansionDirection ( ) ;
/ / Apply expansion styling based on direction
if ( expansion . header == = ' left ' ) {
/ / Header expands to the left ( for right border positions )
this . element . style . width = ' 300px ' ;
this . element . style . transformOrigin = ' top right ' ;
} else {
/ / Header expands to the right ( default )
this . element . style . width = ' 300px ' ;
this . element . style . transformOrigin = ' top left ' ;
}
2025-11-10 19:39:46 +01:00
panel . style . display = ' block ' ;
2025-11-10 20:10:11 +01:00
toggleBtn . style . display = ' none ' ;
2025-11-10 23:10:33 +01:00
this . styleHeader ( ) ;
this . styleContent ( ) ;
2025-11-10 19:39:46 +01:00
} ,
collapse : function ( ) {
this . isExpanded = false ;
2025-11-10 23:10:33 +01:00
const panel = this . element . querySelector ( ' .control-panel ' ) ;
const toggleBtn = this . element . querySelector ( ' .control-toggle ' ) ;
2025-11-10 19:39:46 +01:00
panel . style . display = ' none ' ;
2025-11-10 23:10:33 +01:00
this . element . style . width = ' 40px ' ;
2025-11-10 20:10:11 +01:00
toggleBtn . style . display = ' block ' ;
2025-11-10 23:10:33 +01:00
/ / Reset position to original compass location
this . element . style . top = this . originalPosition . top ;
this . element . style . right = this . originalPosition . right ;
this . element . style . bottom = this . originalPosition . bottom ;
this . element . style . left = this . originalPosition . left ;
this . element . style . transform = this . originalPosition . transform ;
2025-11-10 20:10:11 +01:00
} ,
setupDragHandlers : function ( dragElement ) {
dragElement . addEventListener ( ' mousedown ' , ( e ) = > {
this . isDragging = true ;
2025-11-10 23:10:33 +01:00
const rect = this . element . getBoundingClientRect ( ) ;
const iconRect = dragElement . getBoundingClientRect ( ) ;
/ / Calculate offset relative to the icon position , not the element
2025-11-10 20:10:11 +01:00
this . dragOffset . x = e . clientX - rect . left ;
2025-11-10 23:10:33 +01:00
this . dragOffset . y = iconRect . top - rect . top + ( iconRect . height / 2 ) ; / / Keep mouse at icon center
2025-11-10 20:10:11 +01:00
dragElement . style . cursor = ' grabbing ' ;
e . preventDefault ( ) ;
} ) ;
document . addEventListener ( ' mousemove ' , ( e ) = > {
if ( ! this . isDragging | | ! this . isExpanded ) return ;
const newX = e . clientX - this . dragOffset . x ;
const newY = e . clientY - this . dragOffset . y ;
/ / Keep within viewport bounds
2025-11-10 23:10:33 +01:00
const maxX = window . innerWidth - this . element . offsetWidth ;
const maxY = window . innerHeight - this . element . offsetHeight ;
2025-11-10 20:10:11 +01:00
const boundedX = Math . max ( 0 , Math . min ( newX , maxX ) ) ;
const boundedY = Math . max ( 0 , Math . min ( newY , maxY ) ) ;
2025-11-10 23:10:33 +01:00
this . element . style . left = boundedX + ' px ' ;
this . element . style . top = boundedY + ' px ' ;
2025-11-10 20:10:11 +01:00
} ) ;
document . addEventListener ( ' mouseup ' , ( ) = > {
if ( this . isDragging ) {
this . isDragging = false ;
dragElement . style . cursor = ' grab ' ;
}
} ) ;
2025-11-10 19:39:46 +01:00
}
} ;
2025-11-10 23:10:33 +01:00
/ / Create ContentsControl for edit mode ( new implementation based on Control class )
try {
const contentsControl = Object . create ( Control ) ;
/ / Configure for contents navigation in edit mode
contentsControl . config = {
icon : ' ☰ ' ,
title : ' Contents ' ,
className : ' contents-control edit-mode ' ,
defaultContent : ' No headings found ' ,
ariaLabel : ' Document Navigation ' ,
position : ' wnw ' / / West - north - west positioning
} ;
/ / Override buildContent method for navigation functionality
contentsControl . buildContent = function ( ) {
const content = this . element . querySelector ( ' .control-content ' ) ;
/ / Build navigation content from current DOM
const allHeadings = document . querySelectorAll ( ' h1, h2, h3 ' ) ;
/ / Filter out headings that contain " Contents " or similar navigation - related text
const headings = Array . from ( allHeadings ) . filter ( heading = > {
const text = heading . textContent . trim ( ) . toLowerCase ( ) ;
return ! text . includes ( ' contents ' ) & & ! text . includes ( ' table of contents ' ) & & ! text . includes ( ' navigation ' ) ;
} ) ;
console . log ( " 📋 Found headings for navigation: " , headings . length ) ;
if ( headings . length == = 0 ) {
content . innerHTML = ' <p style= " padding: 1rem; color: #666; " >No headings found</p> ' ;
} else {
let navHtml = ' ' ;
headings . forEach ( ( heading , index ) = > {
if ( ! heading . id ) {
heading . id = ` heading - $ { index + 1 } ` ;
}
const level = parseInt ( heading . tagName . substring ( 1 ) ) ;
const indent = ( level - 1 ) * 1 ;
navHtml + = `
< a href = " #$ {heading.id} "
style = " display: block; padding: 0.5rem; margin-left: $ {indent} rem;
text - decoration : none ; color : #333; font-size: 0.9rem;
border - radius : 4 px ; cursor : pointer ; "
onmouseover = " this.style.backgroundColor= ' #f5f5f5 ' "
onmouseout = " this.style.backgroundColor= ' ' "
onclick = " event.preventDefault(); document.getElementById( ' $ {heading.id} ' ).scrollIntoView( { behavior: ' smooth ' }); if (window.innerWidth <= 768) setTimeout(() => contentsControl.collapse(), 500); " >
$ { heading . textContent . trim ( ) }
< / a >
` ;
} ) ;
content . innerHTML = navHtml ;
}
/ / Show panel
this . expand ( ) ;
} ;
/ / Initialize the ContentsControl
contentsControl . createControl ( ) ;
/ / Make globally available for mobile collapse
window . contentsControl = contentsControl ;
} catch ( error ) {
console . error ( " ContentsControl failed to initialize: " , error ) ;
}
2025-11-10 19:39:46 +01:00
2025-11-04 09:11:50 +01:00
/ / Wire up event handlers
documentControls . setEventHandlers ( {
' save-document ' : ( ) = > {
console . log ( ' Save document clicked ' ) ;
2025-11-04 21:59:22 +01:00
try {
/ / Get current markdown content from section manager
const currentMarkdown = sectionManager . getDocumentMarkdown ( ) ;
/ / Create filename with timestamp suffix following the established convention
const now = new Date ( ) ;
const timestamp = now . toISOString ( ) . slice ( 0 , 19 ) . replace ( / : / g , ' - ' ) . replace ( ' T ' , ' - ' ) ;
/ / Extract original filename from config or use default
const originalFilename = window . editorConfig ? . originalFilename | | ' document ' ;
const editedFilename = ` $ { originalFilename } - edited - $ { timestamp } . md ` ;
/ / Create and download the file
const blob = new Blob ( [ currentMarkdown ] , { type : ' text/markdown ' } ) ;
const url = URL . createObjectURL ( blob ) ;
const a = document . createElement ( ' a ' ) ;
a . href = url ;
a . download = editedFilename ;
document . body . appendChild ( a ) ;
a . click ( ) ;
document . body . removeChild ( a ) ;
URL . revokeObjectURL ( url ) ;
/ / Log success to debug panel
debugPanel . addMessage ( ` Document saved as : $ { editedFilename } ` , ' SUCCESS ' ) ;
console . log ( ` Document successfully saved as : $ { editedFilename } ` ) ;
} catch ( error ) {
debugPanel . addMessage ( ` Save failed : $ { error . message } ` , ' ERROR ' ) ;
console . error ( ' Save error: ' , error ) ;
}
2025-11-04 09:11:50 +01:00
} ,
' reset-all ' : ( ) = > {
console . log ( ' Reset all clicked ' ) ;
2025-11-04 09:40:01 +01:00
/ / Hide any open editors
domRenderer . hideCurrentEditor ( ) ;
/ / Reset all sections to original state
const allSections = Array . from ( sectionManager . sections . values ( ) ) ;
allSections . forEach ( section = > {
section . resetToOriginal ( ) ;
} ) ;
/ / Re - render all sections
domRenderer . renderAllSections ( allSections ) ;
debugPanel . addMessage ( ` Reset all sections to original state ` , ' INFO ' ) ;
2025-11-04 09:11:50 +01:00
} ,
' show-status ' : ( ) = > {
const status = sectionManager . getDocumentStatus ( ) ;
alert ( ` Document Status : \\nTotal Sections : $ { status . totalSections } \\nEditing Sections : $ { status . editingSections } ` ) ;
} ,
' toggle-debug ' : ( ) = > {
debugPanel . toggle ( ) ;
2025-10-28 22:36:15 +01:00
}
2025-11-04 09:11:50 +01:00
} ) ;
2025-10-28 22:36:15 +01:00
2025-11-04 09:11:50 +01:00
/ / Set up debug panel integration
sectionManager . on ( ' sections-created ' , ( data ) = > {
debugPanel . addMessage ( ` Created $ { data . count } sections ` , ' INFO ' ) ;
} ) ;
2025-10-28 22:36:15 +01:00
2025-11-04 09:11:50 +01:00
sectionManager . on ( ' edit-started ' , ( data ) = > {
debugPanel . addMessage ( ` Edit started for section : $ { data . sectionId } ` , ' DEBUG ' ) ;
} ) ;
2025-10-28 22:36:15 +01:00
2025-11-04 09:11:50 +01:00
sectionManager . on ( ' changes-accepted ' , ( data ) = > {
debugPanel . addMessage ( ` Changes accepted for section : $ { data . sectionId } ` , ' SUCCESS ' ) ;
2025-11-04 09:40:01 +01:00
/ / Re - render the section to show updated content
const section = sectionManager . sections . get ( data . sectionId ) ;
if ( section ) {
const sectionElement = domRenderer . findSectionElement ( data . sectionId ) ;
if ( sectionElement ) {
const newElement = domRenderer . renderSection ( section ) ;
sectionElement . parentNode . replaceChild ( newElement , sectionElement ) ;
debugPanel . addMessage ( ` DOM updated for section : $ { data . sectionId } ` , ' INFO ' ) ;
}
}
2025-10-28 22:36:15 +01:00
} ) ;
2025-11-04 09:11:50 +01:00
sectionManager . on ( ' changes-cancelled ' , ( data ) = > {
debugPanel . addMessage ( ` Changes cancelled for section : $ { data . sectionId } ` , ' WARNING ' ) ;
2025-10-28 22:36:15 +01:00
} ) ;
2025-11-04 09:25:15 +01:00
/ / Initialize with markdown content
const markdownToRender = markdownContent | | ' ' ;
if ( markdownToRender . trim ( ) ) {
const sections = sectionManager . createSectionsFromMarkdown ( markdownToRender ) ;
2025-11-04 09:11:50 +01:00
domRenderer . renderAllSections ( sections ) ;
2025-11-04 09:25:15 +01:00
debugPanel . addMessage ( ` Initialized with $ { sections . length } sections ` , ' INFO ' ) ;
} else {
debugPanel . addMessage ( ' No markdown content to initialize ' , ' WARNING ' ) ;
2025-11-04 09:11:50 +01:00
}
2025-10-28 22:36:15 +01:00
2025-11-04 09:11:50 +01:00
/ / Make components globally available for debugging
window . markitectComponents = {
sectionManager ,
domRenderer ,
debugPanel ,
documentControls
} ;
2025-10-28 22:36:15 +01:00
2025-11-04 09:11:50 +01:00
console . log ( ' Markitect modular editor initialized successfully ' ) ;
} ) ;
"""
2025-10-28 22:36:15 +01:00
2025-11-04 09:11:50 +01:00
combined_script . append ( initialization_script )
return ' \n ' . join ( combined_script )