nicegui consolidation

This commit is contained in:
John Lancaster
2026-07-29 23:37:48 -05:00
parent bc0d6ede49
commit 8f26051a52
9 changed files with 242 additions and 435 deletions
@@ -0,0 +1,77 @@
# Architecture and Styling Reference
## Project Boundaries
Use this dependency direction:
- pages import components and services
- components contain presentation logic only
- services contain business logic and do not import UI
- static assets are mounted and loaded once at bootstrap
Suggested module split:
```text
src/app/
ui/pages/
ui/components/
ui/static/
services/
api/
bootstrap.py
```
## Component Extraction Rules
Extract to ui/components when a pattern appears in two or more pages.
Keep in-page if the layout is specific to a single route.
```python
def card_section(title: str, content: str) -> ui.card:
with ui.card().classes("w-full max-w-md") as card:
ui.label(title).classes("text-lg font-bold")
ui.label(content).classes("text-gray-600")
return card
```
## Tailwind-First Layout Pattern
Use Tailwind utility classes for structure and spacing.
Use breakpoint classes for responsive behavior.
Use .style() only for values that must be computed dynamically.
```python
with ui.column().classes("w-full"):
with ui.row().classes("w-full gap-4 flex-wrap sm:flex-nowrap"):
ui.card().classes("flex-1 min-w-64")
ui.card().classes("flex-1 min-w-64")
```
## Styling Decision Order
1. Tailwind utility classes
2. Quasar props
3. Reusable styled component functions
4. Minimal custom CSS loaded once at bootstrap (only when needed)
```python
from fastapi.staticfiles import StaticFiles
app.mount("/static", StaticFiles(directory="src/app/static"), name="static")
ui.add_css(open("src/app/static/css/base.css").read())
```
## Static Asset Rules
- Keep custom CSS small and tokenized with variables.
- Avoid per-page CSS injection.
- Verify static mount paths and reverse proxy rewrites.
## Links
!!! info "Primary sources"
- [NiceGUI elements](https://nicegui.io/documentation/element)
- [NiceGUI binding properties](https://nicegui.io/documentation/section_binding_properties)
- [Tailwind utility-first styling](https://tailwindcss.com/docs/utility-first)
- [Quasar components](https://quasar.dev/vue-components)
+89 -46
View File
@@ -1,32 +1,75 @@
# NiceGUI Architecture Reference
# NiceGUI Application Architecture
This reference expands the workflow in the main skill file and is loaded only when needed.
Load this reference for application composition, package boundaries, and optional subsystem decisions.
## Baseline package boundaries
## Baseline Package Boundaries
- `main.py`: process entrypoint only.
- `bootstrap.py`: app composition, router wiring, page registration, lifespan orchestration.
- `config.py`: typed settings and env parsing.
- `main.py`: process entry point and app factory exposure.
- `bootstrap.py`: app composition, router wiring, page registration, and lifespan orchestration.
- `config.py`: typed settings and environment parsing.
- `logging.py`: centralized logging setup.
- `api/`: HTTP transport layer; delegates to services.
- `services/`: business/use-case logic.
- `api/`: HTTP transport that delegates to services.
- `services/`: business and use-case logic.
- `ui/pages/`: route-level NiceGUI pages.
- `ui/components/`: shared UI building blocks.
- `ui/components/`: shared presentation building blocks.
## Required baseline behavior
Recommended base shape:
```text
.
├─ pyproject.toml
├─ .env.example
├─ src/
│ └─ app/
│ ├─ __init__.py
│ ├─ main.py
│ ├─ bootstrap.py
│ ├─ config.py
│ ├─ logging.py
│ ├─ api/
│ │ ├─ __init__.py
│ │ └─ health.py
│ ├─ services/
│ │ ├─ __init__.py
│ │ └─ example_service.py
│ └─ ui/
│ ├─ __init__.py
│ ├─ components/
│ │ ├─ __init__.py
│ │ └─ nav.py
│ └─ pages/
│ ├─ __init__.py
│ ├─ home.py
│ ├─ dashboard.py
│ └─ about.py
└─ tests/
├─ test_health.py
└─ test_pages_registration.py
```
## Required Baseline Behavior
- FastAPI is the base ASGI app.
- NiceGUI pages are modular and registered from page modules.
- Minimum pages: `/`, `/dashboard`, `/about`.
- FastAPI health route: `/healthz`.
- Lifespan handles startup/shutdown resources.
- No global side effects at import time.
- `create_app()` composes routes, resources, and NiceGUI.
- Lifespan owns startup and shutdown resources.
- NiceGUI pages are modular and explicitly registered.
- FastAPI exposes a health route such as `/healthz`.
- Imports do not trigger runtime global side effects.
## Optional extension: Database
## Dependency Direction
Use only if persistence is required.
Prefer:
Suggested additions:
- `main/bootstrap` -> `config/logging` + `api` + `ui/pages` + `services`
- `api` -> `services`
- `ui/pages` -> `ui/components` + `services`
- `services` -> helpers, clients, and `db/` when enabled
Avoid imports from services back into API or UI modules.
## Optional Persistence
Use only when the product requires durable data.
```text
src/app/db/
@@ -37,19 +80,15 @@ src/app/db/
└─ repositories/
```
Guidelines:
- Create one engine and sessionmaker per process.
- Provide request- or operation-scoped sessions with `yield`.
- Keep transaction boundaries explicit in service or repository flows.
- Never share sessions across concurrent tasks.
- Use Alembic as the schema migration source of truth.
- One engine and one sessionmaker per process.
- Request-scoped session dependency using `yield`.
- Explicit transaction boundaries in service/repository flows.
- Avoid shared sessions across concurrent tasks.
- Use Alembic as schema source of truth.
## Optional LangGraph AI
## Optional extension: LangGraph AI
Use only for multi-step AI orchestration or human-in-the-loop workflows.
Suggested additions:
Use only for multi-step orchestration, resumable work, streaming, or human approval.
```text
src/app/ai/
@@ -60,33 +99,37 @@ src/app/ai/
└─ contracts.py
```
Guidelines:
- Keep graph internals outside API/UI modules.
- Invoke graph through `services/ai_service.py`.
- Use stable thread/session IDs for resumable sessions.
- Keep graph internals outside API and UI modules.
- Invoke graphs through a service such as `services/ai_service.py`.
- Use stable thread or session IDs for resumable flows.
- Keep interrupt payloads JSON-serializable.
## Optional extension: Mounted static docs
## Optional Mounted Docs
Use only when generated docs should be served in-app.
Use only when generated docs must be served by the application.
Suggested settings:
- `docs_enabled`
- `docs_mount_path`
- `docs_site_dir`
- `docs_require_build` (optional)
- `docs_require_build`
Guidelines:
Mount docs in the composition layer, normalize the mount path, avoid route conflicts, and define behavior for missing build artifacts.
- Mount docs in composition layer (`bootstrap.py`).
- Normalize mount path and avoid route conflicts.
- Warn on missing build artifacts unless strict mode is enabled.
## Async And Responsiveness
## Suggested output quality criteria
- Use `async def` where a handler or service path performs I/O.
- Prefer non-blocking clients and libraries.
- Offload CPU-heavy work to worker or background execution.
- Define progress, cancellation, timeout, completion, and error states for long actions.
- Stream or chunk results when workflows are long-running or multi-step.
- Clear architecture summary with assumptions.
- Explicit decisions for DB, AI, and docs.
- Package-scoped implementation checklist.
- Minimal test plan aligned to enabled features.
## Testing Minimums
- Test the FastAPI health route.
- Test page registration wiring.
- If persistence is enabled, test session lifecycle and rollback behavior.
- If AI is enabled, test happy paths and interrupt/resume behavior.
- If docs are enabled, test the mounted index route.
- For long actions, test loading, completion, and error states.
@@ -1,119 +1,100 @@
# Binding Dataclasses Deep Dive
This reference explains how to model state with NiceGUI bindable dataclasses and how to avoid common update and performance pitfalls.
Use this reference to model NiceGUI state with bindable dataclasses and avoid common propagation and performance pitfalls.
## Primary Sources
- NiceGUI binding docs: [Binding properties](https://www.nicegui.io/documentation/section_binding_properties)
- NiceGUI binding docs: [binding properties](https://www.nicegui.io/documentation/section_binding_properties)
- Python dataclass docs: [dataclasses module](https://docs.python.org/3/library/dataclasses.html)
- Data class design rationale: [PEP 557](https://peps.python.org/pep-0557/)
## What bindable_dataclass changes
## Bindable Dataclass Behavior
`@binding.bindable_dataclass` extends standard dataclasses by turning fields into bindable properties so UI bindings can propagate immediately when a field is assigned.
Baseline pattern:
`@binding.bindable_dataclass` extends standard dataclasses by turning fields into bindable properties, allowing UI bindings to propagate when a field is assigned.
```python
from nicegui import binding, ui
@binding.bindable_dataclass
class Profile:
name: str = 'Ada'
name: str = "Ada"
age: int = 37
profile = Profile()
ui.input('Name').bind_value(profile, 'name')
ui.number('Age', min=0).bind_value(profile, 'age')
ui.label().bind_text_from(profile, 'name', backward=lambda n: f'User: {n}')
ui.input("Name").bind_value(profile, "name")
ui.number("Age", min=0).bind_value(profile, "age")
ui.label().bind_text_from(profile, "name", backward=lambda name: f"User: {name}")
```
## Propagation model and performance
## Propagation And Performance
NiceGUI distinguishes between two link types:
- Bindable properties: efficient, event-like propagation on assignment.
- Active links: polled in a refresh loop (default every 0.1s).
- Bindable properties propagate efficiently when values are assigned.
- Active links are checked in a refresh loop.
Practical implications:
Prefer bindable dataclasses for frequently updated form state. Keep binding transforms pure and inexpensive. If an application has many active links, tune `binding_refresh_interval` in `ui.run(...)` only after measuring the impact.
- Prefer bindable dataclasses for frequently updated form state.
- Keep transform functions pure and side-effect free.
- If many active links exist, tune `binding_refresh_interval` in `ui.run(...)` carefully.
## Dataclass modeling rules that matter for binding
## Dataclass Modeling Rules
- Use `field(default_factory=...)` for mutable defaults.
- Avoid `frozen=True` for models that should be edited from UI controls.
- Use `slots=True` only when you have confirmed compatibility with your inheritance and extension needs.
- Avoid `frozen=True` for models edited by UI controls.
- Use `slots=True` only after confirming compatibility with inheritance and extension needs.
- Keep UI-editable fields explicit and typed.
Example with safe mutable defaults:
```python
from dataclasses import field
from nicegui import binding
@binding.bindable_dataclass
class Filters:
query: str = ''
query: str = ""
tags: list[str] = field(default_factory=list)
```
## Nested structures and binding paths
## Nested Structures
NiceGUI supports nested key paths via tuples for nested data (for example dictionaries and nested structures).
NiceGUI supports tuple paths for nested data structures.
```python
from nicegui import ui
data = {'user': {'name': 'Ada'}}
data = {"user": {"name": "Ada"}}
ui.input('Name').bind_value(data, ('user', 'name'))
ui.label().bind_text_from(data, ('user', 'name'))
ui.input("Name").bind_value(data, ("user", "name"))
ui.label().bind_text_from(data, ("user", "name"))
```
When using nested dataclasses, keep updates explicit and predictable at the field level.
Keep nested dataclass updates explicit and predictable at the field level.
## Strictness and refactor safety
Binding can warn when attributes do not exist.
## Strictness And Refactor Safety
- Object attributes are checked by default.
- Dictionary keys are not checked by default.
- Use `strict=True` when you want missing-key warnings for dict-backed state.
- Use `strict=True` when missing dictionary keys should produce warnings.
```python
from nicegui import app, ui
ui.input().bind_value(app.storage.user, 'display_name', strict=True)
ui.input().bind_value(app.storage.user, "display_name", strict=True)
```
## Common pitfalls and safer alternatives
## Common Pitfalls
- Pitfall: mutating nested mutable values in place and expecting immediate UI sync.
- Safer alternative: assign back to the bound field after updates so change propagation is explicit.
- In-place mutation may not produce immediate UI synchronization. Assign the updated value back to the bound field.
- Heavy binding transforms can degrade refresh performance. Move expensive work to event handlers or services.
- State shared across unrelated pages or users can leak data. Scope models to the appropriate page, client, or user context.
- Pitfall: heavy transform functions in bindings.
- Safer alternative: keep transformations cheap and deterministic; move heavy work to event handlers.
## Version Checks
- Pitfall: one model shared across unrelated pages or users.
- Safer alternative: scope model instances to page/client/user context as needed.
- `bindable_dataclass` was added in NiceGUI 2.11.0.
- Depth-first binding propagation was documented in NiceGUI 2.16.0.
- Binding `strict` behavior was documented in NiceGUI 3.0.0.
- Tuple paths for nested properties were documented in NiceGUI 3.10.0.
## Version notes to remember
- `bindable_dataclass` added in NiceGUI 2.11.0.
- Binding `strict` behavior documented as added in NiceGUI 3.0.0.
- Tuple paths for nested properties documented as added in NiceGUI 3.10.0.
- Depth-first binding propagation update documented in NiceGUI 2.16.0.
Verify behavior against the NiceGUI version pinned in your project before relying on version-specific semantics.
## Quick checklist
- Choose bindable dataclasses for interactive form-like state.
- Use `default_factory` for mutable fields.
- Keep transform functions pure.
- Use strict mode intentionally.
- Re-check version notes before migration work.
Verify these behaviors against the NiceGUI version pinned by the target project.
@@ -0,0 +1,110 @@
# Interaction Patterns Reference
## Reactive State
Use bindable dataclasses for local page state.
```python
from dataclasses import field
from nicegui import binding, ui
@binding.bindable_dataclass
class PageState:
selected_id: int | None = None
items: list = field(default_factory=list)
state = PageState()
ui.label().bind_text_from(state, "selected_id")
```
## File Upload Pattern
- Validate extension and size before storing.
- Delegate storage to a service method.
- Notify success and failure explicitly.
```python
async def handle_upload(e: ui.events.UploadEventArguments):
try:
if e.size > 10 * 1024 * 1024:
raise ValueError("File too large")
if not e.name.endswith(".pdf"):
raise ValueError("Only PDF allowed")
await file_service.store(e.content.read(), e.name)
ui.notify(f"Uploaded: {e.name}", type="positive")
except ValueError as err:
ui.notify(str(err), type="negative")
ui.upload(on_upload=handle_upload, auto_upload=True)
```
## Form Submission Pattern
- Bind UI inputs to dataclass fields.
- Perform validation in the service layer.
- Clear form state on success.
```python
@binding.bindable_dataclass
class FormData:
name: str = ""
email: str = ""
data = FormData()
ui.input("Name").bind_value(data, "name")
ui.input("Email").bind_value(data, "email")
async def on_submit():
try:
await user_service.create_user(name=data.name, email=data.email)
ui.notify("User created", type="positive")
data.name = data.email = ""
except ValueError as err:
ui.notify(str(err), type="negative")
ui.button("Submit").on_click(on_submit)
```
## Real-Time Updates Decision
Use SSE for one-way status streaming.
Use WebSocket for bidirectional messaging.
SSE endpoint example:
```python
@app.get("/events/status")
async def status_stream():
async def gen():
while True:
yield f"data: {await get_status()}\\n\\n"
await asyncio.sleep(1)
return StreamingResponse(gen(), media_type="text/event-stream")
```
## Background Work Pattern
- Start long jobs in FastAPI background tasks.
- Expose status via endpoint or streaming channel.
- Guard buttons against duplicate submissions during in-flight tasks.
## Explicit Refresh Pattern
Use @ui.refreshable and call refresh intentionally instead of polling unrelated state.
```python
@ui.refreshable
async def item_list():
items = await service.list()
for item in items:
ui.label(item.name)
ui.button("Refresh").on_click(lambda: item_list.refresh())
```
## Links
!!! info "Primary sources"
- [NiceGUI action events](https://nicegui.io/documentation/section_action_events)
- [FastAPI server-sent events](https://fastapi.tiangolo.com/advanced/server-sent-events/)
- [FastAPI WebSockets](https://fastapi.tiangolo.com/advanced/websockets/)
@@ -1,6 +1,14 @@
# Source Documentation
Use these links for framework-specific details.
Use these links to verify framework-specific behavior before relying on version-sensitive or integration-specific guidance.
## NiceGUI
!!! info "NiceGUI sources"
- [Pages, routing, and FastAPI integration](https://www.nicegui.io/documentation/section_pages_routing)
- [Binding properties and bindable dataclasses](https://www.nicegui.io/documentation/section_binding_properties)
- [Action events](https://www.nicegui.io/documentation/section_action_events)
- [Security best practices](https://www.nicegui.io/documentation/section_security)
## FastAPI
@@ -8,40 +16,34 @@ Use these links for framework-specific details.
- [Lifespan events](https://fastapi.tiangolo.com/advanced/events/)
- [Settings and environment variables](https://fastapi.tiangolo.com/advanced/settings/)
- [Dependencies with yield](https://fastapi.tiangolo.com/tutorial/dependencies/dependencies-with-yield/)
- [SQL databases tutorial](https://fastapi.tiangolo.com/tutorial/sql-databases/)
- [Server-sent events](https://fastapi.tiangolo.com/advanced/server-sent-events/)
- [WebSockets](https://fastapi.tiangolo.com/advanced/websockets/)
## SQLAlchemy and Alembic
## Styling
!!! info "Styling sources"
- [Tailwind utility-first styling](https://tailwindcss.com/docs/utility-first)
- [Quasar components](https://quasar.dev/vue-components)
## Persistence
!!! info "Persistence sources"
- [SQLAlchemy engine configuration and pooling](https://docs.sqlalchemy.org/en/20/core/engines.html)
- [SQLAlchemy session lifecycle basics](https://docs.sqlalchemy.org/en/20/orm/session_basics.html)
- [SQLAlchemy session lifecycle](https://docs.sqlalchemy.org/en/20/orm/session_basics.html)
- [Alembic tutorial](https://alembic.sqlalchemy.org/en/latest/tutorial.html)
## Pydantic
## Configuration And Dataclasses
!!! info "Pydantic source"
- [Pydantic settings management](https://pydantic.dev/docs/validation/latest/concepts/pydantic_settings/)
## NiceGUI
!!! info "NiceGUI sources"
- [Pages, routing, and FastAPI integration](https://www.nicegui.io/documentation/section_pages_routing)
- [Binding properties and bindable dataclass](https://www.nicegui.io/documentation/section_binding_properties)
- [Security best practices](https://www.nicegui.io/documentation/section_security)
## Python Dataclasses
!!! info "Python sources"
- [dataclasses module reference](https://docs.python.org/3/library/dataclasses.html)
!!! info "Python and Pydantic sources"
- [Pydantic settings management](https://docs.pydantic.dev/latest/concepts/pydantic_settings/)
- [Python dataclasses](https://docs.python.org/3/library/dataclasses.html)
- [PEP 557: Data Classes](https://peps.python.org/pep-0557/)
## LangGraph
!!! info "LangGraph sources"
- [Overview](https://docs.langchain.com/oss/python/langgraph/overview)
- [Quickstart](https://docs.langchain.com/oss/python/langgraph/quickstart)
- [Workflows and agents](https://docs.langchain.com/oss/python/langgraph/workflows-agents)
- [Persistence](https://docs.langchain.com/oss/python/langgraph/persistence)
- [Memory concepts](https://docs.langchain.com/oss/python/concepts/memory)
- [Streaming](https://docs.langchain.com/oss/python/langgraph/streaming)
- [Interrupts and human-in-the-loop](https://docs.langchain.com/oss/python/langgraph/interrupts)
@@ -0,0 +1,39 @@
# Troubleshooting and Quality Gates
## Troubleshooting
### Upload Errors
- Validate extension and size before storage.
- Catch expected exceptions and return negative notifications.
- Log unexpected exceptions with request context.
### UI Race Conditions
- Disable triggering controls during async work.
- Remove duplicate timers and listeners targeting the same state.
- Ensure service call ordering is deterministic before render updates.
### Asset Caching
- Confirm static mount and proxy rewrite correctness.
- Add cache-busting query strings for changed assets.
- Avoid per-page CSS injection.
### Navigation and State Drift
- Avoid global mutable UI state.
- Keep state request-scoped or service-managed.
- Rehydrate page data during route load.
## Production Readiness Gate
Pass all checks before shipping:
- Structure: one-way dependencies between pages, components, and services.
- Responsiveness: UI validated at both small and large viewport widths.
- Accessibility: labels and actions are clear and readable.
- Reliability: validation and exception paths surface user feedback.
- Maintainability: repeated UI patterns are extracted; business logic remains in services.
If any check fails, return to the workflow step that owns that concern.