Added first controls
This commit is contained in:
425
CLAUDE.md
Normal file
425
CLAUDE.md
Normal file
@@ -0,0 +1,425 @@
|
||||
# CLAUDE.md
|
||||
|
||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||
|
||||
## Project Overview
|
||||
|
||||
MyFastHtml is a Python utility library that simplifies FastHTML application development by providing:
|
||||
- Command management system for client-server interactions
|
||||
- Bidirectional data binding system
|
||||
- Predefined authentication pages and routes
|
||||
- Interactive control helpers
|
||||
- Session-based instance management
|
||||
|
||||
**Tech Stack**: Python 3.12+, FastHTML, HTMX, DaisyUI 5, Tailwind CSS 4
|
||||
|
||||
## Development Workflow and Guidelines
|
||||
|
||||
### Development Process
|
||||
|
||||
**Code must always be testable**. Before writing any code:
|
||||
|
||||
1. **Explain available options first** - Present different approaches to solve the problem
|
||||
2. **Wait for validation** - Ensure mutual understanding of requirements before implementation
|
||||
3. **No code without approval** - Only proceed after explicit validation
|
||||
|
||||
### Collaboration Style
|
||||
|
||||
**Ask questions to clarify understanding or suggest alternative approaches:**
|
||||
- Ask questions **one at a time**
|
||||
- Wait for complete answer before asking the next question
|
||||
- Indicate progress: "Question 1/5" if multiple questions are needed
|
||||
- Never assume - always clarify ambiguities
|
||||
|
||||
### Communication
|
||||
|
||||
**Conversations**: French or English
|
||||
**Code, documentation, comments**: English only
|
||||
|
||||
### Code Standards
|
||||
|
||||
**Follow PEP 8** conventions strictly:
|
||||
- Variable and function names: `snake_case`
|
||||
- Explicit, descriptive naming
|
||||
- **No emojis in code**
|
||||
|
||||
**Documentation**:
|
||||
- Use Google or NumPy docstring format
|
||||
- Document all public functions and classes
|
||||
- Include type hints where applicable
|
||||
|
||||
### Dependency Management
|
||||
|
||||
**When introducing new dependencies:**
|
||||
- List all external dependencies explicitly
|
||||
- Propose alternatives using Python standard library when possible
|
||||
- Explain why each dependency is needed
|
||||
|
||||
### Unit Testing with pytest
|
||||
|
||||
**Test naming patterns:**
|
||||
- Passing tests: `test_i_can_xxx` - Tests that should succeed
|
||||
- Failing tests: `test_i_cannot_xxx` - Edge cases that should raise errors/exceptions
|
||||
|
||||
**Test structure:**
|
||||
- Use **functions**, not classes (unless inheritance is required)
|
||||
- Before writing tests, **list all planned tests with explanations**
|
||||
- Wait for validation before implementing tests
|
||||
|
||||
**Example:**
|
||||
```python
|
||||
def test_i_can_create_command_with_valid_name():
|
||||
"""Test that a command can be created with a valid name."""
|
||||
cmd = Command("valid_name", "description", lambda: None)
|
||||
assert cmd.name == "valid_name"
|
||||
|
||||
def test_i_cannot_create_command_with_empty_name():
|
||||
"""Test that creating a command with empty name raises ValueError."""
|
||||
with pytest.raises(ValueError):
|
||||
Command("", "description", lambda: None)
|
||||
```
|
||||
|
||||
### File Management
|
||||
|
||||
**Always specify the full file path** when adding or modifying files:
|
||||
```
|
||||
✅ Modifying: src/myfasthtml/core/commands.py
|
||||
✅ Creating: tests/core/test_new_feature.py
|
||||
```
|
||||
|
||||
### Error Handling
|
||||
|
||||
**When errors occur:**
|
||||
1. **Explain the problem clearly first**
|
||||
2. **Do not propose a fix immediately**
|
||||
3. **Wait for validation** that the diagnosis is correct
|
||||
4. Only then propose solutions
|
||||
|
||||
## Development Commands
|
||||
|
||||
### Testing
|
||||
```bash
|
||||
# Run all tests
|
||||
pytest
|
||||
|
||||
# Run specific test file
|
||||
pytest tests/core/test_bindings.py
|
||||
|
||||
# Run specific test
|
||||
pytest tests/core/test_bindings.py::test_function_name
|
||||
|
||||
# Run tests with verbose output
|
||||
pytest -v
|
||||
```
|
||||
|
||||
### Cleaning
|
||||
```bash
|
||||
# Clean build artifacts and cache
|
||||
make clean
|
||||
|
||||
# Clean package distribution files
|
||||
make clean-package
|
||||
|
||||
# Clean test artifacts (.sesskey, test databases)
|
||||
make clean-tests
|
||||
|
||||
# Clean everything including source artifacts
|
||||
make clean-all
|
||||
```
|
||||
|
||||
### Package Building
|
||||
```bash
|
||||
# Build distribution
|
||||
python -m build
|
||||
|
||||
# Install in development mode
|
||||
pip install -e .
|
||||
```
|
||||
|
||||
## Architecture Overview
|
||||
|
||||
### Core System: Commands
|
||||
|
||||
Commands abstract HTMX interactions by encapsulating server-side actions. Located in `src/myfasthtml/core/commands.py`.
|
||||
|
||||
**Key classes:**
|
||||
- `BaseCommand`: Base class for all commands with HTMX integration
|
||||
- `Command`: Standard command that executes a Python callable
|
||||
- `LambdaCommand`: Inline command for simple operations
|
||||
- `CommandsManager`: Global registry for command execution
|
||||
|
||||
**How commands work:**
|
||||
1. Create command with action: `cmd = Command("name", "description", callable)`
|
||||
2. Command auto-registers with `CommandsManager`
|
||||
3. `cmd.get_htmx_params()` generates HTMX attributes (`hx-post`, `hx-vals`)
|
||||
4. HTMX posts to `/myfasthtml/commands` route with `c_id`
|
||||
5. `CommandsManager` routes to correct command's `execute()` method
|
||||
|
||||
**Command customization:**
|
||||
```python
|
||||
# Change HTMX target and swap
|
||||
cmd.htmx(target="#result", swap="innerHTML")
|
||||
|
||||
# Bind to observable data (disables swap by default)
|
||||
cmd.bind(data_object)
|
||||
```
|
||||
|
||||
### Core System: Bindings
|
||||
|
||||
Bidirectional data binding system connects UI components with Python data objects. Located in `src/myfasthtml/core/bindings.py`.
|
||||
|
||||
**Key concepts:**
|
||||
- **Observable objects**: Use `make_observable()` from myutils to enable change detection
|
||||
- **Three-phase lifecycle**: Create → Activate (bind_ft) → Deactivate
|
||||
- **Detection modes**: How changes are detected (ValueChange, AttributePresence, SelectValueChange)
|
||||
- **Update modes**: How UI updates (ValueChange, AttributePresence, SelectValueChange)
|
||||
- **Data converters**: Transform data between UI and Python representations
|
||||
|
||||
**Binding flow:**
|
||||
1. User changes input → HTMX posts to `/myfasthtml/bindings`
|
||||
2. `Binding.update()` receives form data, updates observable object
|
||||
3. Observable triggers change event → `Binding.notify()`
|
||||
4. All bound UI elements update via HTMX swap-oob
|
||||
|
||||
**Helper usage:**
|
||||
```python
|
||||
from myfasthtml.controls.helpers import mk
|
||||
|
||||
# Bind input and label to same data
|
||||
input_elt = Input(name="field")
|
||||
label_elt = Label()
|
||||
|
||||
mk.manage_binding(input_elt, Binding(data, "attr"))
|
||||
mk.manage_binding(label_elt, Binding(data, "attr"))
|
||||
```
|
||||
|
||||
**Important binding notes:**
|
||||
- Elements MUST have a `name` attribute to trigger updates
|
||||
- Multiple elements can bind to same data attribute
|
||||
- First binding call uses `init_binding=True` to set initial value
|
||||
- Bindings route through `/myfasthtml/bindings` endpoint
|
||||
|
||||
### Core System: Instances
|
||||
|
||||
Session-scoped instance management system. Located in `src/myfasthtml/core/instances.py`.
|
||||
|
||||
**Key classes:**
|
||||
- `BaseInstance`: Base for all managed instances
|
||||
- `SingleInstance`: One instance per parent per session
|
||||
- `UniqueInstance`: One instance ever per session (singleton-like)
|
||||
- `RootInstance`: Top-level singleton for application
|
||||
- `InstancesManager`: Global registry with session-based isolation
|
||||
|
||||
**Instance creation pattern:**
|
||||
```python
|
||||
# __new__ checks registry before creating
|
||||
# If instance exists with same (session_id, _id), returns existing
|
||||
instance = MyInstance(parent, session, _id="optional")
|
||||
```
|
||||
|
||||
**Automatic ID generation:**
|
||||
- SingleInstance: `snake_case_class_name`
|
||||
- UniqueInstance: `snake_case_class_name`
|
||||
- Regular BaseInstance: `parent_prefix-uuid`
|
||||
|
||||
### Application Setup
|
||||
|
||||
**Main entry point**: `create_app()` in `src/myfasthtml/myfastapp.py`
|
||||
|
||||
```python
|
||||
from myfasthtml.myfastapp import create_app
|
||||
|
||||
app, rt = create_app(
|
||||
daisyui=True, # Include DaisyUI CSS
|
||||
vis=True, # Include vis-network.js
|
||||
protect_routes=True, # Enable auth beforeware
|
||||
mount_auth_app=False, # Mount auth routes
|
||||
base_url=None # Base URL for auth
|
||||
)
|
||||
```
|
||||
|
||||
**What create_app does:**
|
||||
1. Adds MyFastHtml CSS/JS assets via custom static route
|
||||
2. Optionally adds DaisyUI 5 + Tailwind CSS 4
|
||||
3. Optionally adds vis-network.js
|
||||
4. Mounts `/myfasthtml` app for commands and bindings routes
|
||||
5. Optionally sets up auth routes and beforeware
|
||||
6. Creates AuthProxy instance if auth enabled
|
||||
|
||||
### Authentication System
|
||||
|
||||
Located in `src/myfasthtml/auth/`. Integrates with FastAPI backend (myauth package).
|
||||
|
||||
**Key components:**
|
||||
- `auth/utils.py`: JWT helpers, beforeware for route protection
|
||||
- `auth/routes.py`: Login, register, logout routes
|
||||
- `auth/pages/`: LoginPage, RegisterPage, WelcomePage components
|
||||
|
||||
**How auth works:**
|
||||
1. Beforeware checks `access_token` in session before each route
|
||||
2. Auto-refreshes token if < 5 minutes to expiry
|
||||
3. Redirects to `/login` if token invalid/missing
|
||||
4. Protected routes receive `auth` parameter with user info
|
||||
|
||||
**Session structure:**
|
||||
```python
|
||||
{
|
||||
'access_token': 'jwt_token',
|
||||
'refresh_token': 'refresh_token',
|
||||
'user_info': {
|
||||
'email': 'user@example.com',
|
||||
'username': 'user',
|
||||
'roles': ['admin'],
|
||||
'id': 'uuid',
|
||||
'created_at': 'timestamp',
|
||||
'updated_at': 'timestamp'
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
src/myfasthtml/
|
||||
├── myfastapp.py # Main app factory (create_app)
|
||||
├── core/
|
||||
│ ├── commands.py # Command system
|
||||
│ ├── bindings.py # Binding system
|
||||
│ ├── instances.py # Instance management
|
||||
│ ├── utils.py # Utilities, routes app
|
||||
│ ├── constants.py # Routes, constants
|
||||
│ ├── dbmanager.py # Database helpers
|
||||
│ ├── AuthProxy.py # Auth proxy instance
|
||||
│ └── network_utils.py # Network utilities
|
||||
├── controls/
|
||||
│ ├── helpers.py # mk class with UI helpers
|
||||
│ ├── BaseCommands.py # Base command implementations
|
||||
│ ├── Search.py # Search control
|
||||
│ └── Keyboard.py # Keyboard shortcuts
|
||||
├── auth/
|
||||
│ ├── utils.py # JWT, beforeware
|
||||
│ ├── routes.py # Auth routes
|
||||
│ └── pages/ # Login, Register, Welcome pages
|
||||
├── icons/ # Icon libraries (fluent, material, etc.)
|
||||
├── assets/ # CSS/JS files
|
||||
└── test/ # Test utilities
|
||||
|
||||
tests/
|
||||
├── core/ # Core system tests
|
||||
├── testclient/ # TestClient and TestableElement tests
|
||||
├── auth/ # Authentication tests
|
||||
├── controls/ # Control tests
|
||||
└── html/ # HTML component tests
|
||||
```
|
||||
|
||||
## Testing System
|
||||
|
||||
**Custom test client**: `myfasthtml.test.TestClient` extends FastHTML test client
|
||||
|
||||
**Key features:**
|
||||
- `user.open(path)`: Navigate to route
|
||||
- `user.find_element(selector)`: Find element by CSS selector
|
||||
- `user.should_see(text)`: Assert text in response
|
||||
- Returns `TestableElement` objects with component-specific methods
|
||||
|
||||
**Testable element types:**
|
||||
- `TestableInput`: `.send(value)`
|
||||
- `TestableCheckbox`: `.check()`, `.uncheck()`, `.toggle()`
|
||||
- `TestableTextarea`: `.send(value)`, `.append(text)`, `.clear()`
|
||||
- `TestableSelect`: `.select(value)`, `.select_by_text(text)`, `.deselect(value)`
|
||||
- `TestableRange`: `.set(value)`, `.increase()`, `.decrease()`
|
||||
- `TestableRadio`: `.select()`
|
||||
- `TestableButton`: `.click()`
|
||||
- `TestableDatalist`: `.send(value)`, `.select_suggestion(value)`
|
||||
|
||||
**Test pattern:**
|
||||
```python
|
||||
def test_component(user, rt):
|
||||
@rt("/")
|
||||
def index():
|
||||
data = Data("initial")
|
||||
component = Component(name="field")
|
||||
label = Label()
|
||||
|
||||
mk.manage_binding(component, Binding(data))
|
||||
mk.manage_binding(label, Binding(data))
|
||||
|
||||
return component, label
|
||||
|
||||
user.open("/")
|
||||
user.should_see("initial")
|
||||
|
||||
elem = user.find_element("selector")
|
||||
elem.method("new_value")
|
||||
user.should_see("new_value")
|
||||
```
|
||||
|
||||
## Important Patterns
|
||||
|
||||
### Creating interactive buttons with commands
|
||||
```python
|
||||
from myfasthtml.controls.helpers import mk
|
||||
from myfasthtml.core.commands import Command
|
||||
|
||||
def action():
|
||||
return "Result"
|
||||
|
||||
cmd = Command("action", "Description", action)
|
||||
button = mk.button("Click", command=cmd)
|
||||
```
|
||||
|
||||
### Bidirectional binding
|
||||
```python
|
||||
from myutils.observable import make_observable
|
||||
from myfasthtml.core.bindings import Binding
|
||||
from myfasthtml.controls.helpers import mk
|
||||
|
||||
data = make_observable(Data("value"))
|
||||
input_elt = Input(name="field")
|
||||
label_elt = Label()
|
||||
|
||||
mk.manage_binding(input_elt, Binding(data, "value"))
|
||||
mk.manage_binding(label_elt, Binding(data, "value"))
|
||||
```
|
||||
|
||||
### Using helpers (mk class)
|
||||
```python
|
||||
# Button with command
|
||||
mk.button("Text", command=cmd, cls="btn-primary")
|
||||
|
||||
# Icon with command
|
||||
mk.icon(icon_svg, size=20, command=cmd)
|
||||
|
||||
# Label with icon
|
||||
mk.label("Text", icon=icon_svg, size="sm")
|
||||
|
||||
# Generic wrapper
|
||||
mk.mk(element, command=cmd, binding=binding)
|
||||
```
|
||||
|
||||
## Common Gotchas
|
||||
|
||||
1. **Bindings require `name` attribute**: Without it, form data won't include the field
|
||||
2. **Commands auto-register**: Don't manually register with CommandsManager
|
||||
3. **Instances use __new__ caching**: Same (session, id) returns existing instance
|
||||
4. **First binding needs init**: Use `init_binding=True` to set initial value
|
||||
5. **Observable required for bindings**: Use `make_observable()` from myutils
|
||||
6. **Auth routes need base_url**: Pass `base_url` to `create_app()` for proper auth API calls
|
||||
|
||||
## Dependencies
|
||||
|
||||
**Core:**
|
||||
- python-fasthtml: Web framework
|
||||
- myauth: Authentication backend
|
||||
- mydbengine: Database abstraction
|
||||
- myutils: Observable pattern, utilities
|
||||
|
||||
**UI:**
|
||||
- DaisyUI 5: Component library
|
||||
- Tailwind CSS 4: Styling
|
||||
- vis-network: Network visualization
|
||||
|
||||
**Development:**
|
||||
- pytest: Testing framework
|
||||
- httpx: HTTP client for tests
|
||||
- python-dotenv: Environment variables
|
||||
Reference in New Issue
Block a user