Skip to content

Composition: the provider surface

This is a guide, not the contract. What the platform guarantees is specified under openspec/specs/. For this page: platform-composition · host-services. Where this page and a specification disagree, the specification is right, and that is a defect in this page: change the behaviour there, then explain it here.

There is no single god-provider, on purpose: each decision has its own provider, so the same decision never has two doors. This page is the whole surface indexed by what you want; each row points at the guide section that tells the story.

What the product is

I want to … provider
set the name, logo and tagline provideProductIdentity (Branding)
decide which regions exist and where provideLayout (Layout)
recolour the whole app the design tokens (tokens)
change sizes, radii, density your own CSS on the class contracts (tokens)
replace a built-in icon provideIcons (Icons)
reword the shell itself (“Folder” instead of “View”) provideTranslationOverrides (Rewording)
ship my own translations provideTranslationNamespaces (i18n)

What users are allowed to do

I want to … provider
take a gesture away (splitting, pinning, pop-out, shortcuts …) provideShellFeatures (Switching capabilities off)
change a switch while the app runs, or read it FeatureSwitches (Switches)
offer a pane, workspace, sidebar or reset action from my own control the services on these pages (Panes, Workspaces, Sidebars, Resetting the application)
drop a built-in command, item, settings row, menu entry or route provideShell({ omit }) (Recomposing host chrome)
hand out layouts the product defines provideWorkspaces (Developer-defined workspaces)
let users put the arrangement back nothing: shell.app.reset ships (Resetting the arrangement)

What ships inside

I want to … provider
compose my weaver providePlugins + provideCapabilityGrants (Capabilities)
keep a plugin from being switched off provideRequiredPlugins (A plugin your application cannot run without)
run an isolated plugin provideFramePlugins (Frame plugins)
offer a plugin catalogue providePluginCatalog (Plugin store)
add chrome of my own provideBarItems, provideRailItems, provideViews (Do it below)
put a search entry in a bar provideCommandPaletteEntry, provideQuickOpenEntry (Command palette entry)

What it talks to

I want to … provider
feed the signed-in user in provideAuthSource (Auth integration)
send gated routes to my login provideUnauthorizedRedirect (Redirect)
store settings in my backend provideSettingsStore (Persistence stores)
store working state in my backend provideWorkingStateStore (Persistence stores)
keep two users in one browser apart provideIdentityScopedStores (Identity-scoped stores)
compute a following tab’s address myself provideTabAddressResolver (Following tabs)
route the content area provideShellRouter (Content-area routing)
ship without a service worker provideShell({ serviceWorker: false }) (PWA)
keep hidden surfaces alive by default provideShell({ retention }) (Surface retention)

Do it

A distribution does not need a plugin to add chrome. Three providers contribute the same shapes a weaver contributes, statically at composition time:

// src/app/app.config.ts — in the providers array
...provideViews({ id: 'acme.inspector', title: 'acme.inspector.title', region: 'left-panel', component: Inspector }),
...provideBarItems({ id: 'acme.status', bar: 'status-bar', slot: 'end', component: BuildStatus }),
...provideRailItems({ id: 'acme.help', rail: 'primary', icon: 'help', title: 'acme.help', anchor: 'bottom',
command: 'acme.openHelp' }),

Read it

ContributionRegistry is the registry underneath the providers. Three of its signals answer questions about your own composition, and are what loomweaver.report() reads:

Signal Holds
omitted the ids your omit list names, exactly as you wrote them, prefixes and all
registeredIds every id registered so far, of any kind, including the ones omit hides
registeredCommands every command with the plugin the host stamped on it as its owner (RegisteredCommand); the shell’s own commands carry no owner, which is what tells one plugin’s commands from another’s

What asks about unsaved work

Nothing on this page asks; a provider runs once at composition time, before any surface exists.

Switched off

No switch governs the providers. provideShellFeatures is where the switches themselves are declared; Switches reads and changes them while the app runs.

In depth

Ids address regions. region, bar and rail must name a region id declared in your provideLayout; the ids above match the getting-started layout. A contribution addressing an id no region declares renders nowhere, and a view logs a dev-mode warning.

Ids replace. Because ids are the addressing scheme everywhere, using an existing id replaces that contribution, which is how the demo moves the update badge into a sidebar footer. provideShell({ omit: [...] }) removes one.

At runtime. Injecting ContributionRegistry lets you add and remove contributions while the app runs; addRailItem returns a disposer. Prefer the providers when the answer is known at composition time.

Reading registeredIds. It is the only way to tell an omit that hid something from one that hit nothing at all, because an omitted contribution is by construction absent from every other signal. Reach for the report first: it already phrases the answer, including which prefix you probably meant.

Where the story is told