Captured from planning discussion after finishing the
ContentScaleControllermigration + 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 oldMode/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/MarieScaleConfiggot 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 withDoubleClickRecognizer, which had no other callers).
This is the same pattern that produced
CommandCenterRegistryandSnapRegistryearlier 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:
- Position — already exists (
DraggableResizable, drag-to-move) - Size — already exists (
DraggableResizable, drag-corner-to-resize) - Content Scale — already exists (
ContentScaleController) - Padding — already exists (
ContentScaleController) - Content Position — new,
ContentPositionController(this doc) - Color — not designed yet; likely touches
MarieColors/ThemeKeyand needs its own per-module color-override storage - Opacity — not designed yet; likely pairs naturally with Color
- Visibility (show/hide toggle per module) — not designed
- Alignment/anchoring (snap content to a corner/edge rather than
free-position) — could be a mode on top of
ContentPositionControllerrather than a separate primitive - Snapping —
SnapRegistryalready 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 - 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 relationshipCommandCenterRegistry,EditModeCoordinator, andScaleConfigPanelalready have with their consumers.
Not scoped yet — needs its own discovery pass before starting
- Exact
ContentPositionControllerAPI shape (mirroringContentScaleController'sregister/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
SnapRegistryand the existing drag/resize flow