Building a distribution
This is a guide, not the contract. What the platform guarantees is specified under
openspec/specs/. For this page:platform-composition·shell-layout. 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.
A distribution is your product: a thin app that composes @loomweaver/shell + your weaver(s), declares
a layout, grants capabilities and sets branding. It’s mostly one file: the composition root.
The composition root
Everything a distribution is lives in one providers array, in src/app/app.config.ts, the file
ng new and Nx both generate. Every “add this provider” instruction in these guides means that array.
src/main.ts, src/app/app.ts and src/app/app.html stay as generated, except that App renders
<lw-shell />; see manual setup for those three files.
import { ApplicationConfig } from '@angular/core';import { provideShell, provideShellRouter, provideLayout, providePlugins, provideTranslationNamespaces, provideCapabilityGrants, type ShellLayout,} from '@loomweaver/shell';import { provideProductIdentity } from '@loomweaver/plugin-sdk';import { notesWeaver } from '@my/notes-weaver';
const layout: ShellLayout = { regions: [ { id: 'top-bar', type: 'bar', dock: 'top' }, { id: 'primary', type: 'rail', dock: 'left' }, { id: 'left-panel', type: 'panel', dock: 'left' }, { id: 'main', type: 'content', dock: 'center' }, { id: 'right-panel', type: 'panel', dock: 'right' }, { id: 'status-bar', type: 'bar', dock: 'bottom' }, ],};
export const appConfig: ApplicationConfig = { providers: [ provideShellRouter(), // content-area routing — replaces provideRouter([]) provideShell(), provideLayout(layout), provideProductIdentity({ name: 'Notes Studio', tagline: 'product.tagline', logoUrl: 'logo.png' }), provideTranslationNamespaces('notes', 'product'), // Default-deny: grant the weaver exactly what its manifest declares. provideCapabilityGrants({ notes: ['contributions', 'ui', 'host'] }), ...providePlugins(notesWeaver), // variadic, returns an array — note the spread ],};That’s the whole product wiring. The shell renders the chrome; the weaver fills it.
Which door does my decision go through?
There is no single god-provider, on purpose: each decision has its own provider, so the same decision never has two doors. The whole surface, indexed by what you want, is one page in the reference: Composition: the provider surface. Everything your own code can do at runtime is the rest of that area: Distribution API.
Seeing what you composed
In dev mode the shell puts one function on the window. Call it from the browser console once your app has finished loading:
loomweaver.report()It prints the regions your layout declares, the capabilities you switched off and the ids you omitted, and then warns about the things that quietly land nowhere:
- an
omitthat matched nothing, with the prefix you probably meant: omittingshell.permissionshides a command or item by that id, while the settings section of that name needssetting:shell.permissions, and the bare form fails in silence - a settings button or menu entry pointing at a command no one registers (or one your own
omitremoved): the shell drops the control rather than drawing a dead one, and this says why it vanished - a keyboard shortcut two commands both claim, naming both and saying which one the shortcut actually runs. The chord goes to whichever registered last, so the other command’s menu entry goes on offering a shortcut that now does something else. That is the part worth seeing: nothing looks broken, it simply does the wrong thing. The comparison is on the chord the keyboard resolves, so it finds a clash between two commands that spelled the same shortcut differently
One check does not wait for the console, because it is already decidable at startup. A bar, rail or
view contribution aimed at a region your layout does not declare, or declares with another anatomy,
is warned about immediately, and the warning names the regions of the right type that do exist. That is the
status versus status-bar mistake, which otherwise ships a product whose status bar is simply
empty.
The report exists in dev only; nothing of it reaches a production build.
The pages
Each page below holds one area a distribution configures, with the provider that does it. Where a
decision needs the reasoning first, the page names the concept page under concepts/ that carries it.
- Layout: regions and docks:
provideLayout, the regions and their docks, panes and sidebar curation. Why: Surfaces and panes. - Content-area routing:
provideShellRouter, the pane that carries the address, following tabs. Why: The address. - Workspaces a product ships:
provideWorkspaces, claims, rail items, unusable workspaces, saved workspaces. Why: Workspaces. - Resetting the arrangement: the workspace reset and the app layout reset, and what each puts back.
- Switching capabilities off:
provideShellFeatures, one switch per gesture and its affordance. - Surface retention: the product-wide
retentiondefault and the unsaved-work question. Why: Retention and unsaved work. - Branding:
provideProductIdentityand the--lw-*tenant theme. - Bringing your own CSS framework: the pre-compiled stylesheet, your framework in a cascade layer, the Bootstrap token mapping and the dark-mode mirror.
- Capabilities:
provideCapabilityGrants, the Permissions section,provideRequiredPlugins. Why: Capabilities and trust. - Auth integration:
provideAuthSource, your own login UI,provideUnauthorizedRedirect. - Persistence stores: the two
KeyValueStoreports, the storage-key inventory, identity-scoped stores. - Windows and sync: cross-tab live sync and pop-out windows, and where a product hooks in.
- Frame plugins:
provideFramePlugins, the frame kit you serve, your CSP. Why: Capabilities and trust. - Plugin store:
providePluginCatalog, the catalogue, consent and updates. - Icons, translations and rewording:
provideIcons, translation namespaces,provideTranslationOverrides. - Recomposing host chrome: replacing, hiding and moving default chrome, the palette entry, curating settings, dropping a route.
- PWA and delivery: the service worker, the manifest, and validating the update flow against a build.
Distribution API is what your own code can do once the product runs.