Experimental Python UI runtime with reactive components and live previews.
Project description
Otoe
Otoe is an experimental Python UI runtime for building desktop-style interfaces with component functions, reactive state, explicit events, and a renderer boundary that can evolve beyond the current HTML proof backend.
The project is early. The current repository is a technical preview for the runtime model, visual case studies, and live preview loop. It is not yet a stable public framework or a production desktop renderer.
What Works Today
- Python component functions with typed widget contracts.
signal,computed,effect, owner cleanup, and lifecycle hooks.Showand keyedForcontrol flow.- Fake-widget mounting and deterministic snapshots.
- Static HTML preview rendering.
- Shared live HTML preview server with click/input event dispatch.
- Optional JSX-like
template(...)syntax that returns the sameNodetree. - Experimental portable
css(...)/StyleSheetAPI plus low-levelutility_css()/utility_stylesheet()helpers for app styling. otoe plandiagnostics for checking an app against the first offline hardware/cage profile before any deployment bundle exists, including static extraction of literalclassNamestate classes for simple local targets.- First
otoe.uiprimitives: cards, badges, action buttons, tabs, toolbars, stat cards, data tables, dialogs, toasts, command palettes, app shells, sidebar navigation, route views, command registries, shortcut scopes, menus, controlled selects, section headers, empty states, and feedback toasts. - Modern default presets for no-custom-CSS app surfaces: app frames, sidebars, topbars, surfaces, metric grids, metric tiles, status pills, and list rows.
- Keyboard handling for command palettes, menus, selects, and button-backed controls in the live preview backend.
FocusScopesupport for live focus trapping and focus restoration in dialogs and popovers.- Live autofocus support for command overlays and other focused inputs.
- Wraith-shaped and SaaS-shaped case studies using the same UI primitives.
- UI kit kitchen-sink preview for validating primitives outside one product shape.
- Utility-first reference app for validating low-level styling ergonomics.
- Headless native layout, paint, PNG output, hit-testing, and click dispatch for the first renderer spike.
NativeSurfacefor mounting a tree, rendering PNG frames, dispatching clicks, and refreshing the headless native frame from one object.NativeWindowDriverfor testable native-window event dispatch over aNativeSurface, plus an optional Tk wrapper for local experiments.run_native(...)as the experimental native app entry point, currently backed by the optional Tk wrapper.- Driver-level
key_input(...)text editing for printable keys, Backspace, Delete, Enter/Tab fallback, and shortcut fallback. - Controlled native
ScrollView(scrollY=..., onScroll=...)support with clipped paint, clipped hit-testing, and wheel dispatch through the native window driver. - Headless native focus and keyboard handling for autofocus, click-to-focus, Tab traversal, focused keydown handlers, button submit keys, and global shortcut payloads.
- Headless controlled input text dispatch through
NativeSurface.input_text(...). - Framework-neutral native task board demo with search, filtered rows, empty state, modal state, shortcuts, controlled scroll, and PNG frame output.
- Lazy
NativeSurfacerefresh when reactive props or control-flow branches change outside direct surface events. - Disabled widgets are skipped for native focus and click dispatch.
ScrollViewbounds clip native paint output and hit-tested clicks.- Native paint includes disabled control defaults and focused control rings.
What Is Not Ready Yet
- Production desktop rendering/windowing.
- GPU rendering, layout engine integration, or accessibility tree output.
- Stable public API guarantees.
- Full component library or shadcn/Horizon-style UI kit.
- Packaging for production apps.
Native Renderer Status
The native path is currently a deterministic headless renderer spike. It can
layout mounted Otoe trees, emit paint commands, write PNG preview frames, and
exercise click, focus, keyboard, input, and scroll dispatch through
NativeSurface.
This is enough for renderer tests, visual fixtures, and early framework API
validation. It is not yet a production desktop backend: there is no GPU
renderer, platform accessibility tree, text shaping engine, retained windowing
backend, or backend compatibility promise. See
ADR-012-native-backend-boundary.md for the backend boundary and
ADR-013-native-layout-hardening.md for the current layout-hardening contract.
ADR-014-native-overflow-clipping.md defines the current overflow policy:
normal containers do not clip, while ScrollView clips paint and hit testing.
For day-to-day workflow choices, see NATIVE_WORKFLOWS.md.
Native and window-facing exports are intentionally marked as experimental. The imports remain available for examples and tests, but they are not backend compatibility promises:
from otoe import api_status
assert api_status("NativeSurface").category == "experimental-native"
Quick Start
Use Python 3.11 or newer.
Install the latest published package from PyPI:
python -m pip install otoe
For local development:
python -m venv .venv
. .venv/bin/activate
python -m pip install -e ".[dev]"
pytest -q
On Debian/Ubuntu systems that report an externally managed Python environment,
do not install Otoe into the system Python. Use the virtual environment above,
or run examples directly from a checkout with PYTHONPATH=src:..
Run the local framework health check:
python -m otoe check
python -m otoe check --tests
python -m otoe check --tests -- tests/test_cli.py -k new
python -m otoe new my_app
After installation, the same commands are available through the otoe console
script:
otoe check
otoe new my_app
otoe render examples.quickstart:app --out preview.html --pretty
otoe render examples.quickstart:app --out preview.png --native
otoe plan examples.quickstart:app --profile cage --no-strict-styles
otoe build examples.quickstart:app --out dist/cage --no-strict-styles
otoe compare-contract expected.json actual.json
otoe dev examples.live_counter:app --port 8767
otoe new my_app writes a small renderable app plus styles.css; pass
--no-css when you want only the Python scaffold.
Render the generated app with its stylesheet:
cd my_app
otoe render app:app --out preview.html --css styles.css --pretty
otoe render app:app --out preview.png --native --css styles.css
Render an importable Otoe node or zero-argument factory to HTML:
python -m otoe render examples.quickstart:app --out preview.html --pretty
Apply an Otoe CSS file inline while rendering:
python -m otoe render examples.quickstart:app --out preview.html --css path/to/styles.css --pretty
Render the same target through the native PNG path:
python -m otoe render examples.quickstart:app --out preview.png --native
Check an app against the first offline hardware/cage profile:
python -m otoe plan app:app --profile cage --css styles.css
python -m otoe plan app:app --profile cage --utilities
python -m otoe plan app:app --profile cage --utilities --out dist/otoe-plan.json
python -m otoe plan app:app --profile cage --utilities --json
python -m otoe plan app:app --profile-file otoe.profile.toml --out dist/otoe-plan.json
python -m otoe deps app:app --profile-file otoe.profile.toml --json
otoe plan is diagnostic only. It imports and mounts the target, checks used
classes against the selected style sources, and reports portable, html-only,
deferred, and invalid style work before building or deploying a bundle.
--json emits the same report as machine-readable JSON, and --out writes that
JSON as the first plan artifact.
When otoe.profile.toml exists in the current directory, otoe plan uses it by
default. CSS paths are relative to the profile file:
profile = "cage"
utilities = true
css = ["styles.css"]
assets = ["static/logo.png"]
[runtime]
allow_runtime_installs = false
files = ["app.py"]
[backend]
name = "native"
[deps]
packages = ["pytest"]
extras = ["dev"]
Explicit CLI flags override the profile file. For example, --css custom.css
replaces the profile css list, and --no-utilities disables profile-enabled
utilities.
otoe deps audits declared [deps] entries against the current build
environment. It reports installed and missing packages plus known and unknown
Otoe extras. It does not install anything, download anything, import the app
target, or write files; missing packages must be installed manually before a
hardware/cage deployment. During otoe build, the same audit is written as
otoe-deps.json and invalid dependency audits stop the build before
manifest.json is written.
Write the first minimal offline bundle contract:
python -m otoe build app:app --profile-file otoe.profile.toml --out dist/cage
python -m otoe build app:app --profile-file otoe.profile.toml --out dist/cage --validate
python -m otoe pack dist/cage --out dist/cage.tar.gz
otoe build currently writes dist/cage/otoe-plan.json,
dist/cage/otoe-deps.json, dist/cage/otoe-styles.json,
dist/cage/manifest.json, copies declared assets under dist/cage/assets/,
copies selected Otoe framework/runtime files under dist/cage/framework/,
copies the simple local target module under dist/cage/app/ when the target is
shaped like app:app, follows simple same-directory imports such as
import helpers and from helpers import view, and copies declared extra
runtime files under dist/cage/app/.
It fails when the plan, dependency audit, or backend selection is invalid,
allows warning plans, and does not install dependencies, download anything, or
auto-discover package modules or arbitrary dynamic imports yet. [runtime] files remains the explicit place for packages, dynamic imports, and extra app
files. The manifest
references otoe-deps.json, records copied framework files in frameworkFiles,
and records copied app files in runtimeFiles. The style artifact records used
classes, resolved portable declarations, omitted html-only/deferred
declarations, diagnostics, tokens, and low-level styleOps that backend
candidates can apply without re-parsing CSS on the target.
The bundle also includes otoe-run.py, a minimal generated runner. It adds the
copied app/ and framework/ directories to sys.path, loads the manifest
target, supports --check for import/load validation, and supports --png frame.png for a single headless native PNG frame using the bundled compiled
styles. It also supports --verify to check referenced bundle files, sizes,
SHA-256 hashes, and --layout-check to run native layout/paint validation
without writing a PNG. Pass otoe build --validate to run the generated
runner's --verify, --check, and --layout-check modes after writing the
bundle; this confirms the copied files are intact, the target loads from the
bundle instead of only from the workspace, and compiled styles can drive native
rendering.
otoe pack verifies the bundle with otoe-run.py --verify and writes a
portable .tar.gz archive for deployment. The pack step keeps the bundle rooted
at the archive top level and excludes local cache directories such as
__pycache__/ and .pytest_cache/.
Compare JSON contract artifacts:
python -m otoe compare-contract expected.json actual.json
python -m otoe compare-contract expected.json actual.json --json
python -m otoe compare-contract expected.json actual.json --max-diffs 5
python -m otoe compare-contract expected.json actual.json --ignore-path /pngSmoke/path --ignore-path /calls/raster/signature/0/subject --ignore-path /calls/raster/hash
PYTHONPATH=src:. python -m examples.native.backend_candidate_skeleton --composed-renderer-contract-json --compact-contract --composed-renderer-png /tmp/composed_renderer_candidate.png --contract-out examples/native/contracts/composed_renderer_compact_expected.json
otoe compare-contract performs a deterministic deep JSON comparison, exits
zero only when the contracts match, and reports JSON-pointer paths for
differences. Use --ignore-path for intentionally environment-specific JSON
pointer fields. If the composed renderer PNG smoke filename differs from the
fixture, ignore /pngSmoke/path, /calls/raster/signature/0/subject, and
/calls/raster/hash together. Use it with compact renderer contracts when
checking backend candidates in CI. The native backend candidate fixture at
examples/native/contracts/composed_renderer_compact_expected.json is the
current expected compact composed-renderer contract. Refresh that fixture only
for intentional contract changes, using --contract-out instead of shell
redirection.
Run an importable live preview app locally:
python -m otoe dev examples.live_counter:app --port 8767
Generate the static Wraith Mission Exec preview:
PYTHONPATH=src:. python -m examples.wraith.mission_exec_preview > preview/wraith_mission_exec.html
Run the live Wraith Mission Exec preview:
PYTHONPATH=src:. python -m examples.wraith.mission_exec_live_preview
Then open http://127.0.0.1:8767. Use SIMULATE FRAME to verify that
signals, events, and rerendering are actually changing visible state.
Run the live SaaS preview:
PYTHONPATH=src:. python -m examples.saas.live_preview
Then open http://127.0.0.1:8766.
Generate the static UI kit preview:
PYTHONPATH=src:. python -m examples.ui.preview > preview/ui.html
Run the live UI kit preview:
PYTHONPATH=src:. python -m examples.ui.live_preview
Then open http://127.0.0.1:8768. Use the sidebar, command launcher,
Ctrl+K/Meta+K, command search, Enter key, Escape, and single-key command
shortcuts to verify the live command overlay and route switching.
Generate the framework-neutral native counter PNG frames through
NativeSurface:
PYTHONPATH=src:. python -m examples.native.counter_demo
This writes preview/native/native_counter_before.png and
preview/native/native_counter_after.png.
Generate the framework-neutral native task board PNG frames:
PYTHONPATH=src:. python -m examples.native.task_board_demo
This writes initial, filtered, and modal frames under preview/native/.
Generate native-window driver PNG frames, or open the optional Tk wrapper:
PYTHONPATH=src:. python -m examples.native.window_demo
PYTHONPATH=src:. python -m examples.native.window_demo --window
The --window command requires Python's Tk bindings and a graphical display. On
Debian/Ubuntu, install the OS package with:
sudo apt install python3-tk
The --window mode uses the experimental native entry point:
from otoe import run_native
run_native(App(), stylesheet=styles, title="Otoe")
run_native(...) routes through the experimental NativeBackendAdapter
boundary. The only built-in backend name today is "tk"; it is a manual-test
adapter, not a production desktop backend. The "tk" adapter presents native
paint commands on a Tk Canvas so manual windows can show readable text. The
Canvas scales geometry up to 2x for larger windows while keeping font sizes in
logical native units; this is a presentation proof, not responsive layout
reflow. The headless PNG output path remains deterministic and still uses marker
text.
Tiny Example
from otoe import Button, Text, VStack, component, computed, mount, signal
@component
def Counter():
count = signal(0)
label = computed(lambda: f"Clicked {count.value} times")
return VStack(
Text(label),
Button("Increment", onClick=lambda: count.set(count.value + 1)),
gap=8,
)
tree = mount(Counter())
Event Signatures
Built-in widget event handlers are plain callables. Otoe validates the handler
arity when the event fires and includes the widget/event contract in developer
errors. Arity mismatches raise EventHandlerArityError, which remains an
EventHandlerError for compatibility.
| Widget | Event | Handler shape |
|---|---|---|
Button |
onClick |
lambda: ... |
Button |
onKeyDown |
lambda key: ... |
Button |
onFocus, onBlur |
lambda: ... |
Input |
onChange |
lambda value: ... |
Input |
onKeyDown |
lambda key: ... |
Input |
onFocus, onBlur |
lambda: ... |
ScrollView |
onScroll |
lambda next_scroll_y: ... |
ShortcutScope |
onGlobalKeyDown |
lambda event: ... |
The same contracts are available programmatically:
from otoe import Button, event_signature_for, format_event_signature
signature = event_signature_for(Button, "onKeyDown")
assert format_event_signature("onKeyDown", signature) == "onKeyDown(key)"
The current otoe.ui callback surface follows the same style: onClick(),
on_query(value), on_select(command_id | item_id), on_change(value),
on_open_change(open), and on_navigate(route_id).
Project Shape
Otoe is intentionally split into layers:
- Core runtime: nodes, components, signals, effects, events, owners, control flow.
- Renderer boundary: fake widgets, HTML preview,
NativeSurface, and an early headless native layout/paint/input spike. - Style system: portable style representation and CSS preview adapter.
- UI kits: current
otoe.uiprimitives, growing toward libraries inspired by systems like shadcn or Horizon UI. - Case studies: Wraith validates dense operational UI; SaaS validates softer product UI.
Repository Map
src/otoe/- runtime package.examples/native/- framework-neutral native renderer spike demos.examples/hardware/,examples/admin/, andexamples/data_workflow/- Phase 5 professional reference apps.examples/wraith/- Wraith-shaped components and live previews.examples/saas/- SaaS-shaped generality case study.examples/ui/- shared UI primitive kitchen-sink preview.preview/- generated HTML/CSS preview artifacts, including the sharedreference_theme.cssbase used by the Phase 5 reference apps.tests/- runtime and preview regression tests.ADR-*.md- design decisions.BENCHMARKS.md- concrete Otoe-vs-Wraith UI change benchmarks.COMPONENT_COOKBOOK.md- small component, state, control-flow, live preview, and native smoke recipes.EXAMPLES_GUIDE.md- current quickstart, live, native, UI kit, SaaS, and Wraith example surfaces.REFERENCE_APP_PATTERNS.md- Phase 5 reference app boundaries, provider contracts, and extraction rules.MENTAL_MODEL.md- how nodes, components, signals, events, control flow, mounting, and renderers fit together.NATIVE_WORKFLOWS.md- when to use HTML render, native PNG,NativeSurface,NativeWindowDriver, andrun_native(...).NATIVE_RENDERER_SPIKE.md- current native renderer support and deferred work.STYLE_GUIDE.md- supported style parser, token, HTML, and native style subset behavior.TESTING_GUIDE.md- snapshot, HTML, native surface, window driver, PNG, and backend acceptance testing guidance.WIDGET_CONTRACTS.md- core widget, control node, UI component, callback, and model contracts.ROADMAP.md- current status and phase plan.
Fixture Data
Preview data is fictitious and sanitized. The Wraith examples are design and runtime case studies; they do not run recon, touch network interfaces, or perform security operations.
Status
Current status: v0.1.6 public sync prepared; low-level styleOps contracts and static class extraction. See
ROADMAP.md for the active plan.
License
MIT License. Copyright (c) 2026 Forvara.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file otoe-0.1.6.tar.gz.
File metadata
- Download URL: otoe-0.1.6.tar.gz
- Upload date:
- Size: 154.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
98373e19ac1a6ea1b51d4e267f04cdefc3164d082bd654253b08e200fbabc347
|
|
| MD5 |
a8cd808c749c0a6ca5c2e8d33ecac76f
|
|
| BLAKE2b-256 |
26c6ac7044379e99e00f448e7fe9a9c0a250e26144fbcfd268410bcbaac1f11a
|
Provenance
The following attestation bundles were made for otoe-0.1.6.tar.gz:
Publisher:
publish.yml on NeonShapeshifter/Otoe
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
otoe-0.1.6.tar.gz -
Subject digest:
98373e19ac1a6ea1b51d4e267f04cdefc3164d082bd654253b08e200fbabc347 - Sigstore transparency entry: 1693961908
- Sigstore integration time:
-
Permalink:
NeonShapeshifter/Otoe@8875b187d7ff06c12d0044092cfc10c63f268700 -
Branch / Tag:
refs/tags/v0.1.6 - Owner: https://github.com/NeonShapeshifter
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@8875b187d7ff06c12d0044092cfc10c63f268700 -
Trigger Event:
push
-
Statement type:
File details
Details for the file otoe-0.1.6-py3-none-any.whl.
File metadata
- Download URL: otoe-0.1.6-py3-none-any.whl
- Upload date:
- Size: 92.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1a5df0e7f828baf66691b047dcc54823902262563f890cfb8b271c9a8b6c167d
|
|
| MD5 |
7654bbde5a9c2c1f3a94ca1fd9117e4a
|
|
| BLAKE2b-256 |
16b5cdb10a160685109165c734ec57ca9aa9b78b24044534aab0f8ec21183cf5
|
Provenance
The following attestation bundles were made for otoe-0.1.6-py3-none-any.whl:
Publisher:
publish.yml on NeonShapeshifter/Otoe
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
otoe-0.1.6-py3-none-any.whl -
Subject digest:
1a5df0e7f828baf66691b047dcc54823902262563f890cfb8b271c9a8b6c167d - Sigstore transparency entry: 1693961993
- Sigstore integration time:
-
Permalink:
NeonShapeshifter/Otoe@8875b187d7ff06c12d0044092cfc10c63f268700 -
Branch / Tag:
refs/tags/v0.1.6 - Owner: https://github.com/NeonShapeshifter
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@8875b187d7ff06c12d0044092cfc10c63f268700 -
Trigger Event:
push
-
Statement type: