cjm-fasthtml-settings

A drop-in schema-based configuration system for FastHTML applications with automatic UI generation, sidebar navigation, and persistent storage.

Install

pip install cjm_fasthtml_settings

Project Structure

nbs/
├── components/ (2)
│   ├── forms.ipynb                  # Form generation components for settings interfaces
│   └── master_detail_adapter.ipynb  # Adapter for integrating cjm-fasthtml-interactions MasterDetail pattern into settings
├── core/ (5)
│   ├── config.ipynb        # Configuration constants, directory management, and base application schema
│   ├── html_ids.ipynb      # Centralized HTML ID constants for settings components
│   ├── schema_group.ipynb  # Grouping related configuration schemas for better organization
│   ├── schemas.ipynb       # Schema registry and management for settings
│   └── utils.ipynb         # Configuration loading, saving, and conversion utilities
├── plugins.ipynb  # Optional plugin integration for extensible settings systems
└── routes.ipynb   # FastHTML route handlers for settings interface

Total: 9 notebooks across 2 directories

Module Dependencies

graph LR
    components_forms[components.forms<br/>Forms]
    components_master_detail_adapter[components.master_detail_adapter<br/>Master-Detail Adapter]
    core_config[core.config<br/>Config]
    core_html_ids[core.html_ids<br/>HTML IDs]
    core_schema_group[core.schema_group<br/>Schema Group]
    core_schemas[core.schemas<br/>Schemas]
    core_utils[core.utils<br/>Utils]
    plugins[plugins<br/>Plugins]
    routes[routes<br/>Routes]

    components_forms --> core_html_ids
    components_forms --> core_config
    components_forms --> core_utils
    components_master_detail_adapter --> core_config
    components_master_detail_adapter --> components_forms
    components_master_detail_adapter --> core_utils
    components_master_detail_adapter --> core_schemas
    core_schemas --> core_schemas
    core_schemas --> core_config
    core_schemas --> core_schema_group
    core_utils --> core_config
    routes --> core_utils
    routes --> core_html_ids
    routes --> core_config
    routes --> components_forms
    routes --> routes
    routes --> core_schemas

17 cross-module dependencies detected

CLI Reference

No CLI commands found in this project.

Module Overview

Detailed documentation for each module in the project:

Config (config.ipynb)

Configuration constants, directory management, and base application schema

Import

from cjm_fasthtml_settings.core.config import (
    DEFAULT_CONFIG_DIR,
    get_app_config_schema
)

Functions

def get_app_config_schema(
    app_title: str = "FastHTML Application",  # Default application title
    config_dir: str = "configs",  # Default configuration directory
    server_port: int = 5000,  # Default server port
    themes_enum: Optional[List[str]] = None,  # Optional list of theme values
    themes_enum_names: Optional[List[str]] = None,  # Optional list of theme display names
    default_theme: Optional[str] = None,  # Default theme value
    include_theme: bool = True,  # Whether to include theme selection
    **extra_properties  # Additional custom properties to add to the schema
) -> Dict[str, Any]:  # JSON Schema for application configuration
    "Generate a customizable application configuration schema."

Variables

DEFAULT_CONFIG_DIR

Forms (forms.ipynb)

Form generation components for settings interfaces

Import

from cjm_fasthtml_settings.components.forms import (
    create_settings_form,
    create_settings_form_container
)

Functions

def create_settings_form(
    schema: Dict[str, Any],  # JSON schema for the form
    values: Dict[str, Any],  # Current values for the form fields
    post_url: str,  # URL for form submission
    reset_url: str,  # URL for resetting form to defaults
    target_id: str = None  # HTML ID of target container (defaults to SETTINGS_CONTENT)
) -> FT:  # Form element with settings and action buttons
    "Create a settings form with action buttons."
def create_settings_form_container(
    schema: Dict[str, Any],  # JSON schema for the form
    values: Dict[str, Any],  # Current values for the form fields
    post_url: str,  # URL for form submission
    reset_url: str,  # URL for resetting form to defaults
    alert_message: Optional[Any] = None,  # Optional alert element to display
    use_alert_container: bool = False,  # If True, add empty alert-container div
    target_id: str = None  # HTML ID of target container (defaults to SETTINGS_CONTENT)
) -> FT:  # Div containing the alert (if any) and the settings form
    "Create a container with optional alert and settings form."

HTML IDs (html_ids.ipynb)

Centralized HTML ID constants for settings components

Import

from cjm_fasthtml_settings.core.html_ids import (
    SettingsHtmlIds
)

Classes

class SettingsHtmlIds(AppHtmlIds):
    "HTML ID constants for settings components."
    
    def menu_item(
            name: str  # Settings name
        ) -> str:  # Menu item ID
        "Generate a menu item ID for a given settings name."

Master-Detail Adapter (master_detail_adapter.ipynb)

Adapter for integrating cjm-fasthtml-interactions MasterDetail pattern into settings

Import

from cjm_fasthtml_settings.components.master_detail_adapter import (
    create_settings_detail_renderer,
    create_settings_data_loader,
    is_schema_configured,
    create_settings_master_detail
)

Functions

def create_settings_detail_renderer(
    config_dir: Path,  # Configuration directory
    save_route_fn: callable,  # Function that returns save route URL for schema_id
    reset_route_fn: callable  # Function that returns reset route URL for schema_id
) -> callable:  # Render function for detail view
    """
    Create a render function for settings detail view.
    
    This creates a closure that captures the config_dir and route functions,
    returning a render function compatible with DetailItem.
    """
def create_settings_data_loader(
    schema: Dict,  # JSON schema
    schema_id: str  # Schema identifier
) -> callable:  # Data loader function
    "Create a data loader that provides schema information."
def is_schema_configured(
    schema_id: str,  # Schema identifier
    config_dir: Path  # Configuration directory
) -> bool:  # True if config file exists
    "Check if a schema has been configured."
def create_settings_master_detail(
    schemas: Dict,  # All registered schemas (from registry.get_all())
    config_dir: Path,  # Configuration directory
    save_route_fn: callable,  # Function that returns save route URL for schema_id
    reset_route_fn: callable,  # Function that returns reset route URL for schema_id
    default_schema: str = "general",  # Default schema to show
    menu_section_title: str = "Settings",  # Title for master list
    plugin_registry: Optional[Any] = None,  # Optional plugin registry
    plugin_save_route_fn: Optional[callable] = None,  # Function that returns save route URL for plugin_id
    plugin_reset_route_fn: Optional[callable] = None  # Function that returns reset route URL for plugin_id
) -> MasterDetail:  # Configured MasterDetail instance
    """
    Create a MasterDetail instance configured for settings.
    
    This adapter function transforms the settings schema structure into
    DetailItem and DetailItemGroup objects compatible with MasterDetail.
    """

Plugins (plugins.ipynb)

Optional plugin integration for extensible settings systems

Import

from cjm_fasthtml_settings.plugins import (
    PluginRegistryProtocol
)

Classes

@runtime_checkable
class PluginRegistryProtocol(Protocol):
    "Protocol that plugin registries should implement."
    
    def get_plugin(
            self, 
            unique_id: str  # Plugin unique ID
        ) -> Optional[PluginMetadata]:  # Plugin metadata or None
        "Get plugin metadata by unique ID."
    
    def get_plugins_by_category(
            self, 
            category: str  # Category name
        ) -> list[PluginMetadata]:  # List of plugins in category
        "Get all plugins in a category."
    
    def get_categories_with_plugins(
            self
        ) -> list[str]:  # List of category names
        "Get all categories that have registered plugins."
    
    def load_plugin_config(
            self, 
            unique_id: str  # Plugin unique ID
        ) -> Dict[str, Any]:  # Loaded configuration
        "Load saved configuration for a plugin."
    
    def save_plugin_config(
            self, 
            unique_id: str,  # Plugin unique ID
            config: Dict[str, Any]  # Configuration to save
        ) -> bool:  # True if save succeeded
        "Save configuration for a plugin."

Routes (routes.ipynb)

FastHTML route handlers for settings interface

Import

from cjm_fasthtml_settings.routes import (
    config,
    settings_ar,
    RoutesConfig,
    configure_settings,
    index,
    save,
    reset,
    plugin_reset,
    plugin_save
)

Functions

def configure_settings(
    config_dir: Path = None,  # Directory for storing configuration files
    wrap_with_layout: Callable = None,  # Function to wrap full page content with app layout
    plugin_registry = None,  # Optional plugin registry (must implement PluginRegistryProtocol)
    default_schema: str = "general",  # Default schema to display
    menu_section_title: str = "Settings"  # Title for the settings menu section
) -> RoutesConfig:  # Configured RoutesConfig instance
    "Configure the settings system with a single function call."
def _resolve_schema(
    id: str  # Schema ID
) -> tuple:  # (schema, error_message)
    "Resolve schema from ID using the registry."
@settings_ar
def index(
    request,  # FastHTML request object
    id: str = None  # Schema ID to display (defaults to config.default_schema)
) -> FT:  # Settings page content
    "Main settings page."
@settings_ar
async def save(
    request,  # FastHTML request object
    id: str  # Schema ID to save
) -> FT:  # Response with form or error
    "Save configuration handler."
@settings_ar
def reset(
    id: str  # Schema ID to reset
) -> FT:  # Response with form or error
    "Reset configuration to defaults handler."
@settings_ar
def plugin_reset(
    id: str  # Plugin unique ID
) -> FT:  # Response with form or error
    "Reset plugin configuration to defaults handler."
@settings_ar
async def plugin_save(
    request,  # FastHTML request object
    id: str  # Plugin unique ID
) -> FT:  # Response with form or error
    "Save plugin configuration handler."

Classes

class RoutesConfig:
    "Configuration for settings routes behavior."

Schema Group (schema_group.ipynb)

Grouping related configuration schemas for better organization

Import

from cjm_fasthtml_settings.core.schema_group import (
    SchemaGroup
)

Classes

@dataclass
class SchemaGroup:
    "A group of related configuration schemas."
    
    name: str
    title: str
    schemas: Dict[str, Dict[str, Any]]
    icon: Optional[Any]
    default_open: bool = True
    description: Optional[str]
    
    def get_schema(
            self, 
            schema_name: str  # Schema name
        ) -> Optional[Dict[str, Any]]:  # Schema dictionary or None
        "Get a specific schema from the group by name."
    
    def get_unique_id(
            self, 
            schema_name: str  # Schema name
        ) -> str:  # Unique ID in format: {group_name}_{schema_name}
        "Generate a unique ID for a schema within this group."
    
    def has_configured_schemas(
            self,
            config_dir: Path  # Directory where config files are stored
        ) -> bool:  # True if any schema in group has saved config
        "Check if any schemas in this group have saved configurations."
    
    def get_configured_schemas(
            self,
            config_dir: Path  # Directory where config files are stored
        ) -> list:  # List of schema names that have saved configs
        "Get list of configured schema names in this group."

Schemas (schemas.ipynb)

Schema registry and management for settings

Import

from cjm_fasthtml_settings.core.schemas import (
    registry,
    SettingsRegistry
)

Classes

class SettingsRegistry:
    def __init__(self):
        self._schemas: Dict[str, Union[Dict[str, Any], 'SchemaGroup']] = {}
    "Registry for managing settings schemas and schema groups."
    
    def __init__(self):
            self._schemas: Dict[str, Union[Dict[str, Any], 'SchemaGroup']] = {}
    
    def register(
            self,
            schema: Union[Dict[str, Any], 'SchemaGroup'],  # Schema or SchemaGroup to register
            name: Optional[str] = None  # Optional name override
        )
        "Register a settings schema or schema group."
    
    def get(
            self,
            name: str  # Name of the schema/group to retrieve
        ) -> Optional[Union[Dict[str, Any], 'SchemaGroup']]:  # The schema/group, or None if not found
        "Get a registered schema or group by name."
    
    def list_schemas(
            self
        ) -> list:  # List of registered schema/group names
        "List all registered schema and group names."
    
    def get_all(
            self
        ) -> Dict[str, Union[Dict[str, Any], 'SchemaGroup']]:  # All schemas and groups
        "Get all registered schemas and groups."
    
    def resolve_schema(
            self,
            id: str  # Schema ID (can be 'name' or 'group_schema' format)
        ) -> tuple:  # (schema_dict, error_message)
        "Resolve a schema ID to a schema dictionary."

Utils (utils.ipynb)

Configuration loading, saving, and conversion utilities

Import

from cjm_fasthtml_settings.core.utils import (
    load_config,
    save_config,
    get_default_values_from_schema,
    get_config_with_defaults,
    convert_form_data_to_config
)

Functions

def load_config(
    schema_name: str,  # Name of the schema/configuration to load
    config_dir: Optional[Path] = None  # Directory where config files are stored
) -> Dict[str, Any]:  # Loaded configuration dictionary (empty dict if file doesn't exist)
    "Load saved configuration for a schema."
def save_config(
    schema_name: str,  # Name of the schema/configuration to save
    config: Dict[str, Any],  # Configuration dictionary to save
    config_dir: Optional[Path] = None  # Directory where config files are stored
) -> bool:  # True if save succeeded, False otherwise
    "Save configuration for a schema."
def get_default_values_from_schema(
    schema: Dict[str, Any]  # JSON Schema dictionary
) -> Dict[str, Any]:  # Dictionary of default values extracted from schema
    "Extract default values from a JSON schema."
def get_config_with_defaults(
    schema_name: str,  # Name of the schema (or unique_id for grouped schemas)
    schema: Dict[str, Any],  # JSON Schema dictionary
    config_dir: Optional[Path] = None  # Directory where config files are stored
) -> Dict[str, Any]:  # Merged configuration with defaults and saved values
    "Get configuration with defaults merged with saved values."
def convert_form_data_to_config(
    form_data: dict,  # Raw form data from request
    schema: Dict[str, Any]  # JSON Schema for type conversion
) -> dict:  # Converted configuration dictionary
    "Convert form data to configuration dict based on schema."