Metadata-Version: 2.4
Name: streamlit-pydantic-object-builder
Version: 0.1.0
Summary: Streamlit custom component to build and edit Pydantic objects with a drag-and-drop UI
Author-email: Andrew Ferguson <contact@edinburghdata.tech>
License-Expression: MIT
Project-URL: Homepage, https://pypi.org/project/streamlit-pydantic-object-builder/
Project-URL: Bug Tracker, https://pypi.org/project/streamlit-pydantic-object-builder/
Keywords: streamlit,pydantic,drag-and-drop,editor,form
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: Software Development :: User Interfaces
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: streamlit>=1.62
Requires-Dist: pydantic>=2
Provides-Extra: devel
Requires-Dist: wheel; extra == "devel"
Requires-Dist: pytest>=8; extra == "devel"
Requires-Dist: build; extra == "devel"
Dynamic: license-file

# streamlit-pydantic-object-builder

[![PyPI](https://img.shields.io/pypi/v/streamlit-pydantic-object-builder.svg)](https://pypi.org/project/streamlit-pydantic-object-builder/)
[![Python](https://img.shields.io/pypi/pyversions/streamlit-pydantic-object-builder.svg)](https://pypi.org/project/streamlit-pydantic-object-builder/)
[![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)

Streamlit custom component for building and editing [Pydantic v2](https://docs.pydantic.dev/) objects with a drag-and-drop UI.

The widget returns a **validated Pydantic model instance** (or `None` while the draft is invalid). JSON is only used as the component wire format.

## Install

```sh
pip install streamlit-pydantic-object-builder
```

Requires **Python ≥ 3.10**, **Streamlit ≥ 1.62**, and **Pydantic ≥ 2**.

## Quick start

```python
import streamlit as st
from pydantic import BaseModel
from streamlit_pydantic_object_builder import pydantic_object_builder

class Hair(BaseModel):
    color: str
    length: str

class Person(BaseModel):
    name: str
    age: int
    hair: Hair | None = None

result = pydantic_object_builder(
    edit_object=Person(name="Rhubarb", age=41, hair=Hair(color="red", length="short")),
    library=[Hair(color="brown", length="long")],
    label_fields={Hair: "color"},
    return_on_change=True,
    key="person_editor",
)

if result is None:
    st.warning("Draft does not currently validate")
else:
    st.json(result.model_dump(mode="json"))
```

Pass a **model class** instead of an instance to start from defaults / empty placeholders.

## API

```python
pydantic_object_builder(
    edit_object,
    library=None,
    *,
    return_on_change=True,
    save_button_position="bottom-left",
    label_fields=None,
    key=None,
    disabled=False,
    hide_library=False,
    min_height=360,
    on_change=None,
    on_save=None,
    seed=None,
) -> BaseModel | None
```

| Parameter | Description |
|-----------|-------------|
| `edit_object` | Initial Pydantic instance, or a model class to start blank / from defaults. Applied once per `key`/`seed`, not on every rerun |
| `library` | Instances to drag in. `None` (default) hides the library pane. `[]` shows blank “New {Type}” cards only |
| `return_on_change` | `True`: return a validated model on each change (debounced), or `None` while invalid. `False`: show a Save button and return the last successful save |
| `save_button_position` | `top`, `bottom`, `top-left`, `top-right`, `bottom-left`, or `bottom-right` |
| `label_fields` | Map of model type → field name used for compact labels and breadcrumbs |
| `key` | Streamlit key when more than one editor is on the page |
| `disabled` | Read-only editor |
| `hide_library` | Hide the library pane even when `library` is a list |
| `min_height` | Minimum host height in pixels (the component grows with content) |
| `on_change` | Callback when the live return value changes |
| `on_save` | Callback after a successful Save |
| `seed` | Change this to re-initialize the editor from `edit_object` |
| **returns** | Validated `BaseModel`, last saved snapshot, or `None` |

`edit_object` is the initial value for a given `key` (like other Streamlit widgets). To load a different object into the same editor, pass a new `seed` or `key`.

## Features

- Nested models with compact summaries, click-to-expand, pencil to open a nested view, Expand all / Collapse all, Undo / Reset
- Library pane: search, accordion by type, blank “New {Type}” cards, copy-on-drop
- Lists and sets: reorder, insert (`+`), duplicate, trash-to-remove, drop-to-replace
- Enums, `Literal`, date / time / datetime, dict, tuples, unions (including discriminators)
- `UUID`, `Decimal`, `EmailStr` / `HttpUrl` (when the matching Pydantic extras are installed)
- `Field` metadata: description, examples, numeric and string constraints
- Inline validation errors; unsupported types shown read-only
- Streamlit theme tokens and SVG icons

## Development

Clone the repo, then:

```sh
python -m pip install -e ".[devel]"
cd streamlit_pydantic_object_builder/frontend
npm install
npm run build
```

If your environment uses TLS interception, `npm install --strict-ssl=false` may be required.

```sh
streamlit run example.py
pytest -q
cd streamlit_pydantic_object_builder/frontend && npm test
```

During frontend development, run `npm run dev` in `frontend/` and `streamlit run example.py` in the repo root.

Optional Playwright smoke (demo must be running):

```sh
cd streamlit_pydantic_object_builder/frontend
npx playwright install chromium
E2E=1 npx playwright test
```

Frontend assets are built into the wheel; they are not committed.

```sh
cd streamlit_pydantic_object_builder/frontend
npm ci
npm run build
cd ../../
python -m build
```
