Metadata-Version: 2.4
Name: sistine
Version: 0.3.4
Summary: HTML builder with server routing for Python
Author: Ararya Arka Anugraha
License-Expression: MIT
Project-URL: Homepage, https://github.com/Araryarch/sistine/blob/main/docs.md
Project-URL: Repository, https://github.com/Araryarch/sistine
Keywords: html,web,ssr
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Requires-Python: >=3.10
Description-Content-Type: text/markdown
Requires-Dist: starlette>=0.40.0
Requires-Dist: uvicorn>=0.30.0
Requires-Dist: streamlit>=1.28.0
Requires-Dist: requests>=2.28.0

# Sistine

<img width="511" height="288" alt="download" src="https://github.com/user-attachments/assets/76e85ab4-23be-4d78-8170-8065f143e108" />

> React-like UI framework for Python. Build reactive web UIs with HTML/Tailwind + Alpine.js, powered by Streamlit.

## Installation

```bash
pip install sistine
```

## Quick Start

```python
from sistine import Sistine, el, fetch

app = Sistine(title="My App")
app.use_tailwind()

@app.sistine("/")
def home():
    return str(
        el.div(cls="min-h-screen bg-gray-50 p-10 flex flex-col items-center")(
            el.h1(cls="text-4xl font-extrabold text-blue-600 mb-8")("Hello Sistine!"),
            el.p(cls="text-gray-600")("Welcome to the future of Python web UI.")
        )
    )

if __name__ == "__main__":
    app.run()
```

```bash
python app.py
```

## Reactive Components (`sistine.components`)

Sistine provides ready-to-use components with **Alpine.js** for client-side interactivity — no page reloads needed.

### QueryTable — Auto-generated table from API

```python
from sistine.components import QueryTable

QueryTable(
    query="fetch_pokemon",
    columns=["id", "name"],
    params={"limit": 10},
)
```

### QueryDropdown — Select populated from API

```python
from sistine.components import QueryDropdown

QueryDropdown(
    query="fetch_types",
    label_key="name",
    placeholder="Pilih Tipe Pokémon...",
)
```

### QueryView — Reactive data display with auto-refresh

```python
from sistine.components import QueryView

QueryView(
    query="server_status",
    refetch_interval=5,  # auto-refresh every 5s
    children=el.div(cls="text-lg font-bold", x_text="data.cpu")(),
)
```

### QueryList — Iterate over API data

```python
from sistine.components import QueryList

QueryList(
    query="fetch_berries",
    iterator="berry in data",
    template=el.span(cls="badge", x_text="berry.name")(),
)
```

### QueryMutation — POST/PUT/DELETE with confirm dialog

```python
from sistine.components import QueryMutation

QueryMutation(
    query="add_favorite",
    body_js='{ name: "Pikachu" }',
    confirm="Add to favorites?",
    trigger=el.button(cls="bg-yellow-400 text-white px-4 py-2 rounded")("⭐ Favorite"),
)
```

### RefetchButton — Manual refresh

```python
from sistine.components import RefetchButton

RefetchButton(target="fetch_pokemon", label="🔄 Refresh")
```

## Server Queries (`@app.query`)

Register Python functions as JSON endpoints — callable from browser via `sistine.fetch()`:

```python
from sistine import Sistine, el, fetch

app = Sistine()

@app.query
def fetch_pokemon(limit=10):
    data = fetch(f"https://pokeapi.co/api/v2/pokemon?limit={limit}")
    return data.get("results", [])

@app.query(ttl=60)  # cached for 60s
def get_prices():
    return fetch("https://api.example.com/prices")

@app.sistine("/")
def home():
    return str(
        QueryTable(query="fetch_pokemon", columns=["name"], params={"limit": 5})
    )
```

## Routing

```python
@app.sistine("/")
def home():
    return str(el.h1("Home"))

@app.sistine("/user/{user_id}")
def profile(user_id: int):
    return str(el.h1(f"User {user_id}"))
```

Navigate via `?route=/user/42` query param, or use `el.a(href="/?route=/user/42")`.

## HTML Builder (`el`)

Chaining syntax — attributes first, children second:

```python
el.div(cls="card p-4 rounded shadow")(
    el.h2(cls="text-xl font-bold")("Title"),
    el.p(cls="text-gray-600")("Description")
)
```

## CSS & Tailwind

```python
app.use_tailwind()
app.css("""
    .custom-class { animation: fadeIn 0.3s ease; }
""")
```

## Examples

Check [`examples/query_showcase.py`](examples/query_showcase.py) for a complete demo of all query components.
