Skip to content

navigation: federation — contracts, mount, and capabilities - #12569

Open
aurindam wants to merge 5 commits into
auri/upstream/nav-a5from
auri/upstream/nav-federation-v2
Open

navigation: federation — contracts, mount, and capabilities#12569
aurindam wants to merge 5 commits into
auri/upstream/nav-a5from
auri/upstream/nav-federation-v2

Conversation

@aurindam

Copy link
Copy Markdown
Member

Adds the federation layer to the navigator (built on #12554): an app can be assembled from independently-authored, independently-released parts, composed by an integration layer without editing any part's internals.

This supersedes #12565, re-implemented on master's current interface/implement model. The only user-visible change from that earlier draft: a part declares conformance with the body statement implement <Contract> <=> self; instead of the removed implements header. Contracts, needs, and both mount forms are unchanged.

Multi-team composition

A navigation contract (an interface of routes) is the boundary. A part conforms with implement Contract <=> self, declares the capabilities it needs, and runs its own navigator; the integration shell mounts each part at a route and binds those needs.

Runnable: examples/navigation-federated models this with separate owners.

File Owner Role
ui/contract.slint platform the FeatureNav contract + HostServices
ui/media.slint Media team MediaFeature, implement FeatureNav <=> self, needs HostServices
ui/settings.slint Settings team SettingsFeature, same contract
ui/app.slint integration mounts both features + an external plugin, binds the capabilities
// contract (shared, owned by the platform team)
export interface FeatureNav {
    @version(1)
    @uri("app://feature") route Main;
}
export interface HostServices { callback log(string); callback go-home(); }

// a feature (Media team): conforms + states what it needs, runs its own navigator
export component MediaFeature inherits Rectangle {
    implement FeatureNav <=> self;
    needs HostServices;
    in-out property <MediaRoute> current-route: MediaRoute.Main;
    navigator (current-route) { MediaRoute.Main: Library { home => { root.go-home(); } } }
}

// the integration shell composes features without editing them
export component App inherits Window {
    navigator (current) {
        Shell.Home:    HomeScreen { }
        Shell.Media:   mount MediaFeature via FeatureNav {
            log(message) => { debug(message); }
            go-home => { root.navigate(Shell.Home); }
        }
        Shell.Plugin:  mount extern via FeatureNav { component-factory: root.plugin-factory; }
    }
}

What's in it

  • Contracts — an interface carries a navigation contract; route attributes
    @version(n) / @uri("…") make it versioned and deep-linkable. Routes live in a
    side-car on the component, off the interface member lists, so interface member
    projection never sees them.
  • Conformanceimplement Contract <=> self requires the component's navigator to
    cover every contract route by name (checked at build time).
  • Capabilitiesneeds Capability; declares the interface's callbacks as unbound
    members, bound by the integrator at the mount site; an unbound need is a compile error.
  • Build-time mountmount Impl via Contract { … }, a direct instantiation checked
    against the contract.
  • Cross-process mountmount extern via Contract { component-factory: … } via
    ComponentContainer + ComponentFactory, for parts shipped as their own binary.
    examples/navigation-federated demonstrates it end to end: the host builds the plugin
    in main.rs and passes it in as a slint::ComponentFactory.
  • Public factory APIslint_interpreter::ComponentDefinition::create_embedded is now
    public (was #[doc(hidden)] + #[cfg(feature = "internal")]), so a real app can supply
    the factory for an external mount without reaching into internal API.
  • Docs — a new Federated Navigation guide
    (docs/…/guide/development/navigation-federation.mdx).

Notes

  • Experimental: SLINT_ENABLE_EXPERIMENTAL_FEATURES=1.
  • Base: stacked on navigation: experimental declarative navigator construct #12554 (auri/upstream/nav-a5); the diff shows only the federation delta.
  • Tests: interpreter tests for contract conformance, the build-time mount + capability
    binding, and the external factory seam; cross-language driver case
    tests/cases/navigation/mount.slint (Rust/C++/JS/Python).
  • Deferred (follow-ups): route-parameter payloads (parsed and typed, not yet threaded
    through navigate), cross-process capability binding over the extern seam,
    route param-type conformance, and <=> child contracts.

aurindam and others added 5 commits July 21, 2026 14:52
…rmance

Re-implements the navigation-contract slice on master's redesigned interface
model, where conformance is the body statement `implement I <=> self;` (the old
`implements` header was removed).

- grammar: `route` members (RouteDeclaration) with `@version`/`@uri` attributes
  on an `interface`, parsed unconditionally, gated at lowering.
- object tree: an interface's routes collect into a side-car
  `Component.navigation_contract`, kept off the member lists so interface member
  projection never sees routes; `route` outside an interface is rejected.
- conformance: `validate_navigation_contract_conformance` walks the self-
  implemented interfaces from master's `get_implemented_interfaces` and requires
  the component's navigator to cover every contract route by name.

Test `navigation_contract_conformance` covers both the passing and the
missing-route cases. Mount, `needs`, and external mount are later steps.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- needs: `needs Capability;` declares the interface's callbacks as unbound
  members, bound by the integrator at the mount site
  (get_needed_interfaces/apply_needs; verify_mount_capabilities).
- local mount: `Route.X: mount Impl via Contract { bindings }` desugars to a
  direct instantiation, conformance-checked against the contract
  (from_navigator_node + verify_federated_mount/resolve_mount_contract/
  validate_mount_conformance).
- external mount: `mount extern via Contract { component-factory: … }` builds a
  ComponentContainer the host fills at runtime (from_external_mount_node/
  verify_external_mount); FederatedMount.impl_name is None for external.
- route-set fix: lower_navigator marks the route property set so a mounted
  module's internal navigator isn't classified constant (would panic on navigate).

Tests: navigation_federated_mount (mount + unbound-capability + external), and
navigation_internal_route_is_writable (runtime navigate/back on an internal
route). 30 interpreter + 325 compiler tests pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- example: port examples/navigation-federated to the new conformance syntax
  (`implement <Contract> <=> self;` instead of the removed `implements` header);
  registered in the examples workspace. Builds end to end, including the external
  plugin mounted via `mount extern via` whose factory main.rs builds from
  interpreter-loaded source.
- api: make `slint_interpreter::ComponentDefinition::create_embedded` public so a
  host can supply the factory for an external mount without internal API.
- docs: add the Federated Navigation guide (contracts, implement/needs, mount,
  extern), registered in the sidebar after Navigation.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…al route

- tests/cases/navigation/mount.slint: a mounted feature (implement <=> self)
  driven through the outer navigator, in Rust/C++/JS/Python.
- tests/cases/navigation/internal_route.slint: navigate()/back() on a
  non-root component's internal route (the mark_as_set regression).

Green in the Rust driver (LLR codegen) and the interpreter driver.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Compress the wordier inline/doc comments added by the federation slices to match
the repository convention (why, not what). No behavior change.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

@0x6e 0x6e left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool!


/// Resolve the `needs` specifiers on `node` to capability interfaces.
pub(super) fn get_needed_interfaces(
e: &Element,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please spell out variable names in this file (e -> element, tr -> type_register) etc. Here an below.

for NeededInterface { needs_specifier, interface, interface_name } in needed {
let mut members = Vec::new();
for (name, prop_decl) in interface.borrow().property_declarations.iter().filter(|(_, d)| {
matches!(d.property_type, Type::Callback { .. } | Type::Function { .. })

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can a capability provide a property? An interface can provide one, yet they are not applied.

}

export component MediaFeature inherits Rectangle {
implement FeatureNav <=> self;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that once #12566 lands the interface must be fully implemented here. I haven't understood what that means for your @version and @uri yet.

home => { root.go-home(); }
}
MediaRoute.Player: Player {
can-go-back: root.can-go-back;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where does root.can-go-back come from? Or is root here no longer the MediaFeature?

}
all_bound
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if some of the stuff added to object_tree.rs and interfaces.rs should be moved to object_tree/navigation.rs?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants