- Read
STATUS.md— current implementation status and what's pending - Read parent
../migrate-flow-specs/CLAUDE.md— project overview, architecture, specs index - For protocol details:
../migrate-flow-specs/specs/PROTOCOL.md,../migrate-flow-specs/specs/RPC.md,../migrate-flow-specs/specs/PITFALLS.md - For component roadmap:
../migrate-flow-specs/specs/COMPONENTS.md
- One logical change per commit. Never mix unrelated changes (e.g. new feature + refactor + bug fix) in a single commit.
- Tests must pass at each commit.
- Commit message: concise, describes the "why" not the "what".
# Run the app (auto-detects demo/ module)
source .venv/bin/activate && python -m demo
# http://localhost:8088
# Or via CLI: pyxflow demo --port 8088
# Or auto-detect: pyxflow --port 8088 (finds demo/views/ automatically)
# Run unit tests (default — runs tests/unit/)
pytest -v
# Run ALL tests (unit + UI — auto-starts server if needed)
pytest --all -v
# Run specific test file
pytest tests/unit/test_rpc_events.py -v
# Run UI tests only (auto-starts server if none running on :8088)
pytest tests/ui/ -v # headless
pytest tests/ui/ --headed -v # visible browser
# Regenerate the frontend bundle (auto-discovers _v_fqcn components)
pyxflow --bundle
# Run Java reference (flow-components)
cd ../migrate-flow-specs/flow-components && ./mvnw package jetty:run-war
# http://localhost:8080pyxflow/
├── src/pyxflow/
│ ├── core/ # StateTree, StateNode, Element, Component
│ ├── components/ # Button, TextField, Grid, Dialog, etc. (50+ components)
│ ├── data/ # Binder, DataProvider, validators, converters
│ ├── server/ # HTTP server (aiohttp), UIDL handler
│ ├── main.py # FlowApp + CLI entry point
│ └── resources/ # setup_app, generate_bundle, scaffold templates
├── demo/ # Demo app + __main__.py entry point
│ ├── views/ # Demo views (9 routes)
│ ├── static/ # Static assets (images, CSS) served at /images/*, /styles/*
│ └── services/ # PeopleService (data access layer)
├── tests/
│ ├── views/ # 29 test views with TestMainLayout (independent app, python -m tests)
│ ├── unit/ # 2457 unit tests (default pytest target)
│ └── ui/ # 446 Playwright UI tests (run explicitly via pytest tests/ui/)
└── STATUS.md # Implementation progress
../migrate-flow-specs/flow-components/ # Java project → frontend bundle
| File | Purpose |
|---|---|
server/uidl_handler.py |
UIDL protocol: init, navigation, events, mSync, publishedEventHandler |
server/http_server.py |
aiohttp server, sessions, static files, upload |
core/state_tree.py |
Node management, change tracking, execute queue |
core/state_node.py |
Features, attach/put/splice, change collection |
core/element.py |
Element wrapper: properties, attributes, styles, events |
core/component.py |
Base Component class with element attachment, _BufferedStyle, deferred execute_js |
components/grid.py |
Grid: connectors, renderers, sorting, lazy loading (~600 lines) |
components/dialog.py |
Dialog: FlowComponentHost, publishedEventHandler close |
- Check Java implementation in
../migrate-flow-specs/flow-components/ - Check complexity/connector info in
../migrate-flow-specs/specs/COMPONENTS.md - Get API:
mcp__Vaadin__get_component_java_api - Create
components/new_component.py - Export in
components/__init__.py - Add tests in
tests/ - Add
_v_fqcnclass attribute with the Java FQCN (e.g._v_fqcn = "com.vaadin.flow.component.button.Button") - Regenerate bundle:
pyxflow --bundle(auto-discovers all_v_fqcncomponents)
PyXFlow computes event hashes dynamically using compute_event_hash(config) in server/uidl_handler.py. The function uses base64(sha256(BOM + json.encode('utf-16-be'))[:8]) -- the same algorithm as Java Flow.
All hash constants are computed at module load from their config dicts (e.g. _CLICK_HASH = compute_event_hash(_CLICK_CONFIG)). No hardcoded hash strings -- if a config changes, the hash updates automatically.
Hash constants are centralized in server/uidl_handler.py. Component files import from there.
When the user asks "qué falta" / "what's missing" / "what's pending", perform a gap analysis covering:
- Unimplemented features — Check
STATUS.md§ "What's Missing" → "Unimplemented Features" (PWA, Security, etc.) - Missing API methods — Check
STATUS.API.mdfor[ ]markers. Count[x]vs[ ]for coverage %. - Missing tests — Compare unit test count (
pytest --co -q | tail -1) and UI test count (pytest tests/ui/ --co -q | tail -1) againstSTATUS.md. Checktests/ui/SPECS.mdfor pending UI test scenarios. - LOC — Run
find src -name '*.py' | xargs wc -l | tail -1(implementation) andfind tests -name '*.py' | xargs wc -l | tail -1(tests). Demo code is not counted.
After ANY implementation work, update these files:
STATUS.md— Update test counts, LOC, move items from "Missing" to "Implemented", update API coverage ratioSTATUS.API.md— Flip[ ]→[x]for newly implemented methods, update header counts- Keep the "What's Missing" section in STATUS.md current — it's the single source of truth for gap analysis
Use MCP Playwright tools to verify UI:
browser_navigate → http://localhost:8088 (Python) or :8080 (Java)
browser_snapshot → get element refs
browser_click → interact
browser_take_screenshot → visual check
The Vaadin frontend bundle (FlowClient, web components, Lumo/Aura themes) is pre-built and shipped inside the wheel at src/pyxflow/bundle/. At runtime, the server discovers it automatically.
From the pyxflow/ checkout:
pyxflow --bundle # build → src/pyxflow/bundle/
pyxflow --bundle --keep # keep bundle-project/ for debugging
pyxflow --bundle --vaadin-version 25.1.0 # pin a different Vaadin versionThe command auto-discovers all Component subclasses with _v_fqcn, generates a Maven project with @Uses for each, builds in production mode, and extracts the bundle from the WAR.
If bundle-project/ already exists (from a previous --keep), it reuses the Maven project and skips mvn clean for faster rebuilds.
Users can generate their own bundle to include only the components they use, or to add custom web components:
# From the project root (where my_app/ lives)
pyxflow my_app --bundle # build → my_app/bundle/
pyxflow my_app --bundle --keep # keep my_app/bundle-project/The generated my_app/bundle/ takes priority over the package-internal bundle at runtime. This allows projects to:
- Use a different Vaadin version than the one shipped with pyxflow
- Include additional web components (by adding
_v_fqcnto custom components) - Reduce bundle size by only including used components (future)
get_bundle_directory() checks these locations in order:
<app_directory>/bundle/— user-project bundle (highest priority)<package>/bundle/— package-internal (shipped in wheel)./bundle/— development fallback