Skip to content

Host UI and Surfaces

Jim Daley edited this page May 1, 2026 · 3 revisions

Host UI and Surfaces

Forsetti does not let modules mutate host UI directly. UI modules publish structured contributions, and the host decides how those contributions are rendered.

Surface Composition

flowchart LR
    Active["Active UI/app module"] --> Contributions["UIContributions"]
    Contributions --> Surface["UISurfaceManager"]
    Surface --> Toolbar["toolbarItems"]
    Surface --> Slots["viewInjectionsBySlot"]
    Surface --> Overlay["overlaySchema"]
    Surface --> Theme["themeMask"]
    Toolbar --> Host["Host view"]
    Slots --> Host
    Overlay --> Host
    Theme --> Host
Loading

ModuleManager sanitizes UI contributions before applying them. themeMask is currently stripped because framework shell theming is reserved. The default runtime model applies contributions from the active UI/app module only; switching UI modules deactivates the previous UI module and clears its contributions.

Contribution Types

Type Structure Host Responsibility
Toolbar item ToolbarItemDescriptor Render title/icon and invoke ToolbarAction.
View injection ViewInjectionDescriptor Resolve viewID through ForsettiViewInjectionRegistry and place it in a known slot.
Overlay schema OverlaySchema Resolve pointer and route IDs through OverlayRouting.
Theme mask ThemeMask Reserved by current runtime sanitization.

View Injection Workflow

sequenceDiagram
    participant Module as UI Module
    participant Manager as ModuleManager
    participant Surface as UISurfaceManager
    participant Registry as ViewInjectionRegistry
    participant Host as Host View

    Module-->>Manager: uiContributions.viewInjections
    Manager->>Surface: apply(moduleID, contributions)
    Surface-->>Host: viewInjectionsBySlot
    Host->>Registry: resolve(viewID)
    Registry-->>Host: AnyView
    Host-->>Host: render slot by priority
Loading

View injections are sorted by slot, then by descending priority inside each slot.

Toolbar Actions

Action Runtime Behavior
navigate(pointerID:) Calls runtime.openPointer(pointerID) through the configured router.
openOverlay(routeID:) Calls runtime.openRoute(routeID) through the configured router.
publishEvent(type:payload:) Publishes a ForsettiEvent on the runtime event bus.
flowchart TD
    Click["User clicks toolbar item"] --> Action{"ToolbarAction"}
    Action -- navigate --> Pointer["openPointer(pointerID)"]
    Action -- openOverlay --> Route["openRoute(routeID)"]
    Action -- publishEvent --> Event["eventBus.publish"]
    Pointer --> Router["OverlayRouting"]
    Route --> Router
    Event --> Subscribers["Framework/module subscribers"]
Loading

Overlay Routing

ForsettiHostOverlayRouter resolves active overlay pointers and routes from UISurfaceManager.overlaySchema.

flowchart TD
    Request["Pointer or route request"] --> Schema{"Active overlay schema?"}
    Schema -- no --> NoSchema["schemaUnavailable"]
    Schema -- yes --> Found{"Reference found?"}
    Found -- no --> Missing["missingPointer or missingRoute"]
    Found -- yes --> Destination{"Destination type"}
    Destination -- base --> Base{"Known base destination?"}
    Destination -- moduleOverlay --> Slot{"Known slot?"}
    Base -- yes --> BaseOK["pointerResolved or routeResolved"]
    Base -- no --> BadBase["invalidBaseDestination"]
    Slot -- yes --> SlotOK["routeResolved"]
    Slot -- no --> BadSlot["invalidSlot"]
Loading

Host Shell Guidance

Context Recommended Host Behavior
Production Pattern A Hide shell controls and render the app module's primary view directly.
Production Pattern B Render the active UI/app module and keep service module controls out of end-user UI.
Developer Pattern C Show module lists, lock state, compatibility details, selected module, toolbar feedback, and errors.
Dashboard Pattern D Show intentional navigation among app modules and make the dashboard a product surface.

Surface Safety Rules

  • Treat UISurfaceManager state as presentation input, not domain truth.
  • Register every injectable viewID with the host's ForsettiViewInjectionRegistry.
  • Keep slot names stable and documented in the host app.
  • Avoid embedding host-only assumptions inside reusable modules.
  • Prefer toolbar actions and overlay routes over direct host references.
  • Declare toolbar_items, view_injection, and routing_overlay capabilities before contributing those surfaces.

Clone this wiki locally