from flask import render_template, jsonify, request, session from functools import wraps import time class MithrilError(Exception): def __init__(self, message, code=400): self.message = message self.code = code class Mithril: _shared_data = {} @classmethod def share(cls, key, value): """Add shared data that will be available to all components""" cls._shared_data[key] = value @staticmethod def render(component_name, props=None): """Render a Mithril component""" instance = Mithril(component_name, props or {}) return instance() @classmethod def error_handler(cls, error_handler): """Decorator to register a global error handler""" cls._error_handler = error_handler return error_handler def __init__(self, component_name, props=None): self.component_name = component_name self.props = props or {} self.version = int(time.time()) # Simple asset versioning def _prepare_response_data(self): """Prepare the data to be sent to the frontend""" return { 'component': self.component_name, 'props': self.props, 'shared': self._shared_data, 'version': self.version, 'errors': session.get('errors', {}), # Flash-style errors 'flash': session.get('flash', []) # Flash messages } def __call__(self): try: response_data = self._prepare_response_data() # Clear flash data after preparing response session.pop('errors', None) session.pop('flash', None) if request.headers.get('X-Mithril-Request'): return jsonify(response_data) return render_template('mithril_loader.html', **response_data) except Exception as e: if hasattr(self, '_error_handler'): return self._error_handler(e) raise @classmethod def form(cls, route): """Decorator for handling form submissions""" def decorator(f): @wraps(f) def wrapped(*args, **kwargs): try: if request.headers.get('X-Mithril-Request'): form_data = request.get_json() else: form_data = request.form return f(form_data, *args, **kwargs) except MithrilError as e: session['errors'] = {'message': e.message} if request.headers.get('X-Mithril-Request'): return jsonify({'errors': {'message': e.message}}), e.code return cls.redirect_back() return wrapped return decorator