Documentation
UI Contracts
Source:
ui/README.md
ui defines the toolkit-independent presentation contracts and value objects
shared by applications, controllers, and concrete frontends. Code in this
package must not import Tkinter, Qt, application composition, controllers, or
hardware implementations.
Dependency boundary
flowchart LR
users["Applications / controllers / frontends"] --> ui["ui contracts"] --> stdlib["Standard library / tinycss2 theme parser"]
classDef orcApp fill:#dbeafe,stroke:#2563eb,color:#172554;
classDef orcService fill:#ede9fe,stroke:#7c3aed,color:#2e1065;
classDef orcController fill:#dcfce7,stroke:#16a34a,color:#14532d;
classDef orcMessage fill:#ffedd5,stroke:#ea580c,color:#7c2d12;
classDef orcAdapter fill:#fee2e2,stroke:#dc2626,color:#7f1d1d;
classDef orcExternal fill:#f3f4f6,stroke:#6b7280,color:#1f2937;
class users orcApp;
class ui orcMessage;
class stdlib orcExternal;
Interfaces use the *UiIf or *RequestHandlerIf suffix. A UI interface
describes data a view can display; a request-handler interface describes
semantic user intent emitted by that view. Implementations should inherit only
the narrow contracts required by the screen or panel they represent.
Package map
automotive/contains vehicle, trip, body, tire, connection, and diagnostics presentation contracts.lighting/contains complete lighting state and lighting request contracts.media/contains media state plus playback, track, seek, and volume requests.menu/contains toolkit-independent menu-page and menu-tile models.navigation/contains position, orientation, ground-track, translation, angular-velocity, map, turn-by-turn route, and lane-guidance contracts.
Map and routing contracts are provider-neutral. A future MapLibre frontend may
render MapState, while a Valhalla adapter may produce RouteGeometry,
RouteGuidanceState, and LaneGuidance; neither product API belongs in ui.
radio/contains receiver state, presets, tuning, playback, and application radio requests.system/contains diagnostics, status, top-bar, and system-volume contracts.vision/contains immutable RGB image/object state and semantic camera-mode, activation, and inference-enable requests.- Root modules contain cross-cutting screen, navigation, focus, action, dispatcher, event-handler, and frontend lifecycle contracts.
Screens and panels
A screen is a navigable destination and implements ScreenUiIf. A screen can
compose any number of panels. A panel is a non-navigable region within a screen
or persistent shell chrome. Domain screens additionally implement only the
data contract they need, such as MediaUiIf or LightingUiIf.
The contracts intentionally do not prescribe widget types, layout, threading, or event-loop behavior. Those decisions belong to a concrete frontend.
Camera and perception
VisionUiState is a complete immutable snapshot containing lifecycle, requested
and effective modes, normalized luminance and object bounds, SI timing values,
and an optional immutable RGB888 frame. VisionUiIf receives snapshots while
VisionRequestHandlerIf carries activation, deactivation, camera-mode, and
explicit AI target-state requests back to a controller. Frontends may format
seconds as milliseconds or hertz as FPS, but backend image, camera, detector,
tracker, worker, and hardware-control types do not cross this boundary.
Normalized physical-input contracts are intentionally not UI contracts. They
live under input_events; UiAction remains here because it represents
toolkit-independent semantic UI intent after controller mapping.
Stubs
*_stub.py classes provide inert or state-recording implementations for demos,
tests, and unavailable integrations. They implement the same public contracts
but do not introduce toolkit dependencies.
Documentation and tests
Public methods in *_if.py modules document each parameter and non-None
return value with Doxygen commands. From the repository root, run:
venv/bin/python scripts/check_doxygen_contracts.py
venv/bin/python -m unittest discover -s ui/unit_test -p 'test_*.py'
doxygen Doxyfile
Generated API documentation is written under build/doxygen/html.
Weather maps and radar replay
ui/weather/weather_overlay_state.py defines immutable city, heatmap and route
weather snapshots. Physical values are SI; route rain probability is a 0–1
fraction and times are aware UTC datetimes. WeatherOverlayUiIf is the state
consumer, WeatherOverlayControlsIf adds request binding, and
WeatherOverlayRequestHandlerIf describes city/model/route intent and navigation
visibility/replay. RadarUiState, RadarControlsIf and
RadarRequestHandlerIf provide the corresponding radar replay boundary.
Weather controller tests exercise asynchronous lifecycle and cache behavior
without Tk. Frontend contract tests exercise state consumption and request
forwarding. CI runs scripts/check_weather_ui_contracts.py to reject weather
provider/transport/thread imports in Tk weather views, backend-cache inspection,
and GUI imports in weather controllers. Interface documentation checks remain a
separate check; they do not substitute for this boundary enforcement.
Tooltips
TooltipUiIf receives immutable TooltipState snapshots; hover/focus intent
uses TooltipRequestHandlerIf. TooltipFactoryIf creates a fresh session through
application composition. TooltipController owns the 500 ms delay, cancellation,
and stale callback rejection using UiDispatcherIf. The Tk view renders and
positions the popup, without importing controllers. Leaving, clicking, hiding,
destroying, or rebuilding a control cancels pending/visible help; close is
idempotent. The navigation right-side camera controls use this boundary.
Replaceable UI boundary
UI contracts remain part of this repository. Frontends implement *UiIf and emit
semantic requests through *RequestHandlerIf; composition binds concrete
controllers and owns backend lifecycle. Frontends should consume shared state
from ui/ rather than importing ORC controllers or application-specific wiring.
The static boundary check restricts contract imports to the standard library,
ui, and the existing tinycss2 theme parser. Moving types into ui/ preserves
backend compatibility through re-exports; existing enum and class identities
remain the same. Packaging and distribution changes require discussion with the
user before implementation.
Remaining legacy boundary audit
Moving shared automotive, POI, radio, media, map, and launcher contract types
removed 64 of the original 122 import exceptions. Navigation/POI construction
cleanup removed another four. The platform launcher injection during master integration removed one more exception. The remaining 53 imports are
listed exactly in scripts/ui_boundary_exceptions.json. Existing frontend portability is still incomplete. Their migration order is:
- Spotify/media, streaming radio, and games: replace view-owned service calls, workers, polling, installation, and process lifecycle with state/request controllers; keep toolkit/native host adapters on the frontend side.
- Navigation and ORC radio: move default controller/service construction into composition and expose POI/favorites/radio operations through narrow contracts.
- Car UI factories: move backend wiring out of
screens/into composition; replace concrete player/runtime annotations in screens with public contracts. - Platform launch adapters: keep native/browser access in platform adapters, inject their interfaces into domain controllers, and relocate backend factory construction to composition.
- Startup/X11: distinguish loading presentation from startup worker ownership; retain subprocess access only in the native X11 adapter.
Each migration needs feature and lifecycle tests, and must remove its exact exceptions. This inventory is architectural debt, not an allowance for new UIs.
| Existing module | Import exceptions | Required migration |
|---|---|---|
apps/carUi/screens/aircraft_screen.py |
1 | Replace concrete runtime/player dependencies |
apps/carUi/screens/car_ui_screen_services.py |
1 | Move factory wiring to composition |
apps/carUi/screens/netflix_screen.py |
2 | Replace concrete runtime/player dependencies |
apps/carUi/screens/tk_car_ui_screen_factory.py |
4 | Move factory wiring to composition |
apps/carUi/screens/weather_screen.py |
1 | Replace concrete runtime/player dependencies |
apps/carUi/screens/youtube_screen.py |
2 | Replace concrete runtime/player dependencies |
apps/orcUi/frontend/tk/radio_entry_panel.py |
4 | Radio request/state contracts and service ownership |
apps/orcUi/frontend/tk/radio_panel.py |
4 | Radio request/state contracts and service ownership |
controllers/navigation/google_earth_map_presentation.py |
1 | Inject platform launcher interfaces |
controllers/poi/android_poi_action_executor.py |
1 | Inject platform launcher interfaces |
controllers/video/netflix_player.py |
1 | Inject platform launcher interfaces |
controllers/video/youtube_player.py |
1 | Inject platform launcher interfaces |
frontends/tk/games/games_screen.py |
7 | Game requests and runtime host adapter |
frontends/tk/media/media_screen.py |
1 | Media state/request orchestration and async loading |
frontends/tk/media/spotify_browse_panel.py |
4 | Media state/request orchestration and async loading |
frontends/tk/media/spotify_now_playing.py |
3 | Media state/request orchestration and async loading |
frontends/tk/media/spotify_playback_panel.py |
1 | Media state/request orchestration and async loading |
frontends/tk/media/spotify_screen.py |
3 | Media state/request orchestration and async loading |
frontends/tk/radio/persistent_streaming_radio_panel.py |
3 | Radio request/state contracts and service ownership |
frontends/tk/radio/streaming_radio_now_playing.py |
1 | Radio request/state contracts and service ownership |
frontends/tk/radio/streaming_radio_panel.py |
6 | Radio request/state contracts and service ownership |
frontends/tk/system/startup_splash.py |
1 | Separate startup worker from loading view |
frontends/x11/x11_window_embedder.py |
1 | Retain subprocess only in explicit native adapter |
Navigation places contracts
NavigationPlacesRequestHandlerIf exposes saved shortcut destinations, search,
selection, native camera interaction notifications, and platform place actions.
The values are immutable UI types, including MapFavorite and PointOfInterest.
NavigationPlacesFactoryIf creates one mounted view’s session. The panel rejects
untyped handlers, and the screen rejects untyped factories.
Composition constructs shared favorites/action adapters and a fresh POI search
controller for each mount. NavigationPlacesController implements the UI contract
and suppresses late results and requests after close. Hiding or destroying a panel
cancels its polling/debounce callbacks; hiding or closing the screen releases its
session. Factory shutdown closes surviving sessions, including when startup or
another cleanup fails. All close operations are idempotent.
MapRuntimeIf lives in ui/navigation/map_runtime_if.py; importing a map host
contract must not load application backend infrastructure. The static gate rejects
frontend imports from apps.orcUi.core_runtime as well as direct backend imports.
Weather data health
RadarUiState.data_status carries the controller’s human-readable imagery health
and freshness label. Optional refreshed_at is the Unix time of the last
successful metadata check, distinct from the selected frame’s valid time.
Frontends format that time locally. Detailed errors remain in status.
Tile diagnostics, validation, retries, and freshness decisions belong to weather
controllers and services; views do not inspect those implementations.
City details are part of CityWeatherOverlayState: optional details contains
an immutable selected-city summary and CityWeatherHour rows in Kelvin, m/s,
and metres. CityWeatherPoint.city_id links the map feature to that selection.
request_city_details(city_id) opens a currently displayed city; passing None
dismisses it. Controllers own selection, cached-data lookup, playback pause and
lifecycle invalidation; frontends own local times, units and popup layout.
MusicAnalysisSessionIf describes the existing source-neutral HTTP audio-session
controls. The HTTP routes use this narrow structural contract instead of importing
the concrete session controller; transport paths and response behavior are unchanged.