', 'formatStatistics');
+ }
+
+ /**
+ * Refresh statistics and update display
+ */
+ refreshStats() {
+ return this.safeOperation(() => {
+ // Save current stats as previous
+ this.previousStats = { ...this.stats };
+
+ // Analyze document
+ this.analyzeDocument();
+
+ // Update display
+ this.buildContent();
+
+ // Show success feedback
+ const refreshBtn = this.element?.querySelector('button');
+ if (refreshBtn) {
+ const originalText = refreshBtn.innerHTML;
+ refreshBtn.innerHTML = 'β Updated';
+
+ setTimeout(() => {
+ refreshBtn.innerHTML = originalText;
+ }, 1000);
+ }
+
+ }, null, 'refreshStats');
+ }
+
+ /**
+ * Export statistics to various formats
+ */
+ exportStats() {
+ return this.safeOperation(() => {
+ const exportData = {
+ timestamp: new Date().toISOString(),
+ document: {
+ title: document.title || 'Untitled Document',
+ url: window.location.href
+ },
+ statistics: this.stats,
+ metadata: {
+ wordsPerMinute: this.wordsPerMinute,
+ analysisDate: new Date(this.lastUpdateTime).toISOString()
+ }
+ };
+
+ // Create downloadable JSON
+ const dataStr = JSON.stringify(exportData, null, 2);
+ const dataBlob = new Blob([dataStr], { type: 'application/json' });
+ const url = URL.createObjectURL(dataBlob);
+
+ // Create temporary download link
+ const link = document.createElement('a');
+ link.href = url;
+ link.download = `document-stats-${new Date().toISOString().split('T')[0]}.json`;
+ document.body.appendChild(link);
+ link.click();
+ document.body.removeChild(link);
+
+ // Clean up
+ URL.revokeObjectURL(url);
+
+ // Show feedback
+ const exportBtn = this.element?.querySelector('button:last-child');
+ if (exportBtn) {
+ const originalText = exportBtn.innerHTML;
+ exportBtn.innerHTML = 'β Exported';
+ exportBtn.style.background = '#28a745';
+
+ setTimeout(() => {
+ exportBtn.innerHTML = originalText;
+ exportBtn.style.background = '#28a745';
+ }, 2000);
+ }
+
+ }, null, 'exportStats');
+ }
+
+ /**
+ * Get reading difficulty score (Flesch Reading Ease approximation)
+ */
+ calculateReadabilityScore() {
+ return this.safeOperation(() => {
+ if (this.stats.sentences === 0 || this.stats.words === 0) {
+ return { score: 0, level: 'Unknown' };
+ }
+
+ const avgWordsPerSentence = this.stats.words / this.stats.sentences;
+ const avgSyllablesPerWord = 1.5; // Simplified approximation
+
+ // Flesch Reading Ease formula (simplified)
+ const score = 206.835 - (1.015 * avgWordsPerSentence) - (84.6 * avgSyllablesPerWord);
+
+ let level;
+ if (score >= 90) level = 'Very Easy';
+ else if (score >= 80) level = 'Easy';
+ else if (score >= 70) level = 'Fairly Easy';
+ else if (score >= 60) level = 'Standard';
+ else if (score >= 50) level = 'Fairly Difficult';
+ else if (score >= 30) level = 'Difficult';
+ else level = 'Very Difficult';
+
+ return { score: Math.round(score), level };
+ }, { score: 0, level: 'Unknown' }, 'calculateReadabilityScore');
+ }
+
+ /**
+ * Build the control content
+ * Override of base class method to provide status-specific functionality
+ */
+ buildContent() {
+ return this.safeOperation(() => {
+ // Analyze document first
+ this.analyzeDocument();
+
+ // Generate and set content
+ const content = this.element?.querySelector('.control-content');
+ if (content) {
+ content.innerHTML = this.formatStatistics();
+
+ // Store reference to this control for onclick handlers
+ this.element.statusControl = this;
+ }
+
+ // Set up auto-refresh for dynamic content
+ if (this.updateInterval) {
+ clearInterval(this.updateInterval);
+ }
+
+ this.updateInterval = setInterval(() => {
+ this.refreshStats();
+ }, 10000); // Update every 10 seconds
+
+ }, null, 'buildContent');
+ }
+
+ /**
+ * Clean up resources when control is destroyed
+ */
+ destroy() {
+ if (this.updateInterval) {
+ clearInterval(this.updateInterval);
+ this.updateInterval = null;
+ }
+ super.destroy();
+ }
+}
+
+// Export for module systems or attach to global for direct usage
+if (typeof module !== 'undefined' && module.exports) {
+ module.exports = StatusControl;
+} else {
+ window.StatusControl = StatusControl;
+}
\ No newline at end of file
diff --git a/capabilities/testdrive-jsui/js/tests/button-events.test.js b/capabilities/testdrive-jsui/js/tests/button-events.test.js
index 9d355438..bf6e45fc 100644
--- a/capabilities/testdrive-jsui/js/tests/button-events.test.js
+++ b/capabilities/testdrive-jsui/js/tests/button-events.test.js
@@ -33,10 +33,10 @@ describe('Button Functionality and DOM Events', () => {
mockSection = document.querySelector('.section');
- // Load components
- require('../components/document-controls.js');
- if (global.DocumentControls) {
- documentControls = new global.DocumentControls(document.getElementById('content'));
+ // Load components - using legacy component for backward compatibility
+ require('../components/document-controls-legacy.js');
+ if (global.DocumentControlsLegacy) {
+ documentControls = new global.DocumentControlsLegacy(document.getElementById('content'));
}
});
diff --git a/capabilities/testdrive-jsui/js/tests/test-documentcontrols-extraction.js b/capabilities/testdrive-jsui/js/tests/test-documentcontrols-extraction.js
index 2d5607ca..764313c3 100644
--- a/capabilities/testdrive-jsui/js/tests/test-documentcontrols-extraction.js
+++ b/capabilities/testdrive-jsui/js/tests/test-documentcontrols-extraction.js
@@ -37,14 +37,14 @@ runner.describe('DocumentControls Component Extraction', () => {
runner.it('should load extracted DocumentControls component', () => {
// Load the extracted component
- delete require.cache[require.resolve('../components/document-controls.js')];
+ delete require.cache[require.resolve('../components/document-controls-legacy.js')];
try {
- const module = require('../components/document-controls.js');
- runner.expect(module.DocumentControls).toBeTruthy();
+ const module = require('../components/document-controls-legacy.js');
+ runner.expect(module.DocumentControlsLegacy).toBeTruthy();
// Set global for other tests
- global.ExtractedDocumentControls = module.DocumentControls;
+ global.ExtractedDocumentControls = module.DocumentControlsLegacy;
} catch (error) {
throw new Error(`Failed to load extracted DocumentControls: ${error.message}`);
}
diff --git a/capabilities/testdrive-jsui/js/tests/test-full-integration.js b/capabilities/testdrive-jsui/js/tests/test-full-integration.js
index 3edb0ced..3b06d123 100644
--- a/capabilities/testdrive-jsui/js/tests/test-full-integration.js
+++ b/capabilities/testdrive-jsui/js/tests/test-full-integration.js
@@ -19,18 +19,18 @@ runner.describe('Full Component Integration Tests', () => {
const sectionModule = require('../core/section-manager.js');
const domModule = require('../components/dom-renderer.js');
const debugModule = require('../components/debug-panel.js');
- const controlsModule = require('../components/document-controls.js');
+ const controlsModule = require('../components/document-controls-legacy.js');
runner.expect(sectionModule.SectionManager).toBeTruthy();
runner.expect(domModule.DOMRenderer).toBeTruthy();
runner.expect(debugModule.DebugPanel).toBeTruthy();
- runner.expect(controlsModule.DocumentControls).toBeTruthy();
+ runner.expect(controlsModule.DocumentControlsLegacy).toBeTruthy();
// Set globals for other tests
global.ExtractedSectionManager = sectionModule.SectionManager;
global.ExtractedDOMRenderer = domModule.DOMRenderer;
global.ExtractedDebugPanel = debugModule.DebugPanel;
- global.ExtractedDocumentControls = controlsModule.DocumentControls;
+ global.ExtractedDocumentControls = controlsModule.DocumentControlsLegacy;
} catch (error) {
throw new Error(`Failed to load extracted components: ${error.message}`);
diff --git a/capabilities/testdrive-jsui/js/tests/test-real-user-functionality.js b/capabilities/testdrive-jsui/js/tests/test-real-user-functionality.js
index 3d7fddef..c5117bec 100644
--- a/capabilities/testdrive-jsui/js/tests/test-real-user-functionality.js
+++ b/capabilities/testdrive-jsui/js/tests/test-real-user-functionality.js
@@ -18,12 +18,12 @@ runner.describe('Real User Functionality Tests', () => {
const sectionModule = require('../core/section-manager.js');
const domModule = require('../components/dom-renderer.js');
const debugModule = require('../components/debug-panel.js');
- const controlsModule = require('../components/document-controls.js');
+ const controlsModule = require('../components/document-controls-legacy.js');
const { SectionManager } = sectionModule;
const { DOMRenderer } = domModule;
const { DebugPanel } = debugModule;
- const { DocumentControls } = controlsModule;
+ const { DocumentControlsLegacy } = controlsModule;
// Setup DOM container
const container = document.createElement('div');
@@ -34,7 +34,7 @@ runner.describe('Real User Functionality Tests', () => {
const sectionManager = new SectionManager();
const domRenderer = new DOMRenderer(sectionManager, container);
const debugPanel = new DebugPanel();
- const documentControls = new DocumentControls();
+ const documentControls = new DocumentControlsLegacy();
// Setup document controls
documentControls.create();
@@ -96,11 +96,11 @@ runner.describe('Real User Functionality Tests', () => {
// Setup similar to above
const sectionModule = require('../core/section-manager.js');
const domModule = require('../components/dom-renderer.js');
- const controlsModule = require('../components/document-controls.js');
+ const controlsModule = require('../components/document-controls-legacy.js');
const { SectionManager } = sectionModule;
const { DOMRenderer } = domModule;
- const { DocumentControls } = controlsModule;
+ const { DocumentControlsLegacy } = controlsModule;
const container = document.createElement('div');
container.innerHTML = '';
@@ -108,7 +108,7 @@ runner.describe('Real User Functionality Tests', () => {
const sectionManager = new SectionManager();
const domRenderer = new DOMRenderer(sectionManager, container);
- const documentControls = new DocumentControls();
+ const documentControls = new DocumentControlsLegacy();
documentControls.create();
@@ -195,12 +195,12 @@ runner.describe('Real User Functionality Tests', () => {
const sectionModule = require('../core/section-manager.js');
const domModule = require('../components/dom-renderer.js');
const debugModule = require('../components/debug-panel.js');
- const controlsModule = require('../components/document-controls.js');
+ const controlsModule = require('../components/document-controls-legacy.js');
const { SectionManager } = sectionModule;
const { DOMRenderer } = domModule;
const { DebugPanel } = debugModule;
- const { DocumentControls } = controlsModule;
+ const { DocumentControlsLegacy } = controlsModule;
const container = document.createElement('div');
container.innerHTML = '';
@@ -209,7 +209,7 @@ runner.describe('Real User Functionality Tests', () => {
const sectionManager = new SectionManager();
const domRenderer = new DOMRenderer(sectionManager, container);
const debugPanel = new DebugPanel();
- const documentControls = new DocumentControls();
+ const documentControls = new DocumentControlsLegacy();
documentControls.create();
diff --git a/relicts/AllControlsRudimentary.html b/capabilities/testdrive-jsui/relicts/AllControlsRudimentary.html
similarity index 100%
rename from relicts/AllControlsRudimentary.html
rename to capabilities/testdrive-jsui/relicts/AllControlsRudimentary.html
diff --git a/relicts/ControlFooter.html b/capabilities/testdrive-jsui/relicts/ControlFooter.html
similarity index 100%
rename from relicts/ControlFooter.html
rename to capabilities/testdrive-jsui/relicts/ControlFooter.html
diff --git a/relicts/DebugControlContent.html b/capabilities/testdrive-jsui/relicts/DebugControlContent.html
similarity index 100%
rename from relicts/DebugControlContent.html
rename to capabilities/testdrive-jsui/relicts/DebugControlContent.html
diff --git a/relicts/StatusPsychadelic.html b/capabilities/testdrive-jsui/relicts/StatusPsychadelic.html
similarity index 100%
rename from relicts/StatusPsychadelic.html
rename to capabilities/testdrive-jsui/relicts/StatusPsychadelic.html
diff --git a/capabilities/testdrive-jsui/scripts/list_components.py b/capabilities/testdrive-jsui/scripts/list_components.py
new file mode 100755
index 00000000..e6782e04
--- /dev/null
+++ b/capabilities/testdrive-jsui/scripts/list_components.py
@@ -0,0 +1,319 @@
+#!/usr/bin/env python3
+"""
+TestDrive-JSUI Component Lister
+
+Lists all available UI components with descriptions and key information.
+"""
+
+import re
+import json
+from pathlib import Path
+from typing import Dict, List, Optional, Tuple
+
+
+class ComponentInfo:
+ """Information about a UI component."""
+
+ def __init__(self, name: str, file_path: str, description: str,
+ component_type: str, dependencies: List[str] = None,
+ methods: List[str] = None, classes: List[str] = None):
+ self.name = name
+ self.file_path = file_path
+ self.description = description
+ self.component_type = component_type
+ self.dependencies = dependencies or []
+ self.methods = methods or []
+ self.classes = classes or []
+
+
+class ComponentAnalyzer:
+ """Analyzes JavaScript component files to extract information."""
+
+ def __init__(self, capability_root: Path):
+ self.capability_root = capability_root
+ self.js_root = capability_root / "js"
+
+ def analyze_file(self, file_path: Path) -> Optional[ComponentInfo]:
+ """Analyze a single JavaScript file for component information."""
+ if not file_path.exists():
+ return None
+
+ content = file_path.read_text()
+ relative_path = str(file_path.relative_to(self.capability_root))
+
+ # Extract component name from file path with special handling for acronyms and legacy naming
+ component_name = file_path.stem.replace('-', ' ').title().replace(' ', '')
+
+ # Special handling for common acronyms and naming patterns
+ acronym_mappings = {
+ 'DomRenderer': 'DOMRenderer',
+ 'DomControls': 'DOMControls',
+ 'HtmlRenderer': 'HTMLRenderer',
+ 'CssProcessor': 'CSSProcessor',
+ 'JsEngine': 'JSEngine',
+ 'ApiClient': 'APIClient',
+ 'UrlHandler': 'URLHandler',
+ 'DocumentControlsLegacy': 'DocumentControlsLegacy'
+ }
+
+ component_name = acronym_mappings.get(component_name, component_name)
+
+ # Extract description from file header comment
+ description = self._extract_description(content)
+
+ # Determine component type from path
+ component_type = self._determine_component_type(file_path)
+
+ # Extract dependencies
+ dependencies = self._extract_dependencies(content)
+
+ # Extract class names
+ classes = self._extract_classes(content)
+
+ # Extract method names (public methods)
+ methods = self._extract_public_methods(content)
+
+ return ComponentInfo(
+ name=component_name,
+ file_path=relative_path,
+ description=description,
+ component_type=component_type,
+ dependencies=dependencies,
+ methods=methods,
+ classes=classes
+ )
+
+ def _extract_description(self, content: str) -> str:
+ """Extract description from file header comments."""
+ # Look for block comment at start of file
+ block_comment_match = re.search(r'/\*\*(.*?)\*/', content, re.DOTALL)
+ if block_comment_match:
+ comment_lines = block_comment_match.group(1).strip()
+ # Clean up comment formatting
+ lines = []
+ for line in comment_lines.split('\n'):
+ line = line.strip().lstrip('*').strip()
+ if line and not line.startswith('Dependencies:') and not line.startswith('Extracted from'):
+ lines.append(line)
+ elif line.startswith('Dependencies:'):
+ break
+
+ description = ' '.join(lines)
+ # Take first sentence or reasonable chunk
+ if '.' in description:
+ first_sentence = description.split('.')[0] + '.'
+ if len(first_sentence) < 200:
+ return first_sentence
+
+ # Fallback to first 150 characters
+ return description[:150] + '...' if len(description) > 150 else description
+
+ # Fallback to single-line comments
+ line_comment_match = re.search(r'^\s*//\s*(.+)', content, re.MULTILINE)
+ if line_comment_match:
+ return line_comment_match.group(1).strip()
+
+ return "Component implementation"
+
+ def _determine_component_type(self, file_path: Path) -> str:
+ """Determine component type from file path."""
+ path_str = str(file_path)
+
+ # Check for legacy components first
+ if 'legacy' in path_str.lower():
+ if 'components' in path_str:
+ return "Legacy UI Component"
+ elif 'controls' in path_str:
+ return "Legacy Control"
+ else:
+ return "Legacy Module"
+
+ # Standard component types
+ if 'core' in path_str:
+ return "Core"
+ elif 'components' in path_str:
+ return "UI Component"
+ elif 'controls' in path_str:
+ return "Control"
+ elif 'utils' in path_str:
+ return "Utility"
+ else:
+ return "Module"
+
+ def _extract_dependencies(self, content: str) -> List[str]:
+ """Extract dependencies mentioned in comments or imports."""
+ dependencies = []
+
+ # Look for Dependencies section in comments
+ dep_match = re.search(r'Dependencies:\s*\n(.*?)(?:\*/|\n\s*\n)', content, re.DOTALL)
+ if dep_match:
+ dep_text = dep_match.group(1)
+ # Extract dependency names from bullet points
+ for line in dep_text.split('\n'):
+ line = line.strip().lstrip('*-').strip()
+ if line and not line.startswith('None'):
+ # Clean up dependency description
+ dep_name = line.split('(')[0].split('-')[0].strip()
+ if dep_name:
+ dependencies.append(dep_name)
+
+ # Look for import statements or requires
+ import_matches = re.findall(r'(?:import|require)\s*\(?[\'"]([^\'\"]+)[\'"]', content)
+ dependencies.extend(import_matches)
+
+ return list(set(dependencies)) # Remove duplicates
+
+ def _extract_classes(self, content: str) -> List[str]:
+ """Extract class names from the file."""
+ class_matches = re.findall(r'class\s+([A-Z][a-zA-Z0-9_]*)', content)
+ return class_matches
+
+ def _extract_public_methods(self, content: str) -> List[str]:
+ """Extract public method names from classes."""
+ methods = []
+
+ # Look for method definitions (not starting with _)
+ method_matches = re.findall(r'^\s+([a-zA-Z][a-zA-Z0-9_]*)\s*\(.*?\)\s*{', content, re.MULTILINE)
+
+ # Filter out private methods (starting with _) and constructors
+ public_methods = [m for m in method_matches if not m.startswith('_') and m != 'constructor']
+
+ return list(set(public_methods)) # Remove duplicates
+
+
+def list_components(output_format: str = "table") -> None:
+ """List all UI components in the capability."""
+
+ capability_root = Path(__file__).parent.parent
+ analyzer = ComponentAnalyzer(capability_root)
+
+ # Find all JavaScript component files
+ component_files = []
+ js_root = capability_root / "js"
+
+ if js_root.exists():
+ # Get files from components, core, and other directories
+ for pattern in ["**/*.js"]:
+ for file_path in js_root.glob(pattern):
+ # Skip test files and node_modules
+ path_str = str(file_path)
+ if ('test' not in file_path.name.lower() and
+ '/tests/' not in path_str and
+ 'node_modules' not in path_str and
+ not file_path.name.lower().endswith('.test.js')):
+ component_files.append(file_path)
+
+ # Analyze each file
+ components = []
+ for file_path in sorted(component_files):
+ component_info = analyzer.analyze_file(file_path)
+ if component_info:
+ components.append(component_info)
+
+ if not components:
+ print("β No UI components found in the capability")
+ return
+
+ # Output in requested format
+ if output_format == "json":
+ _output_json(components)
+ elif output_format == "detailed":
+ _output_detailed(components)
+ else:
+ _output_table(components)
+
+
+def _output_table(components: List[ComponentInfo]) -> None:
+ """Output components in table format."""
+ print("π§ͺ TestDrive-JSUI UI Components")
+ print("=" * 60)
+ print()
+
+ # Group by type
+ by_type = {}
+ for comp in components:
+ if comp.component_type not in by_type:
+ by_type[comp.component_type] = []
+ by_type[comp.component_type].append(comp)
+
+ for comp_type, comps in sorted(by_type.items()):
+ print(f"π¦ {comp_type} Components ({len(comps)})")
+ print("-" * 40)
+
+ for comp in sorted(comps, key=lambda x: x.name):
+ print(f" π§ {comp.name}")
+ print(f" π {comp.file_path}")
+ print(f" π {comp.description}")
+ if comp.classes:
+ print(f" ποΈ Classes: {', '.join(comp.classes)}")
+ if comp.methods:
+ key_methods = comp.methods[:4] # Show first 4 methods
+ methods_str = ', '.join(key_methods)
+ if len(comp.methods) > 4:
+ methods_str += f" (+{len(comp.methods) - 4} more)"
+ print(f" βοΈ Methods: {methods_str}")
+ print()
+
+ print()
+
+
+def _output_detailed(components: List[ComponentInfo]) -> None:
+ """Output components with detailed information."""
+ print("π§ͺ TestDrive-JSUI UI Components - Detailed View")
+ print("=" * 60)
+ print()
+
+ for i, comp in enumerate(sorted(components, key=lambda x: x.name), 1):
+ print(f"{i}. {comp.name}")
+ print(f" Type: {comp.component_type}")
+ print(f" File: {comp.file_path}")
+ print(f" Description: {comp.description}")
+
+ if comp.classes:
+ print(f" Classes: {', '.join(comp.classes)}")
+
+ if comp.methods:
+ print(f" Public Methods ({len(comp.methods)}): {', '.join(sorted(comp.methods))}")
+
+ if comp.dependencies:
+ print(f" Dependencies: {', '.join(comp.dependencies)}")
+
+ print()
+
+
+def _output_json(components: List[ComponentInfo]) -> None:
+ """Output components in JSON format."""
+ data = {
+ "capability": "testdrive-jsui",
+ "total_components": len(components),
+ "components": []
+ }
+
+ for comp in sorted(components, key=lambda x: x.name):
+ data["components"].append({
+ "name": comp.name,
+ "type": comp.component_type,
+ "file_path": comp.file_path,
+ "description": comp.description,
+ "classes": comp.classes,
+ "methods": comp.methods,
+ "dependencies": comp.dependencies
+ })
+
+ print(json.dumps(data, indent=2))
+
+
+if __name__ == "__main__":
+ import sys
+
+ output_format = "table"
+ if len(sys.argv) > 1:
+ format_arg = sys.argv[1].lower()
+ if format_arg in ["json", "detailed", "table"]:
+ output_format = format_arg
+ else:
+ print(f"β Invalid format: {format_arg}")
+ print("Usage: python list_components.py [table|detailed|json]")
+ sys.exit(1)
+
+ list_components(output_format)
\ No newline at end of file
diff --git a/capabilities/testdrive-jsui/test-control-base.html b/capabilities/testdrive-jsui/test-control-base.html
new file mode 100644
index 00000000..6ae95633
--- /dev/null
+++ b/capabilities/testdrive-jsui/test-control-base.html
@@ -0,0 +1,111 @@
+
+
+
+
+
+ Control Base Test
+
+
+
+
+
Control Base Functionality Test
+
This page tests the improved ControlBase functionality. You should see:
+
+
Icon-only collapsed state: Controls start as small icons
+
Click to expand: Click the icon to expand the control
+
Drag functionality: When expanded, drag by the header to move
+
Bottom-left resize: When expanded, grab the β corner to resize
+
Close button (β): Returns control to original position
+
Header toggle: Click the title to show/hide content
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/capabilities/testdrive-jsui/tests/test_complete.html b/capabilities/testdrive-jsui/tests/test_complete.html
new file mode 100644
index 00000000..a7431280
--- /dev/null
+++ b/capabilities/testdrive-jsui/tests/test_complete.html
@@ -0,0 +1,191 @@
+
+
+
+
+
+
+ Complete UI Test
+
+
+
+
+
+
+
+
+
+
+
+
+
Complete UI Test
+
This document tests the complete UI control system with all controls.
+
Content Section
+
This section has various content types to test the controls:
+
Lists
+
+
Item 1
+
Item 2
+
Item 3
+
+
Code Example
+
console.log('Hello World');
+
+
+
Table
+
+
+
+
Feature
+
Status
+
+
+
+
+
Status Control
+
β Working
+
+
+
Debug Control
+
β Working
+
+
+
Contents Control
+
β Working
+
+
+
Edit Control
+
β Working
+
+
+
+
Final Section
+
More content to test the table of contents functionality.
+
+
-- html from markdown by MarkiTect on 2025-11-11 23:46:11 by worsch
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/capabilities/testdrive-jsui/tests/test_component_listing.py b/capabilities/testdrive-jsui/tests/test_component_listing.py
new file mode 100644
index 00000000..a0fe2758
--- /dev/null
+++ b/capabilities/testdrive-jsui/tests/test_component_listing.py
@@ -0,0 +1,203 @@
+#!/usr/bin/env python3
+"""
+Test for Component Listing Functionality
+
+Tests that all expected UI components are properly discovered and listed
+by the component analysis system.
+"""
+
+import sys
+from pathlib import Path
+import json
+import subprocess
+import pytest
+
+# Add the scripts directory to path
+sys.path.insert(0, str(Path(__file__).parent.parent / "scripts"))
+
+from list_components import ComponentAnalyzer, list_components
+
+
+@pytest.mark.javascript
+class TestComponentListing:
+ """Test cases for component listing functionality."""
+
+ def setup_method(self):
+ """Setup test environment."""
+ self.capability_root = Path(__file__).parent.parent
+ self.analyzer = ComponentAnalyzer(self.capability_root)
+
+ def test_expected_panel_components_exist(self):
+ """Test that all expected panel components are found."""
+ expected_panels = {
+ 'ContentsControl': 'js/controls/contents-control.js',
+ 'StatusControl': 'js/controls/status-control.js',
+ 'EditControl': 'js/controls/edit-control.js',
+ 'DebugControl': 'js/controls/debug-control.js',
+ 'DebugPanel': 'js/components/debug-panel.js',
+ 'DocumentControlsLegacy': 'js/components/document-controls-legacy.js',
+ 'SectionManager': 'js/core/section-manager.js',
+ 'DOMRenderer': 'js/components/dom-renderer.js'
+ }
+
+ # Get actual components found by analyzer
+ js_files = []
+ js_root = self.capability_root / "js"
+
+ if js_root.exists():
+ for pattern in ["**/*.js"]:
+ for file_path in js_root.glob(pattern):
+ path_str = str(file_path)
+ if ('test' not in file_path.name.lower() and
+ '/tests/' not in path_str and
+ 'node_modules' not in path_str and
+ not file_path.name.lower().endswith('.test.js')):
+ js_files.append(file_path)
+
+ # Analyze each file
+ found_components = {}
+ for file_path in sorted(js_files):
+ component_info = self.analyzer.analyze_file(file_path)
+ if component_info:
+ found_components[component_info.name] = component_info.file_path
+
+ # Check that all expected panels are found
+ missing_components = []
+ for expected_name, expected_path in expected_panels.items():
+ if expected_name not in found_components:
+ missing_components.append(f"{expected_name} (expected at {expected_path})")
+ elif found_components[expected_name] != expected_path:
+ missing_components.append(
+ f"{expected_name} found at {found_components[expected_name]} "
+ f"but expected at {expected_path}"
+ )
+
+ if missing_components:
+ print(f"\nβ Missing or misplaced components:")
+ for missing in missing_components:
+ print(f" - {missing}")
+ print(f"\nβ Found components:")
+ for name, path in found_components.items():
+ print(f" - {name}: {path}")
+
+ assert False, f"Missing {len(missing_components)} expected components: {missing_components}"
+
+ print(f"β All {len(expected_panels)} expected components found!")
+ return True
+
+ def test_component_lister_json_output_completeness(self):
+ """Test that JSON output includes all expected components with proper structure."""
+ # Capture JSON output
+ result = subprocess.run([
+ sys.executable,
+ str(self.capability_root / "scripts" / "list_components.py"),
+ "json"
+ ], capture_output=True, text=True, cwd=str(self.capability_root))
+
+ assert result.returncode == 0, f"Component lister failed: {result.stderr}"
+
+ try:
+ data = json.loads(result.stdout)
+ except json.JSONDecodeError as e:
+ assert False, f"Invalid JSON output: {e}"
+
+ # Verify JSON structure
+ assert "capability" in data
+ assert "total_components" in data
+ assert "components" in data
+ assert data["capability"] == "testdrive-jsui"
+
+ # Verify each component has required fields
+ required_fields = ["name", "type", "file_path", "description", "classes", "methods"]
+ for component in data["components"]:
+ for field in required_fields:
+ assert field in component, f"Component {component.get('name')} missing field: {field}"
+
+ # Check for expected panel types
+ component_names = {comp["name"] for comp in data["components"]}
+ expected_controls = {"ContentsControl", "StatusControl", "EditControl", "DebugControl"}
+
+ found_controls = component_names.intersection(expected_controls)
+ missing_controls = expected_controls - found_controls
+
+ if missing_controls:
+ print(f"\nβ Missing control components: {missing_controls}")
+ print(f"β Found components: {component_names}")
+ assert False, f"Missing control components: {missing_controls}"
+
+ return True
+
+ def test_component_descriptions_are_meaningful(self):
+ """Test that all components have meaningful descriptions."""
+ result = subprocess.run([
+ sys.executable,
+ str(self.capability_root / "scripts" / "list_components.py"),
+ "json"
+ ], capture_output=True, text=True, cwd=str(self.capability_root))
+
+ assert result.returncode == 0, f"Component lister failed: {result.stderr}"
+ data = json.loads(result.stdout)
+
+ generic_descriptions = ["Component implementation", ""]
+ components_with_poor_descriptions = []
+
+ for component in data["components"]:
+ description = component["description"].strip()
+ if (not description or
+ description in generic_descriptions or
+ len(description) < 20):
+ components_with_poor_descriptions.append(component["name"])
+
+ if components_with_poor_descriptions:
+ print(f"\nβ Components with poor descriptions: {components_with_poor_descriptions}")
+ for comp in data["components"]:
+ if comp["name"] in components_with_poor_descriptions:
+ print(f" - {comp['name']}: '{comp['description']}'")
+
+ assert False, f"Components need better descriptions: {components_with_poor_descriptions}"
+
+ return True
+
+
+def run_tests():
+ """Run all component listing tests."""
+ test_instance = TestComponentListing()
+ test_methods = [method for method in dir(test_instance) if method.startswith('test_')]
+
+ results = {}
+ for method_name in test_methods:
+ print(f"\nπ§ͺ Running {method_name}")
+ print("=" * 60)
+
+ try:
+ test_instance.setup_method()
+ method = getattr(test_instance, method_name)
+ result = method()
+ results[method_name] = True
+ print(f"β {method_name} PASSED")
+
+ except Exception as e:
+ results[method_name] = False
+ print(f"β {method_name} FAILED: {e}")
+ import traceback
+ traceback.print_exc()
+
+ # Summary
+ passed = sum(1 for result in results.values() if result)
+ total = len(results)
+
+ print(f"\nπ Test Summary:")
+ print(f" Passed: {passed}/{total}")
+ print(f" Failed: {total - passed}/{total}")
+
+ if passed == total:
+ print("β All tests passed!")
+ return True
+ else:
+ print("β Some tests failed!")
+ return False
+
+
+if __name__ == "__main__":
+ success = run_tests()
+ sys.exit(0 if success else 1)
\ No newline at end of file
diff --git a/capabilities/testdrive-jsui/tests/test_guardrail_js.html b/capabilities/testdrive-jsui/tests/test_guardrail_js.html
new file mode 100644
index 00000000..357851d9
--- /dev/null
+++ b/capabilities/testdrive-jsui/tests/test_guardrail_js.html
@@ -0,0 +1,145 @@
+
+
+
+
+
+
+ Guardrail Principle Test - JavaScript Controls
+
+
+
+
+
Guardrail Principle Test Page
+
+
+
Test Section 1
+
This is a test paragraph to verify that the status control can properly count and analyze document content.
+
Another paragraph with some formatted text and emphasis.
+
+
+
+
Test Subsection with Table
+
+
+
Column 1
+
Column 2
+
Column 3
+
+
+
Row 1, Cell 1
+
Row 1, Cell 2
+
Row 1, Cell 3
+
+
+
Row 2, Cell 1
+
Row 2, Cell 2
+
Row 2, Cell 3
+
+
+
+
+
+
Test with Images
+
Testing image counting (placeholder images):
+
+
+
+
+
+
Test with Lists
+
+
List item 1
+
List item 2 with inline code
+
List item 3
+
+
+
+
Ordered item 1
+
Ordered item 2
+
+
+
+
+ This is a blockquote to test various content types that the status control should analyze.
+
+
+
+// This is a code block
+function testFunction() {
+ return "Testing code block counting";
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/capabilities/testdrive-jsui/tests/test_integration.html b/capabilities/testdrive-jsui/tests/test_integration.html
new file mode 100644
index 00000000..8603a47a
--- /dev/null
+++ b/capabilities/testdrive-jsui/tests/test_integration.html
@@ -0,0 +1,213 @@
+
+
+
+
+
+
+ Integration Test Document
+
+
+
+
+
+
+
+
+
+
+
+
+
Integration Test Document
+
This document tests the JavaScript controls integration with the HTML output after the Guardrail Principle refactoring.
+
Recent Changes
+
Latest Commit (dbde13e)
+
+
Enhanced control system with improved UI and debug functionality
+
Added resize functionality to all controls with hover-only visibility
+
Implemented small circle resize handles positioned in lower-right corner
+
Added header-only toggle mode for space-efficient control management
+
Created independent IndexedDB-based debug system with selection filtering
+
+
Previous Commit (3839a67)
+
+
Fixed control positioning and drag behavior
+
Updated compass positioning to be top-aligned instead of center-aligned
+
Fixed drag offset calculation to maintain cursor position at icon
+
Ensured expanded controls appear top-aligned with anchor position
+
+
Test Content
+
Headers
+
This document contains various content types to test the status control functionality.
+
Subsection
+
Content in subsections should be properly counted.
+
Lists
+
+
Item 1: Testing list counting
+
Item 2: Multiple items
+
Item 3: Final item
+
+
Tables
+
+
+
+
Column A
+
Column B
+
Column C
+
+
+
+
+
Row 1A
+
Row 1B
+
Row 1C
+
+
+
Row 2A
+
Row 2B
+
Row 2C
+
+
+
+
Code Block
+
deftest_function():
+ return"This code block should be counted"
+
+
+
Blockquote
+
+
This is a blockquote that should be analyzed by the status control.
+
+
Expected Behavior
+
The JavaScript controls should:
+1. Initialize successfully with proper error handling
+2. Display accurate document statistics
+3. Provide interactive drag/resize functionality
+4. Work with the debug system integration
+5. Handle errors gracefully per the Guardrail Principle
+
This test will verify that our external JavaScript files work correctly with the HTML template system.
+
+
-- html from markdown by MarkiTect on 2025-11-11 22:10:30 by worsch
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/capabilities/testdrive-jsui/tests/test_js_fixes.py b/capabilities/testdrive-jsui/tests/test_js_fixes.py
new file mode 100644
index 00000000..e0d1ff30
--- /dev/null
+++ b/capabilities/testdrive-jsui/tests/test_js_fixes.py
@@ -0,0 +1,167 @@
+#!/usr/bin/env python3
+"""
+Test JavaScript fixes for const redeclaration and MarkitectMain issues
+"""
+
+import sys
+from pathlib import Path
+import re
+
+# Add project root to path for imports
+project_root = Path(__file__).parent.parent.parent
+sys.path.insert(0, str(project_root))
+
+def test_javascript_fixes():
+ """Test that JavaScript const redeclaration and MarkitectMain issues are resolved."""
+
+ print("π§ Testing JavaScript Fixes")
+ print("=" * 50)
+
+ try:
+ # Test 1: Check for const declarations in loaded files
+ print("1οΈβ£ Checking for const declaration conflicts...")
+
+ from markitect.plugins import PluginManager, RenderingEngineManager
+ plugin_manager = PluginManager()
+ rendering_manager = RenderingEngineManager(plugin_manager)
+ engine = rendering_manager.get_engine('testdrive-jsui')
+
+ required_assets = engine.get_required_assets()
+ js_files = required_assets.get('js', [])
+
+ print(f" π JavaScript files to be loaded: {len(js_files)}")
+
+ const_declarations = {}
+ capability_root = Path(__file__).parent.parent
+ for js_file in js_files:
+ # Check if file exists in capability directory first
+ file_path = capability_root / js_file
+ if file_path.exists():
+ content = file_path.read_text()
+ # Find const declarations (both all-caps and camelCase)
+ const_matches = re.findall(r'^const\s+([A-Za-z_][A-Za-z0-9_]*)\s*=', content, re.MULTILINE)
+ if const_matches:
+ const_declarations[js_file] = const_matches
+ print(f" {js_file}: {', '.join(const_matches)}")
+ else:
+ print(f" {js_file}: File not found in capability directory")
+
+ # Check for duplicates
+ all_consts = []
+ for file, consts in const_declarations.items():
+ all_consts.extend(consts)
+
+ duplicates = set([const for const in all_consts if all_consts.count(const) > 1])
+
+ if duplicates:
+ print(f" β Found duplicate const declarations: {', '.join(duplicates)}")
+ return False
+ else:
+ print(f" β No duplicate const declarations found")
+
+ # Test 2: Verify key components are in the loaded files
+ print(f"\n2οΈβ£ Checking key component availability...")
+
+ # Look for important components instead of MarkitectMain
+ key_components = ['EditState', 'SectionType', 'DocumentControls', 'SectionManager', 'DOMRenderer']
+ found_components = {}
+
+ for file, consts in const_declarations.items():
+ for component in key_components:
+ if component in consts:
+ if component in found_components:
+ found_components[component].append(file)
+ else:
+ found_components[component] = [file]
+
+ missing_components = [comp for comp in key_components if comp not in found_components]
+ if missing_components:
+ print(f" β οΈ Some components not found: {', '.join(missing_components)}")
+
+ duplicate_components = {comp: files for comp, files in found_components.items() if len(files) > 1}
+ if duplicate_components:
+ print(f" β Duplicate components found: {duplicate_components}")
+ return False
+
+ if found_components:
+ print(f" β Found key components: {', '.join(found_components.keys())}")
+ else:
+ print(f" βΉοΈ No key components found (might use different patterns)")
+
+ # Test 3: Verify file structure and loading order
+ print(f"\n3οΈβ£ Checking file structure and loading order...")
+
+ main_files = [f for f in js_files if 'main' in f.lower()]
+ if main_files:
+ print(f" β Main files found: {', '.join(main_files)}")
+ else:
+ print(f" βΉοΈ No explicit main files found in asset list")
+
+ # Check for core components in the expected order
+ core_files = [f for f in js_files if 'core' in f or 'components' in f or 'controls' in f]
+ if core_files:
+ print(f" β Core component files found: {len(core_files)} files")
+ else:
+ print(f" β οΈ No core component files found")
+
+ # Test 4: Generate and verify HTML output
+ print(f"\n4οΈβ£ Testing HTML generation...")
+
+ from markitect.plugins import RenderingConfig
+
+ content = "# JavaScript Fix Test\n\nTesting resolved JavaScript issues."
+ output_dir = Path('/tmp/test_js_fixes_verification')
+ output_dir.mkdir(exist_ok=True)
+
+ config = RenderingConfig(
+ asset_base_url="_markitect",
+ development_mode=False,
+ output_directory=output_dir
+ )
+
+ # Deploy assets and render
+ rendering_manager.deploy_engine_assets('testdrive-jsui', config)
+ html_content = engine.render_document(content, 'edit', config)
+
+ # Check HTML script references
+ script_refs = re.findall(r'