Skip to content

Asymmetric composition: 2D map is root-wired handlers, terrain viewer is a self-contained component #218

Description

@kui

Context

Surfaced while adding the shared prefab tooltip in #217.

Observation

Both viewers now feed the same shared PrefabTooltipController, but they sit at different architectural levels:

  • The 2D map is decomposed into many top-level handlers instantiated at the composition root (src/index.ts): MapCanvasHandler, CursorHandler, MarkerHandler, PrefabHighlightHandler, PrefabTooltipHandler, and so on.
  • The terrain viewer is a single self-contained component (TerrainViewer) that builds its own sub-parts internally: TerrainViewerCameraController, buildMarkerSprites, buildPrefabBoxes, and the hover/tooltip sub-controller.

Because of this, the terrain viewer's tooltip feeder cannot be a peer of the 2D PrefabTooltipHandler. It needs TerrainViewer's live camera and boxes, so it is created inside TerrainViewer, not at the root. In this codebase "Handler" conventionally means a composition-root-wired, page-lifetime unit, so a "Handler" created inside TerrainViewer is a naming/level mismatch. The near-term fix in #217 just names each feeder per its level (a root PrefabTooltipHandler vs a TerrainViewerHoverController owned by TerrainViewer), which reflects the asymmetry rather than resolving it.

Why it matters

Any concern shared between the two viewers (the tooltip today; possibly hover highlight, selection, or markers tomorrow) hits the same seam: one side is a root handler, the other is buried inside a component. That makes truly symmetric wiring impossible and forces shared state (the tooltip DOM/controller) to be injected across two different levels.

Possible directions (large, deferred)

  1. Decompose TerrainViewer into root-level handlers wired in index.ts (camera, markers, boxes, hover), the way the 2D map already is, so both viewers are "a bag of handlers".
  2. Or the opposite: wrap the 2D map into a single self-contained component (like TerrainViewer) that owns its handlers internally, so both viewers are "one component".

Either is a broad restructuring of the composition root and was intentionally left out of #217.

Scope

Tracking only. #217 ships the near-term naming fix; this issue records the underlying asymmetry for a future decision.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions