MariesWiki
MariesLib · Enhancement

Module Editor

Design notes Updated 26 Aug 2026 3 min read

Captured from planning discussion after finishing the ContentScaleController migration + double-click removal across all 8 modules (5 Diet Screen sub-boxes, Calorie History, Activity Log, Nutrient HUD). Nothing here is scheduled yet — this is the design backlog for the next real project once it's time to scope it properly, the same way Command Center and the milestone system each got their own design pass before implementation.

The realization

The double-click+scroll interaction (DoubleClickRecognizer + ContentScaleController's old Mode / onClick / handleScroll) was built to solve a real problem the hard way: there was no existing editor UI, so the interaction had to be built directly into each HUD. It worked, but every new editable property would have meant inventing another special-case mouse interaction.

Then ScaleConfigPanel / MarieScaleConfig got built for a different reason (a proper slider-card UI) — and turned out to already be the right general abstraction for "let the player edit a module's properties," better than the click-inside-the-box interaction it was originally meant to complement. All 8 modules have since migrated to card-only editing, and the double-click interaction was deleted (along with DoubleClickRecognizer, which had no other callers).

This is the same pattern that produced CommandCenterRegistry and SnapRegistry earlier in the project: build something for one problem, recognize it's actually the general-purpose answer, generalize it properly.

The target model

Module
├── Bounds             (DraggableResizable — outer rectangle, existing)
├── ContentScale        (ContentScaleController — existing)
├── Padding             (ContentScaleController — existing)
├── ContentPosition      (NEW — see below)
├── Color                (not yet designed)
└── Opacity              (not yet designed)

Each concern gets its own small, focused primitive — same discipline DraggableResizable (outer bounds only) and ContentScaleController (scale/padding only) already follow. Nothing should try to do more than one job.

Content Position Controller (new primitive, not yet built)

Problem it solves: right now, a module's content (text/icons) always renders at a fixed position within the module's bounds — there's no way for the player to shift where inside the box the content sits without changing the box itself.

What it should do: given a fixed Bounds, let the player click-and-drag the content to reposition it within those bounds — the module's outer rectangle never changes, only an internal contentOffsetX / contentOffsetY (or a more general ContentPosition value) does.

Why not fold this into DraggableResizable: that primitive is fundamentally about the component's outer rectangle (drag to move the whole box, drag corners to resize it). Content positioning is a completely separate concern — moving something inside a box that itself isn't moving. Conflating the two would make both harder to reason about.

Proposed home: dev.marie.framework.ui.edit, peer to DraggableResizable / ContentScaleController — or dev.marie.framework.ui.api if it ends up being public-facing like ContentScaleController's facade work. Persisted the same way (ComponentState, extended with new fields, same additive-field pattern used for contentScale / paddingScale before it).

Full property list for the eventual module editor

In rough order of how naturally they build on what already exists:

  1. Position — already exists (DraggableResizable, drag-to-move)
  2. Size — already exists (DraggableResizable, drag-corner-to-resize)
  3. Content Scale — already exists (ContentScaleController)
  4. Padding — already exists (ContentScaleController)
  5. Content Position — new, ContentPositionController (this doc)
  6. Color — not designed yet; likely touches MarieColors / ThemeKey and needs its own per-module color-override storage
  7. Opacity — not designed yet; likely pairs naturally with Color
  8. Visibility (show/hide toggle per module) — not designed
  9. Alignment/anchoring (snap content to a corner/edge rather than free-position) — could be a mode on top of ContentPositionController rather than a separate primitive
  10. Snapping — SnapRegistry already exists for cross-module snapping during drag; whether it needs a module-editor-specific extension is unclear, revisit once the rest of this list is built
  11. Per-module reset (revert one module's overrides to default without resetting everything) — likely just needs a PersistenceProvider.remove(id)- style call already available, may not need new engine work at all

Architecture principle (carried over from the rest of this project)

Same split as everything else built this session: the generic editor mechanics (ContentPositionController, whatever Color/Opacity storage ends up looking like, the actual editor UI shell) belong in MariesLib as reusable infrastructure. Nourished (and any other consumer mod) just supplies which modules opt in and what properties they expose — the same relationship CommandCenterRegistry, EditModeCoordinator, and ScaleConfigPanel already have with their consumers.

Not scoped yet — needs its own discovery pass before starting

  • Exact ContentPositionController API shape (mirroring ContentScaleController's register /pass-through pattern, or something else)
  • Whether Color/Opacity are one combined primitive or two
  • Whether the "editor" becomes a literal screen (mockup: title "DIET SCREEN EDITOR" with per-module property rows) or stays as per-module ScaleConfigPanel-style cards, just with more rows added
  • How this interacts with SnapRegistry and the existing drag/resize flow

More MariesLib enhancements