""" Content Viewer - Center panel for displaying lesson content """ from PyQt5.QtWidgets import QWidget, QVBoxLayout, QLabel from PyQt5.QtWebEngineWidgets import QWebEngineView, QWebEnginePage from PyQt5.QtCore import Qt, pyqtSignal, QUrl from pathlib import Path import markdown from pymdownx import superfences, arithmatex from app import config from app.models import Lesson from app.utils import VariableWrapper class ContentViewer(QWidget): """Center panel for displaying lesson content with markdown and MathJax""" # Signals scroll_position_changed = pyqtSignal(float) # For auto-save def __init__(self, parent=None): super().__init__(parent) self.current_lesson = None self.markdown_converter = self._init_markdown() self.variable_wrapper = VariableWrapper() self.init_ui() def init_ui(self): """Initialize the UI components""" layout = QVBoxLayout(self) layout.setContentsMargins(0, 0, 0, 0) # Lesson title bar self.title_label = QLabel("No lesson selected") self.title_label.setStyleSheet(f""" background-color: {config.COLOR_PRIMARY}; color: white; font-size: 16pt; font-weight: bold; padding: 12px; """) self.title_label.setWordWrap(True) layout.addWidget(self.title_label) # Web view for content self.web_view = QWebEngineView() self.web_view.setPage(QWebEnginePage(self.web_view)) layout.addWidget(self.web_view, 1) # Load welcome page self.show_welcome() def _init_markdown(self): """Initialize markdown converter with extensions""" return markdown.Markdown( extensions=[ 'extra', 'codehilite', 'tables', 'toc', 'pymdownx.arithmatex', 'pymdownx.superfences', 'pymdownx.highlight', 'pymdownx.inlinehilite', ], extension_configs={ 'pymdownx.arithmatex': { 'generic': True }, 'codehilite': { 'css_class': 'highlight', 'linenums': False } } ) def show_welcome(self): """Display welcome message""" html = self._wrap_html("""

Welcome to Tesla Coil Spark Physics Course

Select a lesson from the navigation panel to begin learning.

⚡ Explore the fascinating world of Tesla coils and electromagnetic theory ⚡

""", "Welcome") self.web_view.setHtml(html) self.title_label.setText("Welcome") def load_lesson(self, lesson: Lesson): """Load and display a lesson""" self.current_lesson = lesson self.title_label.setText(f"{lesson.order}. {lesson.title}") # Read markdown file lesson_path = Path(lesson.file_path) if not lesson_path.exists(): self.show_error(f"Lesson file not found: {lesson.file_path}") return try: with open(lesson_path, 'r', encoding='utf-8') as f: markdown_content = f.read() # Convert markdown to HTML html_content = self.markdown_converter.convert(markdown_content) # Process custom tags html_content = self._process_custom_tags(html_content, lesson) # Wrap variables with tooltips html_content = self.variable_wrapper.wrap_in_context(html_content) # Wrap in full HTML document full_html = self._wrap_html(html_content, lesson.title) # Load into web view self.web_view.setHtml(full_html, QUrl.fromLocalFile(str(lesson_path.parent))) except Exception as e: self.show_error(f"Error loading lesson: {str(e)}") def _process_custom_tags(self, html: str, lesson: Lesson) -> str: """Process custom tags like {exercise:id} and {image:file}""" import re # Process {exercise:id} tags def replace_exercise(match): exercise_id = match.group(1) return f'''

📝 Exercise: {exercise_id}

Interactive exercise will be loaded here

''' html = re.sub(r'\{exercise:([^}]+)\}', replace_exercise, html) # Process {image:file} tags def replace_image(match): image_file = match.group(1) image_path = config.IMAGES_DIR / image_file return f'{image_file}' html = re.sub(r'\{image:([^}]+)\}', replace_image, html) return html def _wrap_html(self, content: str, title: str) -> str: """Wrap content in full HTML document with styling and MathJax""" return f""" {title} {content} """ def show_error(self, message: str): """Display an error message""" html = self._wrap_html(f"""

⚠ Error

{message}

""", "Error") self.web_view.setHtml(html) def get_scroll_position(self) -> float: """Get current scroll position (0.0 to 1.0)""" # This would require JavaScript execution in QWebEngineView # For now, return 0.0 - can be implemented later return 0.0 def set_scroll_position(self, position: float): """Set scroll position (0.0 to 1.0)""" # This would require JavaScript execution in QWebEngineView # For now, do nothing - can be implemented later pass