@leumas/domain-kit
A DOMAIN DOOR, once — the overview HUD, its view host, the console nav view and the manifest→routes/aside/crumbs/⌘K derivation every product's admin repeats. The 3D room is a separate, optional...
@leumas/domain-kit — a domain door, once
Leumas Studio and Leumas Admin are different products for different people, and they draw a domain the same way on purpose: a door you land on, a deck of what is inside it, view modes over that same list, a tab strip under each page, one aside, one breadcrumb trail, one ⌘K. An operator moves between the two consoles all day, so "the same" has to mean the same code, not the same screenshot.
This package is that code. It was Studio-local until every one of its behaviours had to exist in the control plane too.
Subpaths
| Import | What you get | Touches three? |
|---|---|---|
@leumas/domain-kit | DomainOverviewHud, useSeededFocus, DomainViewHost, DOMAIN_VIEWS, useArrowStep, registerDomainRoom, getDomainRoom, HudDeckFigure, useSettledFocus, useDomainScope, scopeHref/unscopeHref/scopeFromPath | no |
@leumas/domain-kit/routes | DomainRoutes, DomainLanding, DomainHub, domainsToRoutes, blockingFeature, CrumbTitleContext, useCrumbTitle | no |
@leumas/domain-kit/console-view | side effect: registers the console nav view | no |
@leumas/domain-kit/room | side effect: registers the 3D room | yes |
@leumas/domain-kit/room/backdrop | DomainWorldBackdrop for a surface drawing its own stage | yes |
@leumas/domain-kit/domain-hud.css | .lms-dhud__* (the shell imports it itself) | no |
The room is opt-in, and that is a bundle decision
DomainOverviewHud does not import the backdrop. It asks roomRegistry.js for a component, and a product opts in with one line in its entry:
import '@leumas/domain-kit/console-view';
import '@leumas/domain-kit/room'; // ← without this line, no renderer in the build
lazy(() => import('./DomainWorldBackdrop.jsx')) inside the shell would look free — it is behind a promise — but it is an unconditional module edge, so every consumer emits a three chunk whether or not any door passes a load. Runtime already degrades correctly, so the defect is invisible in a browser and only ever shows up as a renderer in someone's dist/. A product without the room import gets identical doors minus the backdrop: they render their fallbackStage.
pnpm check:huds asserts the shell mentions neither three nor @leumas/cinematic, and that no domain console imports /room directly.
A character-select deck: HudDeckFigure + useSettledFocus
Two doors draw a deck where the item IS the picture (chrome="none") — AI's personas and Hosting's websites — and they arrived at the same box independently. HudDeckFigure is that box: a poster that is always there and always cheap, an optional live layer over it, a plate, and pills that render only on the centred item.
The poster is never unmounted, only faded. That is the entire failure story: it costs one composited element, the card never reflows when the expensive layer swaps in, and there is no code path to write for a snapshot that never rendered, a document that refused to frame, or a dead WebGL context — because the failure state is the resting state.
useSettledFocus(focusId, ms) reports the focus id only once the deck has stopped moving. Both decks need it for the same reason: a held arrow key steps several cards a second, and mounting the expensive layer per step churns WebGL contexts (browsers cap them near sixteen and drop the oldest, so a long deck starts killing the room drawn behind it — with the symptom appearing nowhere near the cause).
[warning] Not useSeededFocus, which picks the initial focus. Adjacent name, unrelated job.
Five layout rules live in domainhud.css beside the code they constrain, each one a defect that already shipped: the media is position: absolute; inset: 0 and never block-size: 100%; one z-index puts both layers over a door's decorative pseudo-elements; transitions are transform and opacity only; no backdrop-filter anywhere on a deck over a live canvas; a filter may sit on a still element and never on a moving one.
A scoped domain: the thing is in the URL
Some domains are about one of the operator's things — Hosting is about one website, and every one of its tabs is. That subject belongs in the address, not in a component's state:
/admin/hosting/sites/<siteId>/domains
The nav manifest declares those tabs unscoped (/admin/hosting/sites/domains) and marks the sub-item scoped: true. It has to: four guard scripts and the diagram generator read the manifest in bare Node, where there is no current site and never can be. So the manifest states the shape and DomainRoutes reads the id out of the location, publishing it on DomainScopeContext.
Compare unscoped, emit scoped. sectionRows decides which row is active by matching its hrefs against the location, so it is given the location with the id removed; the rows it returns get the id put back. Reversing that is silent — nothing matches, no row is active, and the aside looks like it lost its place rather than like it has a bug. scopePath.js is plain JS so a bare-Node test can pin both directions (test/domain-scope.test.js).
With no scoped sub-item in a domain, useDomainScope() returns null and every consumer behaves exactly as it did before scopes existed — which is what keeps the other doors byte-identical.
The two rules the shell will not break
- It never fetches. Every domain keeps its own data source and hands down
items,status,
loading, onRetry.
- It never gates.
useCanvasCapable()belongs in the caller's default export — except under
downgrade: 'stage', where the registered room asks it and swaps only the stage.
One AdminSurface, ever. It is rendered by DomainOverviewHud, so a caller must not wrap it in another.
Adding a domain to a product
<domain>/model/tabs.js— THE tab list, pure data, no JSX (guards import it in bare Node).<domain>/hud/overview.js— the spec: `{ id, base, hudLabel, deckLabel, surface, select, footer,
downgrade, room? }. id` must equal the folder name.
<domain>/hud/<Domain>Populators.js—(api) => Promise<inhabitant[]>, **contracted never to
reject**. Only if the door draws a room.
<domain>/hud/<Domain>Hud.jsx— the door:DomainViewHostwhoserenderConsoleis a
DomainOverviewHud spread from the spec.
<domain>/pages/<Domain>Hub.jsx— the tabbed surface:RouteTabs+ a nested<Routes>of lazy
panels. activeTabId() is the only source of "which tab is open".
- One manifest sub-item carrying
tabs, and oneLANDINGSentry for the door.
node ops/tools/domain-scaffold/index.mjs --product admin --domain <id> writes 1, 2, 4 and 5 from the manifest.
What it exports
66 exports: 28 components, 11 hooks, 27 helpers. Import from @leumas/domain-kit.
ACT_VERBSActiveDomainContextActiveDomainContextCrumbTitleContextDOMAIN_VIEWSDomainDoorDomainHubDomainLandingDomainOverviewHudDomainRailDomainRoomDomainRoutesDomainScopeContextDomainScopeProviderDomainViewHostDomainWorldBackdropDRILL_PARAMGenericDomainOverviewHudDeckBadgeHudDeckFigureMAX_ACTSPILLAR_FALLBACKPILLAR_ROOMRedirectSubtreeSETTLE_MSSTAGE_MAXToneScopeXuseActiveDomainuseActiveDomainuseArrowStepuseCrumbTitleuseDomainCountsuseDomainEntitiesuseDomainScopeuseDomainSectionsuseDrilluseSeededFocususeSettledFocusactsForblockingFeatureclearDomainCountsclearDomainEntitiescountsByKinddeckItemsFordomainsToRoutesgetDomainRoomindexIteminhabitantsFromEntitiesinhabitantsFromTabsisOnlookForoverviewForpeekDomainEntitiespillarRoomprimaryActForreadDomainEntitiesreadScopeSearchregisterDomainRoomscopeFromPathscopeHrefscopeSearchstripBasetoneForturnForunscopeHref