Files
MyFastHtml/CLAUDE.md
2025-11-26 20:50:26 +01:00

13 KiB

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:

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

# 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

# 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

# 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:

# 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:

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:

# __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

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:

{
    '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:

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

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

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)

# 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