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_settingsProject 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_DIRForms (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."