PrivatecontainerPrivaterootPrivatecameraPrivatecontextClose handle of the open context menu, if any.
PrivateaudioThe sound layer, for sound rows (null before wiring / in tests).
PrivatescenePrivatescenePath index over sceneGraph: every leaf's root→leaf chain in O(1).
PrivateanimationPrivatestatePrivateapplyScene-application engine: recomposes + pushes attrs to materials. Constructed with ACCESSORS for rootGroup/sceneNodeIndex (both reassigned in initFromScene), never captured values — see the stale-capture pitfall.
PrivatecontrolsThe controls section (sliders/selects/LOD readout) below the list.
PrivatepanelPrivatelistPrivatefilterLive filter over layer names; only rendered when the scene has many layers.
PrivatefilterPrivatenoPrivatefilterPrivatevisiblePrivateeventsTracks every event listener attached during buildPanel/renderList so clear()/dispose() can tear them all down with a single call. Without this, listeners attached to detached DOM nodes hold closures referencing the panel until the GC reclaims the subtree — fragile, hard to test, and inconsistent with the rest of the viewer's listener-tracking pattern. (The controls section's listeners are tracked by LayerControls' own group.)
PrivaterowPrivatefailedFailed-load provider (paths + per-path reason), injected by the app after
initFromScene (see core/app/dataset/load-dataset.ts). The SAME provider
the data monitor uses — reads the loader's live failure set. Null before a
scene loads and after dispose.
PrivatepickLate-bound accessor to the current PickingSystem's markDirty, injected by
the app. Kept as a callback (not a captured PickingSystem) so it survives the
per-dataset picking re-creation. Null before wiring / in tests.
PrivatelastFallback for providers without a version. The JSON signature includes
sorted [path, reason] pairs so reason-only changes refresh tooltips.
Both detectors use null as a reset sentinel when rows or providers change.
PrivatelastPrivateunsubscribePrivateresizePrivate Static ReadonlyFILTER_Scenes with more layers than this get the live name filter.
Expose layer state for external consumers (e.g., colormap legend).
Initialize the panel from a loaded scene. Must be called after the scene is loaded and rootGroup is available.
sceneIndex is the scene loader's SceneNodeIndex over sceneGraph
(SceneLoader.sceneNodeIndex); without one, or with one over another
graph, the panel builds its own (one O(N) pass).
OptionalsceneIndex: SceneNodeIndex | nullReset every layer's parameters — visibility, display range, gamma, opacity, blending mode, colormap, the mesh shading values (Ambient, Shade falloff, Specular, Shininess, Alpha cutoff), a physical mesh's knobs, a sound row's gain and a locked Active level — back to their authored defaults.
Re-derives the default state from the scene graph (the same walk
initFromScene uses) and pushes every parameter through the regular
apply paths, so the materials, the row list, and the controls all agree.
No-op before a scene loads or when the scene exposes no layers.
PrivatereapplyPush a freshly re-derived layer back through every apply path — the one apply set both resets share, so the scene and the audio graph agree with the readouts afterwards.
Style a data leaf the scene loader built AFTER this panel initialised (a
partition part activated by the LOD registry, a lazily built level) with
its layers' CURRENT state, before it is first drawn. Wired by
core/app/dataset/load-dataset.ts to SceneLoader.setLeafMaterializedListener.
sceneGraph is the graph the leaf belongs to; a leaf of any graph other than
the one this panel was initialised from (a load still running under a
previous dataset's panel) is left alone — its own initFromScene styles it.
Inject the shared failed-loads provider so per-row error badges can surface
a node whose loader threw (corrupt data / network failure). The app wires
the SAME provider the data monitor uses, after initFromScene. Resets the
change signature and applies the current failure set immediately so a scene
that already had failures at load lights up its rows without waiting for a
frame. Passing null (dispose) clears the provider and removes any badges
(the null reset sentinel forces the following pass through the gate even
when the resulting failed set is empty).
Inject the per-layer camera framer (the app wires SceneManager.fitCameraToObject, same late-binding pattern as the failed-loads provider). Null disables the "Frame camera" menu item.
Inject a callback that marks the GPU pick buffer dirty (PickingSystem.markDirty). Late-bound so it survives per-dataset picking re-creation.
Inject the sound layer for sound rows (eye = mute, slider = gain).
Re-send current layer visibility after a sound scene has attached.
PrivateapplyVisibility, for pixels AND sound: the scene object toggles as before, and the sound layer mutes the node (or, for a group, every sound under it). Every visibility path in the panel routes through here.
PrivateopenRoute a secondary gesture at target (right-click or touch long-press) to
the eye / row / header menu. Right-clicking an unselected row selects it
first (Finder/napari convention); an already-selected row keeps the
current multi-selection.
PrivateopenOpen the eye / row / header menu. layerPath is null for the header
menu. The opener gets aria-expanded while the menu is up; focus returns
to it on close (the utility handles both via onClose/focus-restore).
PrivatebuildPrivatebuildPrivatebuildRow menu: the appearance submenus and the copy verb, behind one separator. None for a sound row — it has no material, and the appearance section of the panel steps aside for it on the same grounds.
PrivatebuildThe Colormap submenu: direct colours, the authored custom LUT when the layer has one (the only way back to it after trying another palette), every builtin.
PrivatebuildPrivateapplyPush every layer's current state.visible into the scene objects.
PrivatesoloPrivatesetPrivateinvertPrivateresetReset ONE layer to its authored state. Re-derives the LayerInfo with a throwaway LayerStateManager over the kept scene graph (deliberately not factoring the private walkSceneGraph derivation — this reuses it verbatim, so reset can never drift from load), applies it through the state manager (which preserves selection and clears any solo capture — a direct Object.assign here would rewrite visibility behind the capture's back), and patches only this row (renderList would drop focus and reset the failed-loads signature).
Stable per-layer appearance summary for the embedder API
(LuxarApp.getLayers()). Copies, in panel display order.
Programmatic per-layer appearance patch (LuxarApp.setLayer()). Each
field takes the SAME route the panel's own control does — state-manager
setter (clamping, persistence, change notification) then the apply
engine — so a remote controller can never put the row, the material and
the stored state out of step with one another. Only the fields present
in patch are touched.
PrivaterefreshSync one row's eye icon + hidden class to the live state (no rebuild).
PrivatesetSet one layer's colormap with the fail-closed contract the dropdown uses (layer-controls): re-default the window on off↔on flips BEFORE applying, and if the C1 guard rejects the palette on every leaf, drop it and restore the identity window rather than leaving contradictory state.
PrivatesetSet one layer's blending mode (resolved per type, explicit ownership).
PrivateapplyCopy the source layer's appearance to every other layer, type-gated: display window clamped into each target's data bounds, gamma verbatim, blending resolved per target type, colormap only where supported. Order matters: the colormap copy runs LAST, so a target whose colormap mode flips gets that mode's re-defaulted window (via setLayerColormap) rather than the copied one — a window is only meaningful within one mode, so carrying it across the flip would mis-scale the new value. Deliberately NOT copied: mesh-only knobs, volumetric-only absorption, layer order, and opacity/visibility — those are per-layer compositing choices, not "appearance" (copying opacity would flatten a scene the user balanced layer-by-layer, while copying order would collapse every layer into one band). Sound rows neither give nor receive: they have no material, only placeholder appearance fields.
PrivateclearPrivatebuildPrivaterenderBuild the full list of layer rows (called once on init, not on every state change)
PrivateapplyApply the live name filter to the rows (case-insensitive substring). Hidden rows keep their DOM (state indices stay valid) and are skipped by the listbox arrow navigation.
PrivateisTrue when the row for a path is hidden by the live filter.
PrivateupdateUpdate selection highlights and visibility classes without rebuilding DOM
PrivateupdatePatch each row's load-failure badge from the injected provider's failed set.
A layer is in error if its own path failed OR any descendant leaf failed
(failedPath === layer.path || failedPath.startsWith(layer.path + '/')), so
a failure inside a kind=lod/kind=partition group lights up the group's row.
Version-gated for the scene loader; structural providers without a version
still use a signature that includes each path's reason.
PrivatechangedPrivateapplyAdd / update / remove a single row's error badge + --error class. The
badge is a warning glyph with an accessible label naming the reason; the
row's own aria-label is left untouched so the base "name (type)" reading
is preserved.
PrivatedescribeTooltip text for a failing row. Prefers the provider's per-path reason
(loader error.message / classified kind); falls back to a clear generic
message. Appends the descendant-failure count when more than one leaf under
the row failed.
PrivatebuildThe sound row's gain slider; owns its pointer and arrow keys so neither selects the row.
PrivatecreatePrivaterepositionReposition the rendering controls (.luxar-gui) so it sits below the
layers panel when visible, or resets to its default position when hidden.
Privaterequest
Late-bound per-layer camera framing (SceneManager.fitCameraToObject).