Commit graph

2 commits

Author SHA1 Message Date
699c288a98
feat(modifiers): bundle preset activations on profiles + editor Presets tab
Profiles can now carry a list of PresetActivation entries alongside
their per-type / per-instance modifiers. When such a profile is
picked in the Lobby, its bundled presets are unioned with any
layout suggestedPresets (profile config wins on id-collision).

Changes:

- ModifierProfile.presetActivations added (optional readonly array).
  Zod schema + server wire schema mirror the field; drift guard
  picks up forgotten updates on either side.
- ModifierProfileEditor grows a Presets tab in the right column
  sharing space with the Library tab. Each preset row carries a
  checkbox, scope radio (both/white/black), and a turns counter
  (blank = permanent). Inline diagnostics warn on redundant layout
  overlap and on loose-scope incompatibility; hard errors under
  overlapping scope disable Save.
- Lobby merges profile.presetActivations into its active preset
  set on profile select, on editor close, and on URL deep-link.
- LayoutPicker suggested-preset chips now expose aria-pressed so
  the active state is readable by assistive tech + e2e tests.

Tests: library round-trip preserves presetActivations (unit); two
new e2e scenarios (pre-seeded bundled profile auto-activates; full
editor-author loop persists + reflects in the lobby). 1752 unit +
105 playwright all green.
2026-04-21 14:03:52 -06:00
4b08b0c71c
feat(modifiers): persist bound layout on profile + drive Lobby layout from profile
Previously the ModifierProfileEditor's layout picker was editor-only
UI state — it drove per-instance board preview and live validation,
but was dropped on save. This meant per-instance modifiers (square-
bound to their authoring layout) silently became orphans in the Lobby
if the user picked a different layout at apply time, with no signal
about the mismatch.

Changes:

- ModifierProfileEditor now writes the bound layout through to
  profile.layoutId (schema already supported the optional field) and
  rehydrates boundLayout from profile.layoutId on open / load /
  undo / redo.
- Lobby rearranged so the Modifier Profile picker sits ABOVE the
  Layout Picker. Picking a profile with a layoutId binding snaps
  selectedLayout to match. Same behavior on URL ?modifierProfile
  deep-links and after the editor closes with a new save.
- Mismatch banner (amber) surfaces when the user overrides the
  layout after picking a bound profile, with a one-click "Switch
  to <LayoutName>" restore.

Unit test added for library round-trip of layoutId; new
profile-layout-binding.spec.ts covers the four scenarios: snap,
manual override + mismatch banner, unbound profile leaves layout
alone, and editor-save persists layoutId.
2026-04-21 13:28:06 -06:00