Skip to content

UI Surface Model

James Daley edited this page Jun 20, 2026 · 2 revisions

UI Surface Model

The UI surface model lets active UI/app modules contribute declarative UI descriptors while the framework owns composition, validation, and final surface state.

Core Rule

Service modules may run concurrently. UI and app modules share one active surface slot.

stateDiagram-v2
    [*] --> NoActiveUI
    NoActiveUI --> UIA: activate UI A
    UIA --> UIB: activate UI B
    UIB --> UIA: activate UI A
    UIA --> NoActiveUI: deactivate UI A
    UIB --> NoActiveUI: deactivate UI B
Loading

When a new UI/app module is activated, it replaces the previous active UI/app module after surface setup and incoming start succeed.

Contribution Types

Contribution Type Capability
Toolbar items ToolbarItemDescriptor toolbar_items
View injections ViewInjectionDescriptor view_injection
Overlay schema OverlaySchema routing_overlay
Toolbar open-overlay action OpenOverlayAction routing_overlay
Toolbar publish-event action PublishEventAction event_publishing
Theme mask ThemeMask reserved framework policy through ui_theme_mask

UIContributions Shape

classDiagram
    class UIContributions {
      optional~ThemeMask~ themeMask
      vector~ToolbarItemDescriptor~ toolbarItems
      vector~ViewInjectionDescriptor~ viewInjections
      optional~OverlaySchema~ overlaySchema
    }
    class ToolbarItemDescriptor {
      string itemID
      string title
      string systemImageName
      ToolbarAction action
    }
    class ViewInjectionDescriptor {
      string injectionID
      string slot
      string viewID
      int priority
    }
    class OverlaySchema {
      vector~NavigationPointer~ navigationPointers
      vector~OverlayRoute~ overlayRoutes
    }
    UIContributions --> ToolbarItemDescriptor
    UIContributions --> ViewInjectionDescriptor
    UIContributions --> OverlaySchema
Loading

Surface Activation Logic

flowchart TD
    Start["activate UI/app module"]
    Contributions["read uiContributions()"]
    Validate["validate capabilities and declared UI IDs"]
    Sanitize["apply themeMask policy"]
    RemovePrev["remove previous contributions"]
    AddIncoming["add incoming contributions"]
    Rebuild["rebuild surface state"]
    StartModule["start incoming module"]
    StopPrev["stop previous module"]
    Persist["persist selected UI/app module"]
    Rollback["stop incoming if needed\nrestore previous surface"]

    Start --> Contributions --> Validate --> Sanitize --> RemovePrev --> AddIncoming --> Rebuild --> StartModule --> StopPrev --> Persist
    AddIncoming -. failure .-> Rollback
    Rebuild -. failure .-> Rollback
    StartModule -. failure .-> Rollback
    StopPrev -. failure .-> Rollback
Loading

UISurfaceManager

UISurfaceManager stores per-module contributions and exposes merged current state:

Getter Meaning
currentThemeMask() Current framework-owned theme mask, if any. Module masks are stripped during activation.
currentToolbarItems() Toolbar descriptors from active contributions.
currentViewInjectionsBySlot() View injections grouped by host slot.
currentOverlaySchema() Active overlay schema.

Hosts can register onChanged() callbacks to respond when surface state changes.

Declared UI IDs

Schema 1.1 manifests declare UI ownership under runtimeRequirements.ui:

Declaration Contribution Field
viewIDs ViewInjectionDescriptor.viewID and overlay view IDs
slotIDs ViewInjectionDescriptor.slot
toolbarItemIDs ToolbarItemDescriptor.itemID
routeIDs OverlayRoute.routeID
pointerIDs NavigationPointer.pointerID
themeIDs ThemeMask.themeID when theme policy permits it

Activation rejects UI/app modules that contribute undeclared IDs.

Theme Mask Policy

ui_theme_mask exists in the object model but remains reserved. A module may declare it, and contribution validation recognizes it. The manager preserves only declared, policy-approved theme IDs in current surface state.

This keeps presentation policy framework-owned until an explicit owner decision changes it.

Example UI Module Contributions

The example UI module contributes:

  • One toolbar item with a navigate action.
  • Three view injections.
  • One overlay navigation pointer.
  • One overlay route targeting the module's overlay view.

These examples demonstrate descriptor shape and capability requirements without granting example modules special privileges.

The example app module contributes an app shell view and toolbar descriptor to demonstrate the app module type sharing the same active surface slot as UI modules.

Clone this wiki locally