## Major Changes - Moved all testdrive-jsui assets from root to capabilities/testdrive-jsui/ - Consolidated directory structure: js/, static/css/, static/images/, static/templates/ - Implemented plugin self-declaration (get_plugin_source_dir, get_asset_paths) - Removed hardcoded plugin discovery from rendering.py - Updated all asset paths to be relative to capability root ## Architecture Improvements - Single source of truth for all testdrive-jsui assets - Plugin declares its own location (no hardcoded paths) - Generic plugin discovery using hasattr check - Clean separation: all JS in .js files, no code mixing - Standalone capability ready for independent use ## Files Changed - markitect/plugins/testdrive_jsui.py: Added self-declaration methods - markitect/plugins/rendering.py: Removed hardcoded discovery - capabilities/testdrive-jsui/README.md: Added standalone usage documentation - Moved 17 asset files to consolidated structure - Deleted obsolete /testdrive-jsui/ root directory ## Testing - All 17 assets verified and working - Tested via CLI: markitect md-render --engine testdrive-jsui - Full document rendering successful Prepares testdrive-jsui to become a git submodule with proper dependency management. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
242 lines
8.4 KiB
Python
242 lines
8.4 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_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'<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}") |