From c8cd71fe0564a391b366a6934d00e6ed5c56b365 Mon Sep 17 00:00:00 2001 From: Norman Fomferra Date: Tue, 9 Jun 2026 08:27:56 +0200 Subject: [PATCH 1/2] Preparing 0.1.0 release --- README.md | 6 +++- remotestate-py/.gitignore | 1 + remotestate-py/LICENSE | 21 +++++++++++++ remotestate-py/README.md | 36 ++++++++++++++++++++++ remotestate-py/notebooks/demo.ipynb | 2 +- remotestate-py/pixi.lock | 47 +++++++++++++++++++++++++++++ remotestate-py/pyproject.toml | 27 ++++++++++++++--- remotestate-ts/LICENSE | 21 +++++++++++++ remotestate-ts/README.md | 21 +++++++++++++ remotestate-ts/package.json | 27 +++++++++++++++++ 10 files changed, 202 insertions(+), 7 deletions(-) create mode 100644 remotestate-py/LICENSE create mode 100644 remotestate-py/README.md create mode 100644 remotestate-ts/LICENSE create mode 100644 remotestate-ts/README.md diff --git a/README.md b/README.md index b333e1e..279ca64 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Remote State -[![CI](https://github.com/forman/remotestate/actions/workflows/ci.yml/badge.svg)](https://github.com/forman/remotestate/actions/workflows/ci.yml) +[![CI](https://github.com/bcdev/remotestate/actions/workflows/ci.yml/badge.svg)](https://github.com/bcdev/remotestate/actions/workflows/ci.yml) [![TypeScript](https://img.shields.io/badge/TypeScript-3178C6?logo=typescript&logoColor=white)](https://www.typescriptlang.org/) [![Vite](https://img.shields.io/badge/Vite-646CFF?logo=vite&logoColor=white)](https://vite.dev/) [![Ruff](https://img.shields.io/badge/Ruff-2C2F3A?logo=ruff&logoColor=white)](https://docs.astral.sh/ruff/) @@ -13,6 +13,10 @@ Remote State is a Python-first framework for building stateful React frontends. It lets you define application state, actions, and queries in Python, then render the UI in React/TypeScript over a WebSocket bridge. +Package-specific docs: +- [Python package README](./remotestate-py/README.md) +- [TypeScript package README](./remotestate-ts/README.md) + The library is designed around two primary use cases: 1. **React frontends for Python code** - especially notebook-driven UIs, where a Jupyter cell or Python script owns the state and the browser only renders the interface. diff --git a/remotestate-py/.gitignore b/remotestate-py/.gitignore index fd2cade..7ab52c2 100644 --- a/remotestate-py/.gitignore +++ b/remotestate-py/.gitignore @@ -5,3 +5,4 @@ __pycache__/ .ipynb_checkpoints/ .venv/ uv.lock +dist/ diff --git a/remotestate-py/LICENSE b/remotestate-py/LICENSE new file mode 100644 index 0000000..d123bc3 --- /dev/null +++ b/remotestate-py/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 Norman Fomferra + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/remotestate-py/README.md b/remotestate-py/README.md new file mode 100644 index 0000000..df25675 --- /dev/null +++ b/remotestate-py/README.md @@ -0,0 +1,36 @@ +# Remote State Python + +`remotestate` is the Python runtime for Remote State. + +It gives you: + +- `Store` for application state +- `Service` for defining actions and queries +- `action` and `query` decorators +- `serve()` for exposing the backend to the React frontend + +## Install + +```bash +pip install remotestate +``` + +## Quick Start + +```python +import remotestate as rs + +store = rs.Store({"count": 0}) + + +class MyService(rs.Service): + @rs.action + async def increment(self): + self.store.set("count", self.store.get("count") + 1) + + +rs.serve(MyService(store), dist_dir="my-ui/dist") +``` + +For the full project overview, see the repository root README: +[Remote State](https://github.com/bcdev/remotestate) diff --git a/remotestate-py/notebooks/demo.ipynb b/remotestate-py/notebooks/demo.ipynb index 8f6c7cb..6f11d99 100644 --- a/remotestate-py/notebooks/demo.ipynb +++ b/remotestate-py/notebooks/demo.ipynb @@ -15,7 +15,7 @@ "id": "b5855fdc-6ffc-486e-8172-8202e56f7778", "metadata": {}, "source": [ - "Assumes you have checked out https://github.com/forman/remotestate-demo next to this project \n", + "Assumes you have checked out https://github.com/bcdev/remotestate-demo next to this project\n", "and called `npm run build`. " ] }, diff --git a/remotestate-py/pixi.lock b/remotestate-py/pixi.lock index c79499e..d278387 100644 --- a/remotestate-py/pixi.lock +++ b/remotestate-py/pixi.lock @@ -206,6 +206,10 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/widgetsnbextension-4.0.15-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-4.1.0-pyhcf101f3_0.conda - pypi: . + - pypi: https://files.pythonhosted.org/packages/0d/fe/6bea5c9162869c5beba5d9c8abbed835ec85bf1ec1fba05a3822325c45f3/build-1.5.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/56/49/2797ec0ef88008a653a8867bb8d1e5c223cd2df8e40390dd5c6a0279cbc5/hatchling-1.30.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/7c/a4/81502f486f01db95bc8320646a8a12511f5e556cb63d5e224d91816605c4/trove_classifiers-2026.6.1.19-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/bd/24/12818598c362d7f300f18e74db45963dbcb85150324092410c8b49405e42/pyproject_hooks-1.2.0-py3-none-any.whl osx-arm64: - conda: https://conda.anaconda.org/conda-forge/noarch/_python_abi3_support-1.0-hd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/annotated-doc-0.0.4-pyhcf101f3_0.conda @@ -400,6 +404,10 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zeromq-4.3.5-h10816f8_11.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstd-1.5.7-hbf9d68e_6.conda - pypi: . + - pypi: https://files.pythonhosted.org/packages/0d/fe/6bea5c9162869c5beba5d9c8abbed835ec85bf1ec1fba05a3822325c45f3/build-1.5.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/56/49/2797ec0ef88008a653a8867bb8d1e5c223cd2df8e40390dd5c6a0279cbc5/hatchling-1.30.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/7c/a4/81502f486f01db95bc8320646a8a12511f5e556cb63d5e224d91816605c4/trove_classifiers-2026.6.1.19-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/bd/24/12818598c362d7f300f18e74db45963dbcb85150324092410c8b49405e42/pyproject_hooks-1.2.0-py3-none-any.whl win-64: - conda: https://conda.anaconda.org/conda-forge/noarch/_python_abi3_support-1.0-hd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/annotated-doc-0.0.4-pyhcf101f3_0.conda @@ -590,6 +598,10 @@ environments: - conda: https://conda.anaconda.org/conda-forge/win-64/zeromq-4.3.5-h3a581c9_11.conda - conda: https://conda.anaconda.org/conda-forge/win-64/zstd-1.5.7-h534d264_6.conda - pypi: . + - pypi: https://files.pythonhosted.org/packages/0d/fe/6bea5c9162869c5beba5d9c8abbed835ec85bf1ec1fba05a3822325c45f3/build-1.5.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/56/49/2797ec0ef88008a653a8867bb8d1e5c223cd2df8e40390dd5c6a0279cbc5/hatchling-1.30.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/7c/a4/81502f486f01db95bc8320646a8a12511f5e556cb63d5e224d91816605c4/trove_classifiers-2026.6.1.19-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/bd/24/12818598c362d7f300f18e74db45963dbcb85150324092410c8b49405e42/pyproject_hooks-1.2.0-py3-none-any.whl packages: - conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-20_gnu.conda build_number: 20 @@ -4664,3 +4676,38 @@ packages: - pydantic>=2,<3 - uvicorn>=0.46,<0.47 requires_python: '>=3.12' +- pypi: https://files.pythonhosted.org/packages/0d/fe/6bea5c9162869c5beba5d9c8abbed835ec85bf1ec1fba05a3822325c45f3/build-1.5.0-py3-none-any.whl + name: build + version: 1.5.0 + sha256: 13f3eecb844759ab66efec90ca17639bbf14dc06cb2fdf37a9010322d9c50a6f + requires_dist: + - packaging>=24.0 + - pyproject-hooks + - colorama ; os_name == 'nt' + - importlib-metadata>=4.6 ; python_full_version < '3.10.2' + - tomli>=1.1.0 ; python_full_version < '3.11' + - keyring ; extra == 'keyring' + - uv>=0.1.18 ; extra == 'uv' + - virtualenv>=20.17 ; python_full_version >= '3.10' and python_full_version < '3.14' and extra == 'virtualenv' + - virtualenv>=20.31 ; python_full_version >= '3.14' and extra == 'virtualenv' + requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/56/49/2797ec0ef88008a653a8867bb8d1e5c223cd2df8e40390dd5c6a0279cbc5/hatchling-1.30.1-py3-none-any.whl + name: hatchling + version: 1.30.1 + sha256: 161eacafb3c6f91526e92116d21426369f2c36e98c36a864f11a96345ad4ee31 + requires_dist: + - packaging>=24.2 + - pathspec>=0.10.1 + - pluggy>=1.0.0 + - tomli>=1.2.2 ; python_full_version < '3.11' + - trove-classifiers + requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/7c/a4/81502f486f01db95bc8320646a8a12511f5e556cb63d5e224d91816605c4/trove_classifiers-2026.6.1.19-py3-none-any.whl + name: trove-classifiers + version: 2026.6.1.19 + sha256: ab4c4ec93cc4a4e7815fa759906e05e6bb3f2fbd92ea0f897288c6a43efd15b3 +- pypi: https://files.pythonhosted.org/packages/bd/24/12818598c362d7f300f18e74db45963dbcb85150324092410c8b49405e42/pyproject_hooks-1.2.0-py3-none-any.whl + name: pyproject-hooks + version: 1.2.0 + sha256: 9e5c6bfa8dcc30091c74b0cf803c81fdd29d94f01992a7707bc97babb1141913 + requires_python: '>=3.7' diff --git a/remotestate-py/pyproject.toml b/remotestate-py/pyproject.toml index 97ddb2a..172e18f 100644 --- a/remotestate-py/pyproject.toml +++ b/remotestate-py/pyproject.toml @@ -1,20 +1,34 @@ [project] -authors = [{name = "Norman Fomferra", email = "norman.fomferra@brockmann-consult.de"}] +name = "remotestate" +version = "0.1.0" +authors = [{name = "forman"}] +description = "Python state, React UI." +readme = "README.md" +keywords = ["python", "state", "react", "ui", "jupyter"] +license = {text = "MIT"} +license-files = ["LICENSE"] +requires-python = ">= 3.12" dependencies = [ "fastapi>=0.136,<0.137", "pydantic>=2,<3", "uvicorn>=0.46,<0.47" ] -name = "remotestate" -requires-python = ">= 3.12" -version = "0.1.0" +classifiers = [ + "Development Status :: 3 - Alpha", + "Intended Audience :: Developers", + "License :: OSI Approved :: MIT License", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.12", + "Topic :: Software Development :: Libraries :: Python Modules", +] +urls = { Homepage = "https://github.com/bcdev/remotestate", Issues = "https://github.com/bcdev/remotestate/issues", Repository = "https://github.com/bcdev/remotestate"} [tool.hatch.build.targets.wheel] packages = ["src/remotestate"] [build-system] build-backend = "hatchling.build" -requires = ["hatchling"] +requires = ["hatchling>=1,<2"] [tool.ruff] src = ["src/remotestate"] @@ -44,9 +58,12 @@ pytest-cov = ">=7.1.0,<8" ruff = ">=0.15.11,<0.16" [tool.pixi.pypi-dependencies] +build = ">=1,<2" +hatchling = ">=1,<2" remotestate = { path = ".", editable = true } [tool.pixi.tasks] +build = "python -m build" format = "ruff format src" checks = "ruff check src" tests = "pytest tests" diff --git a/remotestate-ts/LICENSE b/remotestate-ts/LICENSE new file mode 100644 index 0000000..d123bc3 --- /dev/null +++ b/remotestate-ts/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 Norman Fomferra + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/remotestate-ts/README.md b/remotestate-ts/README.md new file mode 100644 index 0000000..92168db --- /dev/null +++ b/remotestate-ts/README.md @@ -0,0 +1,21 @@ +# Remote State + +TypeScript and React bridge for the Remote State project. + +This package provides the frontend client, provider, and hooks that pair with +the Python backend from the main repository. + +## Install + +```bash +npm install remotestate +``` + +## Use + +```ts +import { RemoteStateProvider, useRemoteStateClient } from "remotestate"; +``` + +For full project documentation, see the repository root README: +[Remote State](https://github.com/bcdev/remotestate) diff --git a/remotestate-ts/package.json b/remotestate-ts/package.json index 1e6207e..7c125f1 100644 --- a/remotestate-ts/package.json +++ b/remotestate-ts/package.json @@ -1,15 +1,42 @@ { "name": "remotestate", "version": "0.1.0", + "description": "Python state, React UI.", "type": "module", "main": "./dist/remotestate.js", "types": "./dist/remotestate.d.ts", + "license": "MIT", + "author": "forman", + "homepage": "https://github.com/bcdev/remotestate#readme", + "bugs": { + "url": "https://github.com/bcdev/remotestate/issues" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/bcdev/remotestate.git", + "directory": "remotestate-ts" + }, + "keywords": [ + "react", + "typescript", + "state", + "websocket", + "hooks" + ], + "files": [ + "dist", + "README.md", + "LICENSE" + ], "exports": { ".": { "import": "./dist/remotestate.js", "types": "./dist/remotestate.d.ts" } }, + "engines": { + "node": ">=20" + }, "scripts": { "build": "tsc && vite build", "tests": "vitest run", From 0266ec404a28193ca0db9ea41ea4e0d8a6001126 Mon Sep 17 00:00:00 2001 From: Norman Fomferra Date: Tue, 9 Jun 2026 08:36:49 +0200 Subject: [PATCH 2/2] update --- README.md | 26 +++++++++++++------------- remotestate-py/README.md | 4 ++-- remotestate-ts/README.md | 4 ++-- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 279ca64..c8f365d 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Remote State +# RemoteState [![CI](https://github.com/bcdev/remotestate/actions/workflows/ci.yml/badge.svg)](https://github.com/bcdev/remotestate/actions/workflows/ci.yml) [![TypeScript](https://img.shields.io/badge/TypeScript-3178C6?logo=typescript&logoColor=white)](https://www.typescriptlang.org/) @@ -9,7 +9,7 @@ > **Python state, React UI.** One runtime for notebook apps and addon backends. -Remote State is a Python-first framework for building stateful React frontends. +_RemoteState_ is a Python-first framework for building stateful React frontends. It lets you define application state, actions, and queries in Python, then render the UI in React/TypeScript over a WebSocket bridge. @@ -27,7 +27,7 @@ React handles presentation, interaction, and reactivity on the browser side. --- -## What Remote State Provides +## What RemoteState Provides - **Python-owned application state** - store nested state in a `Store` and mutate it through actions. - **Read and write separation** - use `@action` for mutations and `@query` for read-only calls. @@ -241,7 +241,7 @@ Re-running the same Jupyter cell restarts the server automatically. ### `createRemoteState(url)` -Creates a typed Remote State bridge. +Creates a typed RemoteState bridge. ```typescript const remoteState = createRemoteState("ws://localhost:9753/ws"); @@ -249,7 +249,7 @@ const remoteState = createRemoteState("ws://localhost:9753/ws"); ### `RemoteStateProvider` and `useRemoteStateClient()` -React context wrapper for a Remote State bridge bound to a WebSocket URL, plus +React context wrapper for a RemoteState bridge bound to a WebSocket URL, plus a hook to access it. ```tsx @@ -314,7 +314,7 @@ re-renders on invalidation. ### Setup ```bash -git clone https://github.com/your-username/remotestate +git clone https://github.com/bcdev/remotestate cd remotestate # Python @@ -352,12 +352,12 @@ remotestate generate my_service.py --out ui/src/MyService.ts ## Architecture ```text -Python (source of truth) TypeScript / React (renderer) -────────────────────────────── ────────────────────────────── -Store StoreImpl (cache) - state lazy fetch per path - actions + queries ──► invalidate -> re-render - progress events ──► task updates +Python (source of truth) TypeScript / React (renderer) +────────────────────────────── ────────────────────────────── +Store StoreImpl (cache) + state lazy fetch per path + actions + queries ──► invalidate -> re-render + progress events ──► task updates Service @action -> mutate state ──► remoteState.action() @@ -403,4 +403,4 @@ and TypeScript strict mode on the JavaScript side. ## License -MIT © Norman Fomferra +MIT © [@forman](https://github.com/forman) diff --git a/remotestate-py/README.md b/remotestate-py/README.md index df25675..9e1adae 100644 --- a/remotestate-py/README.md +++ b/remotestate-py/README.md @@ -1,6 +1,6 @@ -# Remote State Python +# RemoteState - Python -`remotestate` is the Python runtime for Remote State. +`remotestate` is the Python runtime for the _RemoteState_ library. It gives you: diff --git a/remotestate-ts/README.md b/remotestate-ts/README.md index 92168db..0766386 100644 --- a/remotestate-ts/README.md +++ b/remotestate-ts/README.md @@ -1,6 +1,6 @@ -# Remote State +# RemoteState - TypeScript/React -TypeScript and React bridge for the Remote State project. +`remotestate` is the TypeScript and React bridge of the _RemoteState_ library. This package provides the frontend client, provider, and hooks that pair with the Python backend from the main repository.