# RH - Reactive Html Framework
Transform variable relationships into interactive web applications with real-time updates.
## Quick Start
```python
from rh import MeshBuilder
# Define relationships between variables
mesh_spec = {
"temp_fahrenheit": ["temp_celsius"],
"temp_kelvin": ["temp_celsius"],
}
# Define how to compute each relationship
functions_spec = {
"temp_fahrenheit": "return temp_celsius * 9/5 + 32;",
"temp_kelvin": "return temp_celsius + 273.15;",
}
# Set initial values
initial_values = {
"temp_celsius": 20.0
}
# Create and build the app
builder = MeshBuilder(mesh_spec, functions_spec, initial_values)
app_path = builder.build_app(title="Temperature Converter")
# Serve it locally
builder.serve(port=8080)
```
## Features
- **Bidirectional Dependencies**: Variables can depend on each other cyclically
- **Real-time Updates**: Changes propagate instantly through the mesh
- **Convention over Configuration**: Smart defaults based on variable names
- **Type Inference**: Automatic UI widget selection from initial values
- **Zero External Dependencies**: Works with Python stdlib only
## UI Conventions
Variable names automatically determine UI behavior:
```python
initial_values = {
"slider_opacity": 50, # → Range slider (0-100)
"readonly_result": 0, # → Read-only display
"hidden_internal": 10, # → Hidden field
"color_theme": "#ff0000", # → Color picker
"date_created": "2023-01-01" # → Date input
}
```
## Advanced Example
```python
# Physics calculator with custom field overrides
mesh_spec = {
"kinetic_energy": ["mass", "velocity"],
"momentum": ["mass", "velocity"],
"total_energy": ["kinetic_energy", "potential_energy"]
}
functions_spec = {
"kinetic_energy": "return 0.5 * mass * velocity * velocity;",
"momentum": "return mass * velocity;",
"total_energy": "return kinetic_energy + potential_energy;"
}
field_overrides = {
"mass": {
"title": "Mass (kg)",
"minimum": 0.1,
"maximum": 1000,
"ui:help": "Object mass in kilograms"
}
}
builder = MeshBuilder(mesh_spec, functions_spec,
initial_values={"mass": 10, "velocity": 5, "potential_energy": 100},
field_overrides=field_overrides)
```
## Testing
Run the test suite:
```bash
# Run pytest discovery from the project root (recommended)
python -m pytest
```
## Demo
Try the examples:
```bash
python demo.py # Multiple example apps
python example.py # Simple temperature converter with server
python persistent_apps_example.py # Shows app directory management
```
## App Directory Management
RH automatically manages app storage for persistent applications:
### Default Behavior
```python
builder = MeshBuilder(mesh_spec, functions_spec, initial_values)
# App name inferred from title, stored in RH_APP_FOLDER
app_path = builder.build_app(title="Temperature Converter")
# Creates: ~/.rh/apps/temperature_converter/index.html
# Explicit app name
app_path = builder.build_app(title="My App", app_name="custom_name")
# Creates: ~/.rh/apps/custom_name/index.html
```
### Directory Configuration
```python
from rh.util import RH_APP_FOLDER, get_app_directory
# Check current app folder location
print(f"Apps stored in: {RH_APP_FOLDER}")
# Get path for specific app
app_dir = get_app_directory("my_calculator")
```
### Environment Variables
Control where RH stores apps by setting environment variables:
```bash
# Custom app folder location
export RH_APP_FOLDER="/path/to/my/apps"
# Custom local data folder (apps will be in $RH_LOCAL_DATA_FOLDER/apps)
export RH_LOCAL_DATA_FOLDER="/path/to/my/data"
```
### Manual Directory Control
For full control over output location:
```python
builder = MeshBuilder(mesh_spec, functions_spec, initial_values)
builder.output_dir = "/path/to/specific/location"
app_path = builder.build_app(title="My App")
```
## Design & Philosophy
This section communicates the design principles and architectural decisions that guide the development of RH, helping both users understand the framework's approach and contributors align with the project's vision. **Contributors are welcome!!!**
### Core Design Principles
**🧩 Declarative over Imperative**
- Users describe *what* relationships exist between variables, not *how* to update the UI
- The framework handles the complex orchestration of updates, event handling, and DOM manipulation
- Mental model: "I have variables that relate to each other" → "I get a working interactive app"
**🔄 Convention over Configuration**
- Smart defaults based on naming patterns (`slider_*`, `readonly_*`, `hidden_*`)
- Type inference from initial values (float → number input, bool → checkbox)
- Zero-config working apps, with escape hatches for customization when needed
**⚡ Functional Programming & Immutability**
- Pure functions with no side effects in core logic
- Immutable configuration objects generated once and used everywhere
- Predictable behavior through referential transparency
- Data transformations rather than object mutations
### Architectural Decisions
**📐 Clean Architecture**
```
┌─────────────────┐
│ MeshBuilder │ ← Facade/Interface Layer
│ (Facade) │
├─────────────────┤
│ Generators │ ← Application Logic
│ • HTML │
│ • RJSF Schema │
├─────────────────┤
│ Core Logic │ ← Business Logic
│ • Type Inference│
│ • Propagation │
│ • Validation │
├─────────────────┤
│ Infrastructure │ ← Framework/Tools
│ • HTTP Server │
│ • File I/O │
│ • Templates │
└─────────────────┘
```
**🔧 Separation of Concerns**
- **Specification Layer**: Parse and validate user input (mesh specs, functions)
- **Configuration Layer**: Transform specs into explicit, normalized config
- **Generation Layer**: Create output artifacts (HTML, JS, schemas) from config
- **Runtime Layer**: Serve applications and handle deployment
**🎯 Single Source of Truth (SSOT)**
- The `generate_config()` method produces the canonical representation
- All downstream components (HTML generator, RJSF schemas, JS functions) consume only this config
- No scattered state or multiple sources of truth
**🔌 Dependency Injection & Plugin Architecture**
- Pluggable components for different output formats
- Registry pattern for optional tool detection (Jinja2, esbuild, etc.)
- Interface-based design allowing custom generators and processors
### Development Philosophy
**📚 Zero Dependencies by Design**
- Core functionality works with Python stdlib only
- Optional enhancements auto-detected and registered
- Reduces deployment complexity and increases reliability
**🧪 Test-Driven Development**
- Tests written first to clarify requirements and edge cases
- Comprehensive test coverage (24+ tests) ensuring behavioral correctness
- Integration tests verify end-to-end functionality
**📖 Documentation as Code**
- README examples are tested as part of the test suite
- Docstrings include type hints and behavioral descriptions
- Self-documenting code through clear naming and structure
**🌱 Incremental Complexity**
- Simple use cases work with minimal code
- Advanced features available through progressive disclosure
- Each abstraction level serves a clear purpose
### Contributor Guidelines
**🤝 What We Welcome**
- **New Generators**: Support for other UI frameworks (Vue, Angular, Svelte)
- **Input Parsers**: YAML/TOML mesh specs, Excel formula parsing, natural language
- **Enhanced Conventions**: More naming patterns, automatic grouping, layout hints
- **Performance Optimizations**: Faster propagation algorithms, caching strategies
- **Developer Experience**: Better error messages, debugging tools, IDE integration
**🎨 Code Style Expectations**
- **Functional first**: Prefer pure functions and data transformations
- **Type hints**: All public APIs should be fully annotated
- **Docstrings**: Include behavior descriptions and simple doctests where applicable
- **Modular design**: Single responsibility principle, composable components
- **Immutable data**: Avoid mutation, prefer transformation and copying
**🏗️ Architectural Consistency**
- New features should extend existing patterns rather than introduce new paradigms
- Maintain the declarative user interface - complexity hidden behind simple APIs
- Follow the SSOT principle - all generators work from the same configuration
- Preserve zero-dependency core with optional enhancements
**🔍 Testing Philosophy**
- Write tests first to clarify the expected behavior
- Test both happy paths and edge cases
- Include integration tests for user-facing workflows
- Ensure examples in documentation actually work
This framework embodies the principle that **complexity should be in the implementation, not the interface**. Users describe simple relationships; the framework handles the complexity of turning those into rich, interactive applications.
## License
MIT License - see LICENSE file for details.
Raw data
{
"_id": null,
"home_page": "https://github.com/i2mint/rh",
"name": "rh",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.8",
"maintainer_email": null,
"keywords": "reactive, html, web-apps, interactive, forms, javascript, mesh, reactive-programming, data-flow, rjsf, real-time, web-framework, ui-generation, declarative, computational-graph",
"author": "OtoSense",
"author_email": "thor@otosense.ai",
"download_url": "https://files.pythonhosted.org/packages/85/1b/ba4b29d7c58ea2ff4a54e151d47d7166d9623b439368a8d676d0c6436ce8/rh-0.0.9.tar.gz",
"platform": "any",
"description": "# RH - Reactive Html Framework\n\nTransform variable relationships into interactive web applications with real-time updates.\n\n## Quick Start\n\n```python\nfrom rh import MeshBuilder\n\n# Define relationships between variables\nmesh_spec = {\n \"temp_fahrenheit\": [\"temp_celsius\"],\n \"temp_kelvin\": [\"temp_celsius\"],\n}\n\n# Define how to compute each relationship\nfunctions_spec = {\n \"temp_fahrenheit\": \"return temp_celsius * 9/5 + 32;\",\n \"temp_kelvin\": \"return temp_celsius + 273.15;\",\n}\n\n# Set initial values\ninitial_values = {\n \"temp_celsius\": 20.0\n}\n\n# Create and build the app\nbuilder = MeshBuilder(mesh_spec, functions_spec, initial_values)\napp_path = builder.build_app(title=\"Temperature Converter\")\n\n# Serve it locally\nbuilder.serve(port=8080)\n```\n\n## Features\n\n- **Bidirectional Dependencies**: Variables can depend on each other cyclically\n- **Real-time Updates**: Changes propagate instantly through the mesh\n- **Convention over Configuration**: Smart defaults based on variable names\n- **Type Inference**: Automatic UI widget selection from initial values\n- **Zero External Dependencies**: Works with Python stdlib only\n\n## UI Conventions\n\nVariable names automatically determine UI behavior:\n\n```python\ninitial_values = {\n \"slider_opacity\": 50, # \u2192 Range slider (0-100)\n \"readonly_result\": 0, # \u2192 Read-only display\n \"hidden_internal\": 10, # \u2192 Hidden field\n \"color_theme\": \"#ff0000\", # \u2192 Color picker\n \"date_created\": \"2023-01-01\" # \u2192 Date input\n}\n```\n\n## Advanced Example\n\n```python\n# Physics calculator with custom field overrides\nmesh_spec = {\n \"kinetic_energy\": [\"mass\", \"velocity\"],\n \"momentum\": [\"mass\", \"velocity\"],\n \"total_energy\": [\"kinetic_energy\", \"potential_energy\"]\n}\n\nfunctions_spec = {\n \"kinetic_energy\": \"return 0.5 * mass * velocity * velocity;\",\n \"momentum\": \"return mass * velocity;\",\n \"total_energy\": \"return kinetic_energy + potential_energy;\"\n}\n\nfield_overrides = {\n \"mass\": {\n \"title\": \"Mass (kg)\",\n \"minimum\": 0.1,\n \"maximum\": 1000,\n \"ui:help\": \"Object mass in kilograms\"\n }\n}\n\nbuilder = MeshBuilder(mesh_spec, functions_spec, \n initial_values={\"mass\": 10, \"velocity\": 5, \"potential_energy\": 100},\n field_overrides=field_overrides)\n```\n\n## Testing\n\nRun the test suite:\n\n```bash\n# Run pytest discovery from the project root (recommended)\npython -m pytest\n```\n\n## Demo\n\nTry the examples:\n\n```bash\npython demo.py # Multiple example apps\npython example.py # Simple temperature converter with server\npython persistent_apps_example.py # Shows app directory management\n```\n\n## App Directory Management\n\nRH automatically manages app storage for persistent applications:\n\n### Default Behavior\n\n```python\nbuilder = MeshBuilder(mesh_spec, functions_spec, initial_values)\n\n# App name inferred from title, stored in RH_APP_FOLDER\napp_path = builder.build_app(title=\"Temperature Converter\")\n# Creates: ~/.rh/apps/temperature_converter/index.html\n\n# Explicit app name\napp_path = builder.build_app(title=\"My App\", app_name=\"custom_name\")\n# Creates: ~/.rh/apps/custom_name/index.html\n```\n\n### Directory Configuration\n\n```python\nfrom rh.util import RH_APP_FOLDER, get_app_directory\n\n# Check current app folder location\nprint(f\"Apps stored in: {RH_APP_FOLDER}\")\n\n# Get path for specific app\napp_dir = get_app_directory(\"my_calculator\")\n```\n\n### Environment Variables\n\nControl where RH stores apps by setting environment variables:\n\n```bash\n# Custom app folder location\nexport RH_APP_FOLDER=\"/path/to/my/apps\"\n\n# Custom local data folder (apps will be in $RH_LOCAL_DATA_FOLDER/apps)\nexport RH_LOCAL_DATA_FOLDER=\"/path/to/my/data\"\n```\n\n### Manual Directory Control\n\nFor full control over output location:\n\n```python\nbuilder = MeshBuilder(mesh_spec, functions_spec, initial_values)\nbuilder.output_dir = \"/path/to/specific/location\"\napp_path = builder.build_app(title=\"My App\")\n```\n\n## Design & Philosophy\n\nThis section communicates the design principles and architectural decisions that guide the development of RH, helping both users understand the framework's approach and contributors align with the project's vision. **Contributors are welcome!!!**\n\n### Core Design Principles\n\n**\ud83e\udde9 Declarative over Imperative**\n- Users describe *what* relationships exist between variables, not *how* to update the UI\n- The framework handles the complex orchestration of updates, event handling, and DOM manipulation\n- Mental model: \"I have variables that relate to each other\" \u2192 \"I get a working interactive app\"\n\n**\ud83d\udd04 Convention over Configuration**\n- Smart defaults based on naming patterns (`slider_*`, `readonly_*`, `hidden_*`)\n- Type inference from initial values (float \u2192 number input, bool \u2192 checkbox)\n- Zero-config working apps, with escape hatches for customization when needed\n\n**\u26a1 Functional Programming & Immutability**\n- Pure functions with no side effects in core logic\n- Immutable configuration objects generated once and used everywhere\n- Predictable behavior through referential transparency\n- Data transformations rather than object mutations\n\n### Architectural Decisions\n\n**\ud83d\udcd0 Clean Architecture**\n```\n\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n\u2502 MeshBuilder \u2502 \u2190 Facade/Interface Layer\n\u2502 (Facade) \u2502\n\u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n\u2502 Generators \u2502 \u2190 Application Logic\n\u2502 \u2022 HTML \u2502\n\u2502 \u2022 RJSF Schema \u2502\n\u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n\u2502 Core Logic \u2502 \u2190 Business Logic\n\u2502 \u2022 Type Inference\u2502\n\u2502 \u2022 Propagation \u2502\n\u2502 \u2022 Validation \u2502\n\u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n\u2502 Infrastructure \u2502 \u2190 Framework/Tools\n\u2502 \u2022 HTTP Server \u2502\n\u2502 \u2022 File I/O \u2502\n\u2502 \u2022 Templates \u2502\n\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n```\n\n**\ud83d\udd27 Separation of Concerns**\n- **Specification Layer**: Parse and validate user input (mesh specs, functions)\n- **Configuration Layer**: Transform specs into explicit, normalized config\n- **Generation Layer**: Create output artifacts (HTML, JS, schemas) from config\n- **Runtime Layer**: Serve applications and handle deployment\n\n**\ud83c\udfaf Single Source of Truth (SSOT)**\n- The `generate_config()` method produces the canonical representation\n- All downstream components (HTML generator, RJSF schemas, JS functions) consume only this config\n- No scattered state or multiple sources of truth\n\n**\ud83d\udd0c Dependency Injection & Plugin Architecture**\n- Pluggable components for different output formats\n- Registry pattern for optional tool detection (Jinja2, esbuild, etc.)\n- Interface-based design allowing custom generators and processors\n\n### Development Philosophy\n\n**\ud83d\udcda Zero Dependencies by Design**\n- Core functionality works with Python stdlib only\n- Optional enhancements auto-detected and registered\n- Reduces deployment complexity and increases reliability\n\n**\ud83e\uddea Test-Driven Development**\n- Tests written first to clarify requirements and edge cases\n- Comprehensive test coverage (24+ tests) ensuring behavioral correctness\n- Integration tests verify end-to-end functionality\n\n**\ud83d\udcd6 Documentation as Code**\n- README examples are tested as part of the test suite\n- Docstrings include type hints and behavioral descriptions\n- Self-documenting code through clear naming and structure\n\n**\ud83c\udf31 Incremental Complexity**\n- Simple use cases work with minimal code\n- Advanced features available through progressive disclosure\n- Each abstraction level serves a clear purpose\n\n### Contributor Guidelines\n\n**\ud83e\udd1d What We Welcome**\n- **New Generators**: Support for other UI frameworks (Vue, Angular, Svelte)\n- **Input Parsers**: YAML/TOML mesh specs, Excel formula parsing, natural language\n- **Enhanced Conventions**: More naming patterns, automatic grouping, layout hints\n- **Performance Optimizations**: Faster propagation algorithms, caching strategies\n- **Developer Experience**: Better error messages, debugging tools, IDE integration\n\n**\ud83c\udfa8 Code Style Expectations**\n- **Functional first**: Prefer pure functions and data transformations\n- **Type hints**: All public APIs should be fully annotated\n- **Docstrings**: Include behavior descriptions and simple doctests where applicable\n- **Modular design**: Single responsibility principle, composable components\n- **Immutable data**: Avoid mutation, prefer transformation and copying\n\n**\ud83c\udfd7\ufe0f Architectural Consistency**\n- New features should extend existing patterns rather than introduce new paradigms\n- Maintain the declarative user interface - complexity hidden behind simple APIs\n- Follow the SSOT principle - all generators work from the same configuration\n- Preserve zero-dependency core with optional enhancements\n\n**\ud83d\udd0d Testing Philosophy**\n- Write tests first to clarify the expected behavior\n- Test both happy paths and edge cases\n- Include integration tests for user-facing workflows\n- Ensure examples in documentation actually work\n\nThis framework embodies the principle that **complexity should be in the implementation, not the interface**. Users describe simple relationships; the framework handles the complexity of turning those into rich, interactive applications.\n\n## License\n\nMIT License - see LICENSE file for details.\n",
"bugtrack_url": null,
"license": "mit",
"summary": "Reactive Html Framework - Transform variable relationships into interactive web apps",
"version": "0.0.9",
"project_urls": {
"Bug Reports": "https://github.com/i2mint/rh/issues",
"Documentation": "https://github.com/i2mint/rh#readme",
"Homepage": "https://github.com/i2mint/rh",
"Source": "https://github.com/i2mint/rh"
},
"split_keywords": [
"reactive",
" html",
" web-apps",
" interactive",
" forms",
" javascript",
" mesh",
" reactive-programming",
" data-flow",
" rjsf",
" real-time",
" web-framework",
" ui-generation",
" declarative",
" computational-graph"
],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "e5ed9f1b496def7dd17abbc8195d7a5da4b0f999dd26dbf399ff0c3534b860cf",
"md5": "11741b0d7b89d991e178982bd3b41733",
"sha256": "5bb2cd40efcb500045d1b07ae559abb1044a5dc13bdc958ca0e312b49e274706"
},
"downloads": -1,
"filename": "rh-0.0.9-py3-none-any.whl",
"has_sig": false,
"md5_digest": "11741b0d7b89d991e178982bd3b41733",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.8",
"size": 17839,
"upload_time": "2025-08-30T15:03:42",
"upload_time_iso_8601": "2025-08-30T15:03:42.504872Z",
"url": "https://files.pythonhosted.org/packages/e5/ed/9f1b496def7dd17abbc8195d7a5da4b0f999dd26dbf399ff0c3534b860cf/rh-0.0.9-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "851bba4b29d7c58ea2ff4a54e151d47d7166d9623b439368a8d676d0c6436ce8",
"md5": "0ce7d8ef45484ba0fff5ffb5dc843f0a",
"sha256": "7965d38709c92f977c0eaee603c3fa465a75bc184407dc26e3af4a857fde32e5"
},
"downloads": -1,
"filename": "rh-0.0.9.tar.gz",
"has_sig": false,
"md5_digest": "0ce7d8ef45484ba0fff5ffb5dc843f0a",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.8",
"size": 20556,
"upload_time": "2025-08-30T15:03:43",
"upload_time_iso_8601": "2025-08-30T15:03:43.514030Z",
"url": "https://files.pythonhosted.org/packages/85/1b/ba4b29d7c58ea2ff4a54e151d47d7166d9623b439368a8d676d0c6436ce8/rh-0.0.9.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-08-30 15:03:43",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "i2mint",
"github_project": "rh",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"lcname": "rh"
}