rh specifications #1
Replies: 1 comment
-
Reactive Html Framework (RH) - Complete SpecificationOverviewWe are building a framework that transforms user-defined relationships between variables into interactive, computable systems. The framework handles bidirectional dependencies and cycles gracefully, enabling users to express their mental models of how variables relate without worrying about computational ordering or infinite loops. Core Architecture1. Mesh Specification LayerWe define computational networks (which we call "meshes") using various input formats: Input Sources:
All input sources compile to a unified mesh representation where:
2. Intermediate Representation (IR)We compile mesh specifications into a static, optimized representation containing:
This IR eliminates the need for runtime dependency analysis and enables efficient execution across different rendering environments. 3. Execution ModulesWe implement multiple execution environments from the same IR: Computational Executors:
Interactive Renderers:
Export Formats:
Key InnovationOur system resolves bidirectional dependencies through constraint satisfaction rather than sequential computation, allowing the expression of natural relationships (like Target Use CaseWe prioritize creating interactive web applications where users can manipulate variable values and observe real-time propagation of changes throughout their defined system of relationships, while maintaining flexibility in how meshes are specified and executed. Technical ImplementationCore Data Structure and AlgorithmMesh Specification Formatmesh = {
"temp_fahrenheit": ["temp_celsius"], # F = f(C)
"temp_celsius": ["temp_fahrenheit"], # C = g(F)
"temp_kelvin": ["temp_celsius"], # K = h(C)
"pressure_adjusted": ["pressure", "temp_kelvin"] # P_adj = j(P, K)
}Where:
Reverse Mapping ConstructionWe construct the inverse mapping from argument names → function names: reverse_mesh = {
"temp_celsius": ["temp_fahrenheit", "temp_kelvin"],
"temp_fahrenheit": ["temp_celsius"],
"temp_kelvin": ["pressure_adjusted"],
"pressure": ["pressure_adjusted"]
}RJSF Form GenerationFor each unique argument name across all functions, we generate:
JavaScript Propagation Algorithmclass MeshPropagator {
constructor(mesh, functions) {
this.mesh = mesh; // {"temp_f": ["temp_c"], "temp_k": ["temp_c"]}
this.functions = functions; // {"temp_f": (temp_c) => temp_c * 9/5 + 32}
this.reverseMesh = this.buildReverse(mesh);
}
// General callback factory
createCallback(changedVariable) {
return (value, formData) => {
return this.propagate(changedVariable, value, formData);
};
}
propagate(changedVariable, newValue, formData) {
const newFormData = {...formData, [changedVariable]: newValue};
const computed = new Set();
const queue = [...(this.reverseMesh[changedVariable] || [])];
while (queue.length > 0) {
const funcName = queue.shift();
if (computed.has(funcName)) {
throw new Error(`Cyclic computation detected: ${funcName}`);
}
computed.add(funcName);
const args = this.mesh[funcName];
const argValues = args.map(arg => newFormData[arg]);
const result = this.functions[funcName](...argValues);
if (newFormData[funcName] !== result) {
newFormData[funcName] = result;
// Add dependent functions to queue
if (this.reverseMesh[funcName]) {
queue.push(...this.reverseMesh[funcName].filter(f => !computed.has(f)));
}
}
}
return newFormData;
}
}Package ArchitectureFile StructureMain API Design# rh/__init__.py
from .rh import MeshBuilder
from .spec.parsers import from_functions, from_json, from_yaml
class MeshBuilder:
def __init__(self, mesh_spec, functions_spec, *,
initial_values=None,
field_overrides=None,
ui_config=None,
output_dir="./mesh_app"):
"""
mesh_spec: dict or path to JSON/YAML
functions_spec: dict mapping function names to JS code/paths/URLs
initial_values: dict of initial values for type inference
field_overrides: dict of field-specific UI customizations
"""
self.mesh = self._parse_mesh(mesh_spec)
self.functions = self._resolve_functions(functions_spec)
self.initial_values = initial_values or {}
self.field_overrides = field_overrides or {}
self.output_dir = Path(output_dir)
def generate_config(self):
"""Generate explicit configuration from inputs + conventions"""
# This is the key method - everything downstream uses only this config
def build_app(self, *, title="Mesh App", serve=False, port=8080):
"""Generate complete HTML app with all assets bundled"""
# Returns path to generated HTML file
def build_components_from_config(self, config):
"""Generate RJSF schema, functions bundle, and config separately"""
# Returns dict with 'rjsf_schema', 'rjsf_ui_schema', 'js_functions_bundle', 'propagation_config'
def serve(self, port=8080):
"""Serve the app locally for development"""Function Resolution Strategies1. Inline Functionsfunctions_spec = {
"temp_fahrenheit": "return temp_celsius * 9/5 + 32;",
"temp_kelvin": "return temp_celsius + 273.15;"
}2. File Pathsfunctions_spec = {
"temp_fahrenheit": "./functions/temperature.js#fahrenheit", # function in file
"temp_kelvin": "./functions/temperature.js#kelvin",
"complex_calc": "./calculations/physics.js" # whole file
}3. NPM Packages/URLsfunctions_spec = {
"math_operation": "https://unpkg.com/mathjs@11.0.0/lib/cjs/index.js#evaluate",
"lodash_func": "lodash#get" # from CDN
}4. Built-in Registry# rh comes with common functions
from rh.functions.registry import math_functions
functions_spec = {
**math_functions, # Pre-built math functions
"custom_func": "return x * 2;"
}JavaScript Function Resolver# rh/functions/js_resolver.py
class JSResolver:
def resolve_functions(self, functions_spec: dict) -> str:
"""Bundle all functions into a single JS object"""
resolved = {}
for func_name, spec in functions_spec.items():
if isinstance(spec, str):
if spec.startswith('http'):
resolved[func_name] = self._fetch_from_url(spec)
elif spec.startswith('./') or spec.endswith('.js'):
resolved[func_name] = self._load_from_file(spec)
elif '#' in spec:
resolved[func_name] = self._extract_function(spec)
else:
resolved[func_name] = f"function({self._infer_args(func_name)}) {{ {spec} }}"
return self._bundle_functions(resolved)
def _bundle_functions(self, functions: dict) -> str:
"""Generate JS object with all functions"""
js_functions = []
for name, code in functions.items():
js_functions.append(f'"{name}": {code}')
return f"const meshFunctions = {{{','.join(js_functions)}}};"Convention Over ConfigurationCore Conventions1. Type Inference from Values# Smart defaults based on initial values
{
"temperature": 20.5, # → number input (float detected)
"count": 42, # → number input (int detected)
"name": "default", # → text input
"enabled": True, # → checkbox
"category": ["A", "B"], # → select dropdown
}2. Function Naming Conventions# Variable names suggest UI behavior
{
"slider_temperature": 20, # → range slider (prefix detection)
"readonly_result": ["x"], # → read-only display
"hidden_internal": ["x"], # → hidden field
}3. Mesh Structure Patterns# Input variables (no dependencies) → form inputs
# Computed variables (have dependencies) → read-only displays
# Intermediate variables → hidden by defaultConfiguration Override Systemclass MeshBuilder:
def __init__(self,
mesh_spec,
functions_spec,
*,
# UI Configuration
ui_config: dict = None,
type_mappings: dict = None,
field_overrides: dict = None,
# Behavior Configuration
validation_rules: dict = None,
initial_values: dict = None):
self.conventions = DefaultConventions()
if ui_config:
self.conventions.update(ui_config)Smart DefaultsType → UI Element MappingDEFAULT_TYPE_MAPPINGS = {
(int, float): {
"ui:widget": "updown",
"ui:options": {"step": 0.1}
},
bool: {"ui:widget": "checkbox"},
str: {"ui:widget": "text"},
list: {"ui:widget": "select"},
}
# Convention-based overrides
PREFIX_MAPPINGS = {
"slider_": {"ui:widget": "range", "minimum": 0, "maximum": 100},
"readonly_": {"ui:readonly": True},
"hidden_": {"ui:widget": "hidden"},
"color_": {"ui:widget": "color"},
"date_": {"ui:widget": "date"},
}Layout Conventions# Auto-group related variables
{
"temp_celsius": ...,
"temp_fahrenheit": ..., # → Temperature group
"temp_kelvin": ...,
"pressure_psi": ..., # → Pressure group
"pressure_bar": ...,
}
# Auto-detect input vs output sections
inputs = variables_with_no_dependencies()
outputs = variables_with_dependencies()Configuration Escape Hatches# Fine-grained field control
field_overrides = {
"temperature": {
"title": "Temperature (°C)",
"ui:widget": "range",
"minimum": -100,
"maximum": 500,
"ui:help": "Ambient temperature"
}
}
# Custom type mappings
type_mappings = {
"percentage": { # Custom semantic type
"ui:widget": "range",
"minimum": 0,
"maximum": 100,
"ui:options": {"step": 1}
}
}Progressive Configuration Complexity
Lightweight Tool StrategyCore Tools with Stdlib-First Approach1. JS Bundler
2. Template Engine
3. HTTP Server
4. Dependency Resolver
5. Validation Engine
Plugin Architecture Design# rh/util.py
class PluginRegistry:
def __init__(self):
self._handlers = {}
self._auto_register()
def register(self, tool_type: str, handler_class, priority: int = 0):
"""Manual registration for custom handlers"""
def _auto_register(self):
"""Detect and register third-party tools automatically"""
if self._has_package('jinja2'):
self.register('template', Jinja2Handler, priority=10)
if self._has_package('esbuild'):
self.register('bundler', ESBuildHandler, priority=10)
# ... etc
def get_handler(self, tool_type: str):
"""Return highest-priority available handler"""
# rh/base.py
@dataclass
class MeshBuilder:
plugins: PluginRegistry = field(default_factory=PluginRegistry)
def set_bundler(self, handler):
self.plugins.register('bundler', handler, priority=20)Dependencies by Feature Level
Python Coding Standards (From User Preferences)General Principles
Modularity and Helper Functions
Documentation and Testing
Architectural Patterns: Using Built-in FacadesFavor
|
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
A place to discussion specifications
Beta Was this translation helpful? Give feedback.
All reactions