Skip to content

Dash

Breaking change: rewritten around tabs + controls

The Dash modality was rewritten (wire-breaking). The old UI-tree dump (get_tree / DashTree / DashElement / DashRect) and the list_screens / set_screen / DashScreen surface are gone. The dash is now modelled as a bottom tab bar plus the interactable controls of the current tab: enumerate tabs with list_tabs, switch with set_tab, enumerate the current tab's pressable / scrollable controls with list_controls, and act on one by ref_id with invoke / scroll / highlight. The *_by_label helpers add client-side label/index resolution while the wire stays ref_id-based.

Runnable example

python/examples/dash_navigate.py — opens the ESC dash → list tabs → set tab → list controls → invoke → close.

resoio.dash.DashClient

DashClient(socket_path: str | None = None)

Bases: _BaseClient[DashStub]

Async client for the Resonite IO Dash service over a UDS.

Use as an async context manager so the gRPC channel is closed deterministically. Socket resolution mirrors :class:resoio.ConnectionClient.

Source code in src/resoio/_client.py
def __init__(self, socket_path: str | None = None) -> None:
    # Defer resolution to __aenter__ so env vars patched between
    # construction and connection are honoured, and so resolution
    # errors surface at the connect site.
    self._explicit_path: str | None = socket_path
    self._channel: Channel | None = None
    self._stub: TStub | None = None
    self._resolved_path: str | None = None

open async

open() -> DashState

Open the dash overlay and return the resulting state.

Calling when already open is a no-op that returns the current state. gRPC failures surface as :class:grpclib.exceptions.GRPCError.

Source code in src/resoio/dash.py
async def open(self) -> DashState:
    """Open the dash overlay and return the resulting state.

    Calling when already open is a no-op that returns the current state.
    gRPC failures surface as :class:`grpclib.exceptions.GRPCError`.
    """
    request = DashOpenRequest()
    return _state_from_proto(await self._dispatch(lambda stub: stub.open(request)))

close async

close() -> DashState

Close the dash overlay and return the resulting state.

Source code in src/resoio/dash.py
async def close(self) -> DashState:
    """Close the dash overlay and return the resulting state."""
    request = DashCloseRequest()
    return _state_from_proto(await self._dispatch(lambda stub: stub.close(request)))

get_state async

get_state() -> DashState

Return the current dash state without modifying it.

Source code in src/resoio/dash.py
async def get_state(self) -> DashState:
    """Return the current dash state without modifying it."""
    request = DashGetStateRequest()
    return _state_from_proto(
        await self._dispatch(lambda stub: stub.get_state(request))
    )

list_tabs async

list_tabs() -> list[DashTab]

Enumerate the dash's bottom tab bar as a list of :class:DashTab.

Tabs can be enumerated even while the dash is closed. Each tab carries the language-independent ref_id / locale_key / name that :meth:set_tab and :meth:find_tab address.

gRPC failures surface as :class:grpclib.exceptions.GRPCError.

Source code in src/resoio/dash.py
async def list_tabs(self) -> list[DashTab]:
    """Enumerate the dash's bottom tab bar as a list of :class:`DashTab`.

    Tabs can be enumerated even while the dash is closed. Each tab
    carries the language-independent ``ref_id`` / ``locale_key`` /
    ``name`` that :meth:`set_tab` and :meth:`find_tab` address.

    gRPC failures surface as :class:`grpclib.exceptions.GRPCError`.
    """
    request = DashListTabsRequest()
    tab_list = await self._dispatch(lambda stub: stub.list_tabs(request))
    return [_tab_from_proto(tab) for tab in tab_list.tabs]

set_tab async

set_tab(*, ref_id: str = '', locale_key: str = '') -> DashActionResult

Switch the current tab, selecting by ref_id or locale_key.

ref_id takes precedence: when non-empty it selects the tab by its exact engine ReferenceID; otherwise the tab is matched by its language-independent locale_key (e.g. Dash.Screens.Worlds). Passing neither raises :class:ValueError before any network round trip.

An unresolved ref_id / locale_key is a soft failure (found == ok == False), not an exception. After switching, the new tab's controls are read with :meth:list_controls.

gRPC failures surface as :class:grpclib.exceptions.GRPCError.

Source code in src/resoio/dash.py
async def set_tab(
    self, *, ref_id: str = "", locale_key: str = ""
) -> DashActionResult:
    """Switch the current tab, selecting by ``ref_id`` or ``locale_key``.

    ``ref_id`` takes precedence: when non-empty it selects the tab by its
    exact engine ``ReferenceID``; otherwise the tab is matched by its
    language-independent ``locale_key`` (e.g. ``Dash.Screens.Worlds``).
    Passing neither raises :class:`ValueError` before any network round
    trip.

    An unresolved ``ref_id`` / ``locale_key`` is a soft failure
    (``found == ok == False``), not an exception. After switching, the
    new tab's controls are read with :meth:`list_controls`.

    gRPC failures surface as :class:`grpclib.exceptions.GRPCError`.
    """
    if not ref_id and not locale_key:
        raise ValueError("set_tab requires ref_id or locale_key")
    request = DashSetTabRequest(ref_id=ref_id, locale_key=locale_key)
    return _result_from_proto(
        await self._dispatch(lambda stub: stub.set_tab(request))
    )

list_controls async

list_controls(*, include_disabled: bool = False) -> list[DashControl]

Enumerate the current tab's controls in reading order.

Returns the pressable / scrollable controls of whichever tab is current; switch tabs with :meth:set_tab first to read another tab. When include_disabled is True disabled controls are included too (the default lists only enabled ones). The list is empty when the dash is closed.

gRPC failures surface as :class:grpclib.exceptions.GRPCError.

Source code in src/resoio/dash.py
async def list_controls(
    self, *, include_disabled: bool = False
) -> list[DashControl]:
    """Enumerate the **current** tab's controls in reading order.

    Returns the pressable / scrollable controls of whichever tab is
    current; switch tabs with :meth:`set_tab` first to read another tab.
    When ``include_disabled`` is ``True`` disabled controls are included
    too (the default lists only enabled ones). The list is empty when the
    dash is closed.

    gRPC failures surface as :class:`grpclib.exceptions.GRPCError`.
    """
    request = DashListControlsRequest(include_disabled=include_disabled)
    control_list = await self._dispatch(lambda stub: stub.list_controls(request))
    return [_control_from_proto(control) for control in control_list.controls]

invoke async

invoke(ref_id: str) -> DashActionResult

Press the control identified by ref_id.

Fires the control's interaction (a button press), which may mutate world or dash state. The returned result reports whether the ref_id resolved and whether the action was applied.

gRPC failures surface as :class:grpclib.exceptions.GRPCError.

Source code in src/resoio/dash.py
async def invoke(self, ref_id: str) -> DashActionResult:
    """Press the control identified by ``ref_id``.

    Fires the control's interaction (a button press), which may mutate
    world or dash state. The returned result reports whether the
    ``ref_id`` resolved and whether the action was applied.

    gRPC failures surface as :class:`grpclib.exceptions.GRPCError`.
    """
    request = DashInvokeRequest(ref_id=ref_id)
    return _result_from_proto(
        await self._dispatch(lambda stub: stub.invoke(request))
    )

scroll async

scroll(ref_id: str, *, delta_x: float = 0.0, delta_y: float = 0.0) -> DashActionResult

Scroll the control identified by ref_id by (delta_x, delta_y).

delta_x / delta_y are added to the ScrollRect 's normalized position ([0, 1] space). The result reports whether the ref_id resolved and whether the scroll was applied.

gRPC failures surface as :class:grpclib.exceptions.GRPCError.

Source code in src/resoio/dash.py
async def scroll(
    self, ref_id: str, *, delta_x: float = 0.0, delta_y: float = 0.0
) -> DashActionResult:
    """Scroll the control identified by ``ref_id`` by ``(delta_x,
    delta_y)``.

    ``delta_x`` / ``delta_y`` are added to the ``ScrollRect`` 's
    normalized position (``[0, 1]`` space). The result reports whether the
    ``ref_id`` resolved and whether the scroll was applied.

    gRPC failures surface as :class:`grpclib.exceptions.GRPCError`.
    """
    request = DashScrollRequest(ref_id=ref_id, delta_x=delta_x, delta_y=delta_y)
    return _result_from_proto(
        await self._dispatch(lambda stub: stub.scroll(request))
    )

highlight async

highlight(ref_id: str) -> DashActionResult

Hover-highlight the control identified by ref_id.

Highlight only moves the visual hover; it does not press the control. A control that does not support hover (e.g. a ScrollRect) is a soft rejection (found == True, ok == False). Use :meth:invoke to act on a control.

gRPC failures surface as :class:grpclib.exceptions.GRPCError.

Source code in src/resoio/dash.py
async def highlight(self, ref_id: str) -> DashActionResult:
    """Hover-highlight the control identified by ``ref_id``.

    Highlight only moves the visual hover; it does not press the control.
    A control that does not support hover (e.g. a ``ScrollRect``) is a
    soft rejection (``found == True``, ``ok == False``). Use
    :meth:`invoke` to act on a control.

    gRPC failures surface as :class:`grpclib.exceptions.GRPCError`.
    """
    request = DashHighlightRequest(ref_id=ref_id)
    return _result_from_proto(
        await self._dispatch(lambda stub: stub.highlight(request))
    )

find_tab async

find_tab(query: str) -> DashTab

Resolve query to a single :class:DashTab (client-side).

query matches a tab's ref_id / locale_key / name / label via :func:_resolve_one (exact, then case-insensitive exact, then unique substring). Raises :class:DashNoMatchError or :class:DashAmbiguousMatchError when the query is unresolvable.

Source code in src/resoio/dash.py
async def find_tab(self, query: str) -> DashTab:
    """Resolve ``query`` to a single :class:`DashTab` (client-side).

    ``query`` matches a tab's ``ref_id`` / ``locale_key`` / ``name`` /
    ``label`` via :func:`_resolve_one` (exact, then case-insensitive
    exact, then unique substring). Raises :class:`DashNoMatchError` or
    :class:`DashAmbiguousMatchError` when the query is unresolvable.
    """
    return _resolve_one(await self.list_tabs(), query, _tab_keys)

set_tab_by_label async

set_tab_by_label(query: str) -> DashActionResult

Resolve query to a tab and switch to it by its ref_id.

Convenience over :meth:find_tab + :meth:set_tab; the wire request always carries the resolved ref_id. Resolution errors surface as :class:DashNoMatchError / :class:DashAmbiguousMatchError.

Source code in src/resoio/dash.py
async def set_tab_by_label(self, query: str) -> DashActionResult:
    """Resolve ``query`` to a tab and switch to it by its ``ref_id``.

    Convenience over :meth:`find_tab` + :meth:`set_tab`; the wire request
    always carries the resolved ``ref_id``. Resolution errors surface as
    :class:`DashNoMatchError` / :class:`DashAmbiguousMatchError`.
    """
    tab = await self.find_tab(query)
    return await self.set_tab(ref_id=tab.ref_id)

find_control async

find_control(query: str) -> DashControl

Resolve query to a single control in the current tab.

query matches a control's ref_id / locale_key / label via :func:_resolve_one. Raises :class:DashNoMatchError or :class:DashAmbiguousMatchError when the query is unresolvable.

Source code in src/resoio/dash.py
async def find_control(self, query: str) -> DashControl:
    """Resolve ``query`` to a single control in the current tab.

    ``query`` matches a control's ``ref_id`` / ``locale_key`` / ``label``
    via :func:`_resolve_one`. Raises :class:`DashNoMatchError` or
    :class:`DashAmbiguousMatchError` when the query is unresolvable.
    """
    return _resolve_one(await self.list_controls(), query, _control_keys)

invoke_by_label async

invoke_by_label(query: str) -> DashActionResult

Resolve query to a control and :meth:invoke it by ref_id.

Source code in src/resoio/dash.py
async def invoke_by_label(self, query: str) -> DashActionResult:
    """Resolve ``query`` to a control and :meth:`invoke` it by
    ``ref_id``."""
    control = await self.find_control(query)
    return await self.invoke(control.ref_id)

scroll_by_label async

scroll_by_label(
    query: str, *, delta_x: float = 0.0, delta_y: float = 0.0
) -> DashActionResult

Resolve query to a control and :meth:scroll it by ref_id.

Source code in src/resoio/dash.py
async def scroll_by_label(
    self, query: str, *, delta_x: float = 0.0, delta_y: float = 0.0
) -> DashActionResult:
    """Resolve ``query`` to a control and :meth:`scroll` it by
    ``ref_id``."""
    control = await self.find_control(query)
    return await self.scroll(control.ref_id, delta_x=delta_x, delta_y=delta_y)

highlight_by_label async

highlight_by_label(query: str) -> DashActionResult

Resolve query to a control and :meth:highlight it by ref_id.

Source code in src/resoio/dash.py
async def highlight_by_label(self, query: str) -> DashActionResult:
    """Resolve ``query`` to a control and :meth:`highlight` it by
    ``ref_id``."""
    control = await self.find_control(query)
    return await self.highlight(control.ref_id)

resoio.dash.DashState dataclass

DashState(is_open: bool, open_lerp: float)

Snapshot of the dash (userspace overlay) open/close state.

open_lerp is the open/close animation lerp in [0.0, 1.0].

resoio.dash.DashTab dataclass

DashTab(
    ref_id: str, locale_key: str, name: str, label: str, is_current: bool, enabled: bool
)

A single tab in the dash's bottom tab bar.

ref_id (the tab slot's engine ReferenceID) and locale_key (the tab label's LocaleStringDriver key, e.g. Dash.Screens.Worlds) are the language-independent keys that :meth:DashClient.set_tab addresses; name is the slot name (e.g. Worlds) and label is the localized display text. is_current is True for the tab currently shown; enabled reports whether the tab is navigable (e.g. Contacts is False while logged out).

resoio.dash.DashControl dataclass

DashControl(
    ref_id: str,
    control_type: str,
    label: str,
    locale_key: str,
    enabled: bool,
    parent_ref_id: str,
    depth: int,
)

A single interactable control within the current tab.

control_type is "button" (pressable) or "scroll" (scrollable). ref_id (the slot's engine ReferenceID) is the stable key that :meth:DashClient.invoke / :meth:DashClient.scroll / :meth:DashClient.highlight address. label is the human-readable text (may be empty for icon-only buttons) and locale_key is the language-independent key when present. parent_ref_id links the control to its nearest enumerated control ancestor (empty at the top) and depth is that light hierarchy's depth (top = 0).

resoio.dash.DashActionResult dataclass

DashActionResult(ok: bool, found: bool, ref_id: str, detail: str)

Result of a mutating dash action.

found indicates whether the requested ref_id resolved to a tab or control (ok is always False when found is False). ref_id echoes the resolved target and detail carries the reason when the action could not be applied (e.g. type mismatch, disabled).

resoio.dash.DashNoMatchError

Bases: ValueError

A label / index query did not match any tab or control.

resoio.dash.DashAmbiguousMatchError

Bases: ValueError

A label query matched more than one tab or control.

The message lists the matching candidates so the caller can narrow the query (or use an exact ref_id).