feat(actions): item-level Design/Arrange move + untag actions; W3-A polish
Add three MIDI-bindable item actions mirroring the track tag family, routed through the shared hookcommand: retag selected items' membership then re-drive mint/apply so each lands on its mode's managed lane (manual-lane items exempt), all in one undo block. Extract shared item_read seam, simplify applyMintPlan's lane-count pass, and guard reconcile against unregistered-mode lanes.
This commit is contained in:
@@ -519,6 +519,50 @@ std::vector<AutoTag> autoTagNewContent(const std::vector<std::string>& newTrackG
|
||||
const std::vector<NewItem>& newItems,
|
||||
const std::string& activeMode);
|
||||
|
||||
// -- Item-level mode-move decision (Phase D2 / Wave 3-B) ---------------------
|
||||
//
|
||||
// The bindable item actions (Move selected items -> Design / -> Arrange / Untag)
|
||||
// retag the CURRENT item selection's membership, then re-drive the minting/apply
|
||||
// path so each moved item lands on its target mode's managed lane. The DECISION —
|
||||
// which selected items to retag, and to what — is pure and unit-tested here; the
|
||||
// shell only reads the item selection (GUID + manual-lane disposition) and applies
|
||||
// the resulting membership writes + re-lane pass.
|
||||
//
|
||||
// MANAGED-LANES-ONLY INVARIANT (upheld at the source, exactly as auto-tag does): an
|
||||
// item the shell reports as already on a MANUAL lane is EXEMPT — it is never retagged,
|
||||
// never untagged, never re-laned. The tool drives only what it minted, even under an
|
||||
// explicit user action. The shell reports `onManualLane` per item and this decision
|
||||
// emits NO op for such items; the shell then skips them entirely.
|
||||
|
||||
// One selected item the shell reports for the retag decision: its GUID and whether it
|
||||
// currently sits on a MANUAL lane (⇒ EXEMPT: no membership change, no re-lane).
|
||||
struct RetagItem {
|
||||
std::string guid;
|
||||
bool onManualLane = false; // true ⇒ EXEMPT from the item mode-move actions
|
||||
};
|
||||
|
||||
// One membership op the item mode-move decision produced for one selected item. `untag`
|
||||
// true ⇒ remove the item from the index (return it to the Arrange default); otherwise
|
||||
// tag it into `modeId`. The shell applies each verbatim to the MembershipIndex.
|
||||
struct ItemRetagOp {
|
||||
std::string guid;
|
||||
bool untag = false; // true ⇒ untag; false ⇒ tag into modeId
|
||||
std::string modeId; // the target mode when !untag (empty when untag)
|
||||
|
||||
bool operator==(const ItemRetagOp& o) const {
|
||||
return guid == o.guid && untag == o.untag && modeId == o.modeId;
|
||||
}
|
||||
};
|
||||
|
||||
// The pure item mode-move decision: given the selected items and a target mode, produce
|
||||
// the membership ops. An EMPTY `targetMode` means UNTAG (the "Untag selected items" and
|
||||
// "Move -> Arrange" actions collapse to the same act — Arrange is the absence of a tag,
|
||||
// mirroring the track-level doUntag). A non-empty `targetMode` tags each eligible item
|
||||
// into it. Manual-lane items are skipped (no op emitted); items with an empty GUID are
|
||||
// skipped (defensive). The function mutates nothing — it returns a plan the shell applies.
|
||||
std::vector<ItemRetagOp> planItemRetag(const std::vector<RetagItem>& selected,
|
||||
const std::string& targetMode);
|
||||
|
||||
// -- Lane minting decision (Phase D2 / Wave 3) -------------------------------
|
||||
//
|
||||
// D1 parks a whole track when it holds content of only ONE mode. The moment a track
|
||||
|
||||
Reference in New Issue
Block a user