Docs
/

@leumas/site-map

The live 'you are here' map of a product's navigation. Walks any resolved nav manifest into a graph of domains → pages → tabs → sub-tabs, locates the current pathname in it, emits it as mermaid, and...

@leumas/site-map

The live "you are here" map of a product's navigation — a GTA-style radar you can navigate by clicking, plus the expanded panel behind it.

It walks any resolved nav manifest into a graph of root → pillar → domain → page → tab → sub-tab, locates the current pathname in it, and draws it two ways. It also emits that graph as mermaid, and ops/tools/domain-diagrams/index.mjs (pnpm diagrams) uses the same emitter — so the 39 reference pages in ops/reference/diagrams/ and the map in the HUD are one drawing routine, not two that agree until the next tab is renamed.

Using it

/* SUBPATHS, never the barrel, on a boot path — see "Things that will bite". */
import { SiteMapProvider } from '@leumas/site-map/provider';
import { siteMapScreen } from '@leumas/site-map/screen';
import SiteMapRadarSlot from '@leumas/site-map/radar-slot';
import '@leumas/site-map/site-map.css';

/* MODULE SCOPE. LeviathanWidget keys its open sheet on this array; a fresh identity per render
   remounts the map under whoever is reading it. */
const ASSISTANT_SCREENS = [siteMapScreen()];

<SiteMapProvider nav={resolved} pillars={PILLARS} rootLabel="Leumas Studio" rootHref="/admin">
  <AppLayout assistantScreens={ASSISTANT_SCREENS}>
    <SiteMapRadarSlot />
    …
  </AppLayout>
</SiteMapProvider>

SiteMapRadarSlot is what makes the radar (and the panel's "Pin as radar") work. Mount it and forget it: while the radar is closed it reads one value from a store and renders null, and the radar's own module is never fetched.

The provider must sit above whatever draws the map. In Studio that means outside AppLayout, because AppLayout is what renders the Leviathan widget that hosts the map screen.

For a product with no assistant — the control plane runs assistant={false} on purpose — drop the screen and keep the slot, which is the whole reason the radar is not Leviathan-only:

<SiteMapProvider nav={VENDOR_NAV} rootLabel="Control plane" rootHref="/">
  <AppLayout assistant={false}>
    <SiteMapRadarSlot storageKey="leumas.siteMap.control.v1" />
    …
  </AppLayout>
</SiteMapProvider>

storageKey is per product. Sharing one would mean opening the radar in Studio opened it in the control plane too.

The public surface

ImportWhat it is
SiteMapProvider / useSiteMap()builds the graph once, re-locates on navigation. { graph, scoped, here, level, zoom }
SiteMapRadarSlotmount this, not the radar — a store read while closed, lazy radar when opened
SiteMapRadarthe compact floating radar itself — corner-docked, three sizes, closable
SiteMapPanelthe expanded map: [Radar | Diagram], .mmd, Pin
SiteMapStagethe HUD chrome + pan/zoom surface both renderers sit on
SiteMapViewjust the radar SVG, if you want to place it yourself
usePanZoom()drag-to-pan, wheel-to-zoom, fit-to-view — for any canvas-like child
siteMapScreen()a LeviathanWidget speed-dial screen descriptor
useNavConfirm(onGo) / NavConfirmthe "Go to X?" gate — ONE gate both graphs share
setRadarOpen(key, open?)open/close/toggle the radar from anywhere — a menu, a shortcut
@leumas/site-map/graphpure, no ReactbuildSiteGraph, buildDomainGraph, buildProductGraph, locate, scopeGraph
@leumas/site-map/mermaidpure, no React, no NodetoMermaid, escapeLabel, DIAGRAM_PALETTE

Things that will bite

  • ./graph and ./mermaid must stay React-free. pnpm diagrams imports them in bare Node with

no bundler. A React import there does not fail loudly; it breaks the script that keeps the checked-in reference diagrams honest.

  • .lms-hud-tokens on the host element is load-bearing. The neon palette (--hud-cyan,

--hud-violet, --hud-line) is declared on .lms-hud, .lms-hud-tokens in hud-shell.css. Drop the class and the widget renders as a flat borderless box with no error anywhere.

  • An href can be claimed by two nodes. Studio's pillars point straight at their headline domain,

so seven live paths have two claimants. locate breaks the tie by DEPTH, and seal must recompute depth rather than trust the one a sub-graph already carried — trusting it made every pillar tie with the domain beneath it and win by array order.

  • trail vs crumbs. Studio really does nest a Devices pillar → a Devices domain → a Devices

page. Light the trail; print the crumbs, which collapse a run of identical labels.

  • Never import the barrel from a product's App.jsx. No package here declares sideEffects, so

a bundler will not shake it: importing SiteMapProvider from @leumas/site-map measurably dragged the panel, the SVG renderer and treeLayout2D onto Studio's boot path — 272.1KB gzip with subpaths versus 284.8KB with the barrel. /provider, /screen, /radar-slot, /store, /graph and /mermaid are the boot-path-safe entries.

  • The viewBox is in PIXELS. A 0..1 viewBox was the first attempt: vector-effect: non-scaling-stroke

rescues strokes there, but nothing rescues TEXT — a font-size: 0.026px label is clamped up to the browser's minimum rendered size while its advances stay tiny, so every label draws as full-size letters at hairline spacing.

  • Mermaid is ~600KB and lives behind exactly one lazy() edge (MermaidView.jsx). Nothing else

in the package imports it. pnpm check:boot is what proves that claim.

  • Every pick is a QUESTION, not a jump. useNavConfirm sits between the renderers and

navigate(), so a radar dot and a diagram box both raise "Go to X?" with Yes/No (Enter/Escape). It is intercepted ONCE in SiteMapPanel, which is why neither renderer knows the gate exists and a third would inherit it. NOT the kit's ConfirmDialog — that is a Modal for irreversible actions, and .st-modal__backdrop is position: fixed and unportalled, which canvas-hud.css says is trapped by a <CanvasHud>'s container-type.

  • Float the gate off --lms-chud-footer-h, the height CanvasHud measures with a ResizeObserver

(canvas-hud.css:181 uses the same expression). A guessed offset landed the question on top of the zoom control, because the footer's height depends on what a surface puts in it.

  • The Diagram view is ALWAYS the whole domain, deliberately — the zoom ladder

(domain → pillar → site) drives the RADAR only. The radar is an instrument that has to stay legible; the diagram is what you open to see your section whole. Scoping the diagram to the ladder meant "zoom out" quietly replaced the picture you asked for.

  • Mermaid's node ids are NOT flowchart-<id>-<n> — they carry the render id too

(lms-smap-n_devices_door-2-flowchart-n_devices_door-0). idFromMermaidNode splits on the LAST flowchart-; anchoring at the start matched nothing and made every diagram node silently unclickable.

  • Fit with offsetWidth, never getBoundingClientRect(). The rect is the TRANSFORMED box, so

fitting from it needs the current scale divided back out — and reading that scale inside a ResizeObserver races the render that set it. offsetWidth is the untransformed layout size, and because ResizeObserver does not fire on a transform, fit() writing a new scale cannot loop.

  • Container queries against .lms-chud must NAME it (@container lms-chud (...)).

canvas-hud.css sets container-name and warns why: @leumas/inputs puts an ANONYMOUS container-type: inline-size on its controls, so an unnamed query resolves against whichever slider is the nearest ancestor.

  • treeLayout2D sorts siblings alphabetically, so the radar is alphabetical where the sidebar is

authored. That is a property of the shared layout (it must be stable for data arriving in any order), not something this package chose.

What it exports

49 exports: 16 components, 4 hooks, 29 helpers. Import from @leumas/site-map.

DIAGRAM_PALETTEDIAGRAM_PALETTEDOMAIN_CLASSESDOMAIN_CLASSESLEVELSLEVELSNavConfirmRADAR_KEYRADAR_KEYSiteMapPanelSiteMapProviderSiteMapProviderSiteMapRadarSiteMapRadarSlotSiteMapStageSiteMapViewuseNavConfirmusePanZoomuseSiteMapuseSiteMapbuildDomainGraphbuildDomainGraphbuildProductGraphbuildProductGraphbuildSiteGraphbuildSiteGraphescapeLabelescapeLabelgetRadarStategetRadarStateidFromMermaidNodelocatelocatenidnidnormalizeRadarStatenormalizeRadarStatescopeGraphscopeGraphsetRadarOpensetRadarOpensetRadarStatesetRadarStatesiteMapScreensiteMapScreensubscribeRadarsubscribeRadartoMermaidtoMermaid
Source shared/packages/site-map/README.md (no-git)markdownjson
Generated from the Leumas repository. Every page cites the file it came from.leumas.techllms.txt