From aa5b3db01120e434c5863d961ecc37e97e540d68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?LoboGuardian=20=F0=9F=90=BA?= Date: Sun, 5 Jul 2026 07:24:39 -0400 Subject: [PATCH 1/2] docs(readme): fix broken Quick Example and document ASGI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The headline example imported `HTeaPot` and used `app.wsgi_app`, neither of which exists — the package exports `htealeaf` (an alias of the Server class) and the app object is itself the WSGI/ASGI callable, so the snippet failed at import for every new user. It also omitted the required `enable_reactivity(app)` call, without which the reactive counter never wires up. Rewrite the example to mirror demo/demo_wsgi.py (verified-runnable), pass an explicit Store id to avoid instance collisions, add an ASGI section and a "Running the demo" section, note the Python >=3.10 requirement, and link docs/PY2JS.md. --- README.md | 66 +++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 54 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index e04e8da..40b3694 100644 --- a/README.md +++ b/README.md @@ -17,16 +17,30 @@ and HTeaLeaf takes care of keeping everything in sync automatically. --- +## 📦 Installation + +```bash +pip install htealeaf +``` + +Requires **Python ≥ 3.10**. HTeaLeaf has no runtime dependencies; bring your own +WSGI/ASGI server (`wsgiref` from the standard library works for local development, +`uvicorn` for ASGI). + +--- + ## 🚀 Quick Example ```python -from HTeaLeaf import Store, SuperStore, HTeaPot, adapters +from HTeaLeaf import htealeaf, adapters, Store from HTeaLeaf.Elements import div, h3, button +from HTeaLeaf.State import enable_reactivity -app = HTeaPot(adapters.WSGI) -SuperStore(app) +app = htealeaf(adapters.WSGI) +enable_reactivity(app) # injects the client-side reactivity runtime -counter = Store({"count": 0}) +# Pass an explicit id so multiple stores don't collide. +counter = Store({"count": 0}, id="counter") @app.route("/") def home(): @@ -36,16 +50,14 @@ def home(): button("+").attr(onclick=counter.js.update("count", 1)), ) -application = app.wsgi_app - if __name__ == "__main__": from wsgiref.simple_server import make_server - with make_server("", 8000, application) as server: + with make_server("", 8000, app) as server: print("Serving at http://127.0.0.1:8000") server.serve_forever() ``` -Visit `http://127.0.0.1:8000` a fully reactive counter, zero JavaScript written by hand. +Visit `http://127.0.0.1:8000` — a fully reactive counter, zero JavaScript written by hand. You can also write client-side logic directly in Python using the `@js` decorator, and HTeaLeaf will compile it to JavaScript automatically: @@ -60,22 +72,51 @@ def greet(event): button("Click me").attr(onclick=greet) ``` +> **Note:** the `app` object returned by `htealeaf(...)` is itself the WSGI/ASGI +> callable — pass it straight to your server (`make_server("", 8000, app)`), there is +> no separate `.wsgi_app` attribute. + +--- + +## ⚡ ASGI + +The same app runs under ASGI by swapping the adapter: + +```python +from HTeaLeaf import htealeaf, adapters +from HTeaLeaf.State import enable_reactivity + +app = htealeaf(adapters.ASGI) +enable_reactivity(app) +# ... routes ... +``` + +```bash +uvicorn myapp:app +``` + +A `CGI` adapter is also available (`adapters.CGI`). + --- ## ✨ Key Features - **Declarative HTML**: build DOM trees with a fluent Python DSL, no templates needed - **Reactive server state**: `Store` objects stay in sync with the UI automatically -- **Local route state**: `use_state()` for state scoped to a single route +- **Local route state**: `use_state()` for state scoped to a single route (client-side, no server round-trip) - **Python → JS transpilation**: write client-side logic in Python with `@js`; HTeaLeaf compiles it - **Session support**: per-user state with `AuthStore` and cookies +- **Multiple transports**: WSGI, ASGI, and CGI adapters behind one API --- -## 📦 Installation +## 🧩 Running the demo + +The repository ships a small demo app: ```bash -pip install htealeaf +python -m demo.demo_wsgi # WSGI on http://127.0.0.1:8000 +uvicorn demo.demo_asgi:app # ASGI ``` --- @@ -99,7 +140,8 @@ pip install htealeaf ## 📖 Documentation -Full documentation is available in the [Wiki](https://github.com/Az107/HTeaLeaf/wiki/Welcome-to-the-HTeaLeaf!). +- Full documentation: [Wiki](https://github.com/Az107/HTeaLeaf/wiki/Welcome-to-the-HTeaLeaf!) +- [`docs/PY2JS.md`](docs/PY2JS.md) — design notes for the Python → JavaScript (`JSCode`) interop layer --- From a76446476de5be158aef1097cd15e8cb2d29c1f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?LoboGuardian=20=F0=9F=90=BA?= Date: Sat, 11 Jul 2026 13:04:31 +0200 Subject: [PATCH 2/2] docs(readme): update examples to the renamed lowercase htealeaf API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The develop branch renamed the package to lowercase htealeaf, exports the app class as HteaLeaf (the htealeaf factory alias is gone), removed enable_reactivity in favour of SuperStore(app) wiring, and dropped Store.react() — stores are now read server-side with .read() and the client re-renders via DOM reconciliation. Store ids are auto-generated per instance since #25, so the explicit id workaround is unnecessary. Also marks async-first architecture done in the roadmap and notes the session TTL work on feature/alb-28. --- README.md | 42 ++++++++++++++++++++---------------------- 1 file changed, 20 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index 40b3694..0cc15af 100644 --- a/README.md +++ b/README.md @@ -23,31 +23,28 @@ and HTeaLeaf takes care of keeping everything in sync automatically. pip install htealeaf ``` -Requires **Python ≥ 3.10**. HTeaLeaf has no runtime dependencies; bring your own -WSGI/ASGI server (`wsgiref` from the standard library works for local development, -`uvicorn` for ASGI). +Requires **Python ≥ 3.10**. Bring your own WSGI/ASGI server (`wsgiref` from the +standard library works for local development, `uvicorn` for ASGI). --- ## 🚀 Quick Example ```python -from HTeaLeaf import htealeaf, adapters, Store -from HTeaLeaf.Elements import div, h3, button -from HTeaLeaf.State import enable_reactivity +from htealeaf import HteaLeaf, SuperStore, Store, adapters +from htealeaf.elements import div, h3, button -app = htealeaf(adapters.WSGI) -enable_reactivity(app) # injects the client-side reactivity runtime +app = HteaLeaf(adapters.WSGI) +SuperStore(app) # wires the store API routes and client-side runtime -# Pass an explicit id so multiple stores don't collide. -counter = Store({"count": 0}, id="counter") +counter = Store({"count": 0}) @app.route("/") def home(): return div( - button("-").attr(onclick=counter.js.update("count", -1)), - h3(counter.react("count")), - button("+").attr(onclick=counter.js.update("count", 1)), + button("-").attr(onclick=counter.js.update("count", counter.read("count") - 1)), + h3(counter.read("count")), + button("+").attr(onclick=counter.js.update("count", counter.read("count") + 1)), ) if __name__ == "__main__": @@ -63,7 +60,8 @@ You can also write client-side logic directly in Python using the `@js` decorato and HTeaLeaf will compile it to JavaScript automatically: ```python -from HTeaLeaf.JS import js +from htealeaf.js import js +from htealeaf.js.common import console @js def greet(event): @@ -72,9 +70,9 @@ def greet(event): button("Click me").attr(onclick=greet) ``` -> **Note:** the `app` object returned by `htealeaf(...)` is itself the WSGI/ASGI -> callable — pass it straight to your server (`make_server("", 8000, app)`), there is -> no separate `.wsgi_app` attribute. +> **Note:** the `HteaLeaf` app object is itself the WSGI/ASGI callable — pass it +> straight to your server (`make_server("", 8000, app)`), there is no separate +> `.wsgi_app` attribute. --- @@ -83,11 +81,10 @@ button("Click me").attr(onclick=greet) The same app runs under ASGI by swapping the adapter: ```python -from HTeaLeaf import htealeaf, adapters -from HTeaLeaf.State import enable_reactivity +from htealeaf import HteaLeaf, SuperStore, adapters -app = htealeaf(adapters.ASGI) -enable_reactivity(app) +app = HteaLeaf(adapters.ASGI) +SuperStore(app) # ... routes ... ``` @@ -130,9 +127,10 @@ uvicorn demo.demo_asgi:app # ASGI - [x] Local route state (`use_state()`) - [x] Session support - [x] Client-side-only state (no server round-trip) +- [x] Async first architecture - [ ] Render optimisation - [ ] Persistent Store backends (Redis, SQL, …) -- [ ] Async first architecture +- [ ] Session expiration (TTL + eviction) — in progress on `feature/alb-28` - [ ] CLI - [ ] Build system to static assets