Metadata-Version: 2.4
Name: htag
Version: 2.0.25
Summary: Python3 GUI toolkit for building 'beautiful' applications for mobile, web, and desktop from a single codebase
Author: manatlan
License: MIT
Project-URL: Homepage, https://github.com/manatlan/htag
Project-URL: Repository, https://github.com/manatlan/htag.git
Project-URL: Bug Tracker, https://github.com/manatlan/htag/issues
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Topic :: Software Development :: User Interfaces
Requires-Python: >=3.10
Description-Content-Type: text/markdown
Requires-Dist: starlette>=0.40.0
Requires-Dist: uvicorn>=0.40.0
Requires-Dist: websockets>=16.0

# htag 2

<p align="center">
  <img src="https://manatlan.github.io/htag/assets/logo.png" width="300" alt="htag logo">
</p>

<p align="center">
  <a href="https://pypi.org/project/htag/">
    <img src="https://img.shields.io/pypi/v/htag?style=for-the-badge&logo=pypi&color=blue" alt="PyPI">
  </a>
  <a href="https://github.com/manatlan/htag/actions/workflows/tests.yml">
    <img src="https://img.shields.io/github/actions/workflow/status/manatlan/htag/tests.yml?branch=main&style=for-the-badge&logo=github&label=Tests" alt="Test Status">
  </a>
  <a href="https://github.com/manatlan/htag/releases/tag/latest">
    <img src="https://img.shields.io/badge/Download-Latest_Build-blue?style=for-the-badge&logo=github" alt="Download Latest Build">
  </a>
</p>

**htag** is a Python3 GUI toolkit for building "beautiful" applications for mobile, web, and desktop from a single codebase


Here is a full rewrite of **htag 1.0.0**, using only antigravity and prompts/intentions. It's the future of **htag**.

Currently, it works ...

- For building desktop apps (ChromeApp on linux/windows/mac, or simple WebApp)
- For building web apps (WebApp as an asgi app (for starlette/fastapi/...))
- For building SPA HTML Page (with the PyScript runner)
- For building android apps ([it works](examples/app_android/README.md), **but need to improve**)

## Major differences between v1 and v2

- A lot simpler & more reactive
- `Tag.App` is the main app component
- Full starlette compliant from the ground
- Websocket communication, fallback to HTTP SSE, and ultimately fallback to Pure HTTP
- No more generic Runner
- State object for reactivity
- DX: errors are a lot better handled/viewable, hot-reload available
- Better events
- less boilerplate (kiss minded)
- Styles can be scoped by component
- ...


## Get Started

Check the [Official V2 Documentation](https://manatlan.github.io/htag/) for more information. [old v1 docs](https://manatlan.github.io/htag/v1/)


## Install

```bash
uv add htag -U
```

Or using pip:
```bash
pip install -U htag
```

Alternatively, you can run from source:
```bash
git clone https://github.com/manatlan/htag.git
cd htag
uv run examples/main3.py
```

### Skill

With agentic llm, you can use this [SKILL.md](.agent/skills/htag-development/SKILL.md) to create an htag application.


## Antigravity resumes :

htag is a Python library for building web applications using HTML, CSS, and JavaScript.

### New Features
*   **Zero-Config Hot-Reload**: Passing `reload=True` to any runner (e.g. `ChromeApp(App).run(reload=True)`) automatically watches for Python file changes, seamlessly restarts the backend, and gracefully refreshes the frontend without losing your browser window session.
*   **F5/Reload Robustness**: Refreshing the browser no longer kills the Python backend; the session reconstructs cleanly.
*   **Level 2: HTTP SSE**: If WebSockets are blocked (e.g. strict proxies) or fail to connect, the client seamlessly falls back to HTTP POST for events and Server-Sent Events (SSE) for receiving UI updates.
*   **Level 3: Pure HTTP**: If even SSE is unavailable, the application gracefully degrades to purely synchronous HTTP POST requests. The server processes the events and returns the UI updates in the HTTP response directly. (Note: Server-initiated `yield` and background tasks won't reflect on the client until their next interaction).
*   **Production Debug Mode**: Easily disable error reporting in the client by setting `debug=False` on the runner (e.g. `WebApp(App, debug=False).app`), preventing internal stacktraces from leaking to users.
*   **Parano Mode (Payload Obfuscation)**: By initializing `WebApp(App, parano=True)`, all data exchanged between the frontend and backend is automatically obfuscated using a dynamic XOR cipher and Base64 wrapping, making network traffic unreadable to MITM proxies.
*   **Progressive UI in Lifecycle Hooks**: Both `on_mount()` and `on_unmount()` fully support yielding intermediate UI states natively. In `WebApp`, `on_unmount()` is correctly called before `on_mount()` version a page refresh (F5), ensuring resource cleanup (e.g. cancelling tasks). `htag` intelligently queues `on_mount` generators until the client connects.
*   **`.root`, `.parent`, and `.childs` properties**: Every `GTag` exposes its position in the component tree. `.root` references the main `Tag` instance, `.parent` references the direct parent component, and `.childs` is a list of its children. This allows components to easily navigate the DOM tree and trigger app-level actions.
*   **Declarative UI with Context Managers (`with`)**: You can now build component trees visually using `with` blocks (e.g., `with Tag.div(): Tag.h1("Hello")`), removing the need for `self <= ...` boilerplate.
*   **Modern Attribute Access (Dictionary Style)**: In addition to `Tag.div(_class="foo")` in constructors, you must now use `self["class"] = "foo"` for dynamic property management after instantiation. This is the only way to handle all attributes, including those with dashes (e.g., `self["data-id"] = 123`).
*   **Reactive State Management (`States`, `State`)**: The preferred way to manage state is using the **`States`** container to hold multiple variables in one reactive object.
    - **Multiple States (Recommended)**: `self.s = States(count=0, loading=False)`. Access/mutate directly: `self.s.count += 1`.
    - **Bulk Operations**: Use `self.s.dump()` (to dict) and `self.s.load(dict)` for easy persistence or initialization.
    - **Standard `State`**: Use `State(value)` for individual reactive objects. Both support transparent **Proxying**: use operators directly (`if self.count > 0: ...`), mutate nested structures (lists, dicts), and delegate attribute assignments.
    - Data-driven UIs: `Tag.div(self.s.count)` or `Tag.div(lambda: f"Count: {self.s.count}")`.
    - Safe value retrieval: Use `self.s.count.get()` to retrieve the underlying value regardless of its type (essential for `bool` values).
*   **Iterative & Attribute Reactivity**: Iterating over a `State` now yields proxies, meaning mutations like `for item in self.list: item.active = True` trigger re-renders. Attribute assignments (e.g. `self.user.name = "Bob"`) are automatically delegated to the underlying object and notified.
*   **Direct & Reactive Attributes**: Attributes like `_class`, `_style`, or `_disabled` in constructors now support lambdas OR `State` objects directly (e.g., `Tag.button(_disabled=self.loading)`). Boolean attributes (e.g. `_disabled=True`) are correctly rendered as key-only or omitted.
*   **Simplified Removal (`.remove()`)**: To remove a component from its parent, simply call `self.remove()` without arguments.
*   **Rapid Content Replacement (`.text`)**: Use the `.text` property on any tag to quickly replace its inner text content without needing to manually clear its children first.
*   **Recursive Statics & JS**: Components created dynamically (via lambdas) now have their `statics` (CSS) and `call_js` commands correctly collected and sent to the client.
*   **Scoped Styles (`styles`)**: Define a `styles` class attribute on any component to get automatically scoped CSS. The framework prefixes every rule with `.htag-ClassName`, handles `@media`, `@keyframes`, pseudo-selectors, and multi-selectors.
*   **CSS Class Helpers**: `add_class()`, `remove_class()`, `toggle_class()`, and `has_class()` for convenient class manipulation without manual string handling.
*   **Simple Events & HashChange**: Support for passing primitive values or custom objects from JS (via `tag["onclick"] = func`). Includes built-in support for `onhashchange`.
*   **Fragments (`Tag()`)**: Create virtual components that group children without adding a wrapper tag to the DOM.
*   **Advanced Tag Search (`.find_tag()`)**: Effortlessly locate any component in the tree by its internal htag ID or its manually assigned HTML `id`.
*   **Custom ID Resilience**: Manual HTML `id` attributes are now supported without breaking htag's reactive partial updates, thanks to an automatic `data-htag-id` fallback mechanism.
*   **Automatic Attribute Assignment**: Non-prefixed keyword arguments passed during component instantiation are automatically assigned as instance attributes, simplifying data passing to custom components.
*   **Unified Form Handling**: When a `Tag.form` is submitted, all input fields are automatically collected into a dictionary and passed as `event.value`. You can conveniently access fields using square brackets on the event object (e.g., `e["fieldname"]`). Checkboxes and radios are correctly represented as boolean states.
*   **Robust Event & Attribute Sync**: For all form elements (`input`, `textarea`, `select`), `htag` now automatically synchronizes their `value`, `checked`, and `name` attributes from the client to the server **immediately before any event callback**. This ensures that `event.checked` is available for checkboxes and that `e.target["checked"]` is always up-to-date with reality.
*   **Background Reactivity & `update()`**: `State` mutations from background tasks (started via `asyncio.create_task`) now automatically trigger UI updates without needing manual user interaction. For non-state-based changes, every component now exposes an `.update()` method (e.g., `self.root.update()` or simply `self.update()`) to manually schedule a throttled UI synchronization.
*   **Hash-Based SPA Router**: Introducing `Router` for client-side navigation. Define route patterns (e.g., `/tasks/:id`), map them to component classes, and handle navigation seamlessly with browser history support (Back/Forward buttons) and automatic lifecycle management.
*   **Ephemeral Ports**: Support for `port=0` in all runners to pick a free port automatically.
*   **Automatic Page Title**: htag automatically extracts `Tag.title` from your `App.statics` list to set the browser tab title, avoiding duplicate tags in the HTML head. It fallbacks to the class name if none is provided.
*   **Session Lifecycle & `on_destroy`**: Introducing `on_destroy()` on `App` instances, a definitive cleanup hook called only when the runner discards the session. Unlike `on_unmount()`, it doesn't trigger on page refreshes (F5), making it perfect for closing database connections or stopping background servers.
*   **Forced Session Reset (`?n`)**: Users can now append `?n` to any application URL to force the destruction of their current session and create a fresh one from scratch, immediately triggering the `on_destroy()` hook on the old instance.

## Tests communication between front and back

By default, htag will use a Websocket (level1). It it cant, it will use HTTP SSE (/stream & /event) (level ). And if it can't, it will use pure HTTP connection (level3).

To tests them in live (debugging):

- 1/ **websocket** : press F5, it will reset to websocket mode
- 2/ **sse** : in devtools/console : type `fallback();`
- 3/ **http-pure**: in devtools/console : `fallback_pure_http();` (should always work!)

### Forcing Interaction Mode (htag_mode cookie)

To manually test or force a specific mode (bypassing auto-detection), set the `htag_mode` cookie:
- `document.cookie="htag_mode=http;path=/"` -> Force pure HTTP
- `document.cookie="htag_mode=sse;path=/"` -> Force SSE
- `document.cookie="htag_mode=;Max-Age=0;path=/"` -> Back to default (WS)

## History

At the beginning, there was [guy](https://github.com/manatlan/guy), which was/is the same concept as [python-eel](https://github.com/ChrisKnott/Eel), but more advanced.
One day, I've discovered [remi](https://github.com/rawpython/remi), and asked my self, if it could be done in a *guy way*. The POC was very good, so I released
a version of it, named [gtag](https://github.com/manatlan/gtag). It worked well despite some drawbacks, but was too difficult to maintain. So I decided to rewrite all
from scratch, while staying away from *guy* (to separate, *rendering* and *runners*)... and [htag](https://github.com/manatlan/htag/tree/v1-legacy) was born. The codebase is very short, concepts are better implemented, and it's very easy to maintain. And now (2026) [htag v2](https://github.com/manatlan/htag) is here (a full rewrite of v1 with antigravity) !

