Files
markitect-main/markitect/plugins/testdrive_jsui.py
tegwick 8ef356af57 feat: implement plugin infrastructure for rendering engines
Added comprehensive plugin system for independent JavaScript UI development:

**Plugin Infrastructure:**
- Extended existing MarkiTect plugin system with RenderingEnginePlugin base class
- Added RENDERING plugin type to PluginType enum
- Created RenderingConfig for asset management and deployment
- Implemented RenderingEngineManager for plugin discovery and lifecycle

**TestDrive JSUI Plugin:**
- Extracted JavaScript UI components to independent testdrive-jsui plugin
- Created standalone development environment (no Python required)
- Implemented compass-positioned control panels (NW, NE, E, SE)
- Added clean JSON configuration interface for Python↔JavaScript data transfer

**Asset Management:**
- Development mode: serve assets directly from plugin source directory
- Production mode: deploy to _markitect/plugins/[plugin-name]/ structure
- Configurable asset URLs and deployment strategies
- Support for external dependencies (CDN resources)

**Standalone Development:**
- testdrive-jsui/test.html for browser-based development
- Package.json with npm scripts for development server
- Complete separation of JavaScript development from Python environment
- Hot reload and standard web development workflow

**Integration Demo:**
- demo_plugin_integration.py showcasing all plugin capabilities
- Standalone, plugin discovery, production deployment examples
- Asset URL generation for different deployment modes

This enables JavaScript-first development while maintaining clean integration
with the MarkiTect Python ecosystem. Developers can now work on UI components
independently using standard web development tools and workflows.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-14 06:49:41 +01:00

218 lines
7.7 KiB
Python

"""
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_required_assets(self) -> Dict[str, List[str]]:
"""Define required JavaScript, CSS, and other assets."""
return {
"js": [
"static/js/core/debug-system.js",
"static/js/core/section-manager.js",
"static/js/components/debug-panel.js",
"static/js/components/document-controls.js",
"static/js/components/dom-renderer.js",
"static/js/controls/control-base.js",
"static/js/controls/contents-control.js",
"static/js/controls/status-control.js",
"static/js/controls/debug-control.js",
"static/js/controls/edit-control.js",
"static/js/config-loader.js",
"static/js/main.js"
],
"css": [
"static/css/editor.css",
"static/css/controls.css",
"static/css/themes/github.css"
],
"images": [
"images/icons/edit.png",
"images/icons/save.png",
"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."""
# Look for template in plugin directory structure
plugin_dir = Path(__file__).parent.parent.parent / "testdrive-jsui"
template_path = plugin_dir / "templates" / "index.html"
if template_path.exists():
return template_path
# Fallback to current template location
return Path(__file__).parent.parent / "templates" / "edit-mode-fixed.html"
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'<script src="{external_url}"></script>')
# Plugin assets
for js_file in assets.get("js", []):
url = config.get_asset_url(self.metadata.name, js_file)
js_scripts.append(f'<script src="{url}"></script>')
for css_file in assets.get("css", []):
url = config.get_asset_url(self.metadata.name, css_file)
css_links.append(f'<link rel="stylesheet" href="{url}">')
# 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'<h1>\1</h1>', html, flags=re.MULTILINE)
html = re.sub(r'^## (.+)$', r'<h2>\1</h2>', html, flags=re.MULTILINE)
html = re.sub(r'^### (.+)$', r'<h3>\1</h3>', html, flags=re.MULTILINE)
# Paragraphs
html = re.sub(r'\n\n', '</p><p>', html)
html = re.sub(r'\n', '<br>', html)
# Wrap in paragraph tags
if html.strip() and not html.startswith('<'):
html = f'<p>{html}</p>'
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}")