""" TestDrive JSUI Rendering Engine Plugin Independent JavaScript UI rendering engine for Markitect edit mode. Designed for standalone development and testing of JavaScript components. """ from pathlib import Path from typing import Dict, List, Optional import json from .base import PluginMetadata, PluginType from .rendering import RenderingEnginePlugin, RenderingConfig class TestDriveJSUIEngine(RenderingEnginePlugin): """TestDrive JavaScript UI rendering engine.""" def __init__(self): super().__init__() self._metadata = PluginMetadata( name="testdrive-jsui", version="1.0.0", description="Independent JavaScript UI engine for markdown editing", author="Markitect Team", plugin_type=PluginType.RENDERING ) @property def metadata(self) -> PluginMetadata: """Return plugin metadata.""" return self._metadata def get_supported_modes(self) -> List[str]: """Support edit and view modes.""" return ["edit", "view"] def get_plugin_source_dir(self) -> Path: """ Return the source directory for this plugin. This allows the plugin to declare its own location. """ # Plugin is located in capabilities/testdrive-jsui/ return Path(__file__).parent.parent.parent / "capabilities" / "testdrive-jsui" def get_asset_paths(self) -> Dict[str, Path]: """ Return paths to asset directories relative to plugin source. This allows flexible asset organization within the plugin. """ base = self.get_plugin_source_dir() return { 'js': base / 'js', 'css': base / 'static' / 'css', 'images': base / 'static' / 'images', 'templates': base / 'static' / 'templates', } def get_required_assets(self) -> Dict[str, List[str]]: """ Define required JavaScript, CSS, and other assets. All paths are relative to the plugin source directory. """ return { "js": [ "js/core/debug-system.js", "js/core/section-manager.js", "js/components/debug-panel.js", "js/components/dom-renderer.js", "js/controls/control-base.js", "js/controls/contents-control.js", "js/controls/status-control.js", "js/controls/debug-control.js", "js/controls/edit-control.js", "js/config-loader.js", "js/main-updated.js" ], "css": [ "static/css/editor.css", "static/css/controls.css", "static/css/themes/github.css" ], "images": [ "static/images/icons/edit.png", "static/images/icons/save.png", "static/images/icons/reset.png" ], "external": [ "https://cdn.jsdelivr.net/npm/marked/marked.min.js" ] } def get_template_path(self) -> Optional[Path]: """Return path to the HTML template.""" # Template is in the plugin's static/templates directory template_path = self.get_asset_paths()['templates'] / "index.html" if template_path.exists(): return template_path raise FileNotFoundError( f"Template not found at {template_path}. " f"Ensure testdrive-jsui is properly installed in capabilities/" ) def render_document(self, content: str, mode: str, config: RenderingConfig) -> str: """ Render markdown content using TestDrive JSUI. Args: content: Markdown content to render mode: Rendering mode ('edit' or 'view') config: Rendering configuration Returns: Complete HTML document """ if not self.validate_mode(mode): raise ValueError(f"Mode '{mode}' not supported by TestDrive JSUI engine") # Get template template_path = self.get_template_path() if not template_path or not template_path.exists(): raise FileNotFoundError(f"Template not found: {template_path}") # Load template content with open(template_path, 'r', encoding='utf-8') as f: template_content = f.read() # Generate asset URLs assets = self.get_required_assets() js_scripts = [] css_links = [] # External dependencies for external_url in assets.get("external", []): js_scripts.append(f'') # Plugin assets for js_file in assets.get("js", []): url = config.get_asset_url(self.metadata.name, js_file) js_scripts.append(f'') for css_file in assets.get("css", []): url = config.get_asset_url(self.metadata.name, css_file) css_links.append(f'') # Generate configuration JSON for JavaScript js_config = { "markdownContent": content, "markdownContentWithDogtag": content, # Could add dogtag here "dogtagContent": "", "mode": mode, "theme": "github", "keyboardShortcuts": True, "autosave": False, "sections": True, "originalFilename": "document", "base64References": {}, "version": f"Markitect {self.metadata.version}", "repoName": "Markitect" } # Basic fallback content rendering (simple markdown to HTML) fallback_html = self._render_markdown_fallback(content) # Replace template placeholders using safe substitution html_content = template_content html_content = html_content.replace("{title}", "TestDrive JSUI Document") html_content = html_content.replace("{version}", f"Markitect {self.metadata.version}") html_content = html_content.replace("{mode_class}", f"markitect-{mode}-mode") html_content = html_content.replace("{css_content}", "\n".join(css_links)) html_content = html_content.replace("{js_scripts}", "\n".join(js_scripts)) html_content = html_content.replace("{config_json}", json.dumps(js_config, indent=2)) html_content = html_content.replace("{fallback_content}", fallback_html) return html_content def _render_markdown_fallback(self, content: str) -> str: """ Render basic markdown to HTML for fallback content. Args: content: Markdown content Returns: Basic HTML rendering """ import re # Very basic markdown to HTML conversion for fallback html = content # Headers html = re.sub(r'^# (.+)$', r'
', html)
html = re.sub(r'\n', '
', html)
# Wrap in paragraph tags
if html.strip() and not html.startswith('<'):
html = f'
{html}
' return html def get_development_config(self, source_dir: Path) -> RenderingConfig: """ Get development configuration for standalone testing. Args: source_dir: Path to testdrive-jsui source directory Returns: Development rendering configuration """ return RenderingConfig( asset_base_url=".", # Serve from current directory in dev development_mode=True, plugin_source_dirs={self.metadata.name: source_dir} ) def create_standalone_test_document(self, test_content: str, output_path: Path) -> None: """ Create a standalone HTML document for testing. Args: test_content: Markdown content to test with output_path: Where to write the test HTML file """ config = self.get_development_config(output_path.parent) html_content = self.render_document(test_content, "edit", config) output_path.write_text(html_content, encoding='utf-8') print(f"✅ Created standalone test document: {output_path}")