From fbb584436b9b5ffad9bc510a6ff6124ac9a111ad Mon Sep 17 00:00:00 2001 From: Erik Date: Mon, 7 Sep 2026 01:57:29 +0200 Subject: [PATCH] =?UTF-8?q?feat(vt):=20round=203=20item=2010=20=E2=80=94?= =?UTF-8?q?=20Delete=20action=20for=20Meta/Route/Loot=20profiles?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Round 2 step 5 gave the settings Profiles tab a Delete action; Meta, Route, and Loot never got the same verb. Added MossTankMetaProfileStore.Delete, MossTankRouteProfileStore.Delete, and MossTankLootProfileStore.Delete (same contract as Settings: remove the selected named profile's real file, fall back to By char; refuse for By char itself, which has nothing to delete — see each store's ClearCurrent for that case), wired through MossTankPanel.DeleteMetaProfile/DeleteRouteProfile/DeleteLootProfile to three new "Delete" buttons in mosstank.xml (Route tab row, the Meta tab's button row, and the Loot rule editor's button row). MossTankMarkupContractTests' interactive-control count moves 191 -> 194 for the three new buttons. Mutation: reverted all four .cs files and mosstank.xml to HEAD (keeping only the new/changed tests) — the test project failed to even COMPILE (DeleteRouteProfile/DeleteMetaProfile/DeleteLootProfile do not exist on MossTankPanel), confirming the six new behavioral tests (DeleteRouteProfile/DeleteMetaProfile/DeleteLootProfile, each with a successful-delete and a refuse-by-char case) and the markup-count update all depend on this commit's code. Co-Authored-By: Claude Fable 5.1 --- .../MossTankLootProfileStore.cs | 23 +++++ .../MossTankMetaProfileStore.cs | 24 +++++ src/AcDream.Plugins.MossTank/MossTankPanel.cs | 36 ++++++++ .../MossTankRouteProfileStore.cs | 24 +++++ src/AcDream.Plugins.MossTank/mosstank.xml | 13 ++- .../MossTankMarkupContractTests.cs | 4 +- .../MossTankPanelTests.cs | 92 +++++++++++++++++++ 7 files changed, 213 insertions(+), 3 deletions(-) diff --git a/src/AcDream.Plugins.MossTank/MossTankLootProfileStore.cs b/src/AcDream.Plugins.MossTank/MossTankLootProfileStore.cs index 4c675120..2444c9cf 100644 --- a/src/AcDream.Plugins.MossTank/MossTankLootProfileStore.cs +++ b/src/AcDream.Plugins.MossTank/MossTankLootProfileStore.cs @@ -238,6 +238,29 @@ internal sealed class MossTankLootProfileStore WriteUtl(fileName, profile); } + /// + /// Round 3 item 10: deletes the currently selected named loot profile's + /// real .utl file, then falls back to . + /// Refuses for itself — there is no file to + /// delete, only a reset (see ), matching + /// 's own contract. + /// + public bool Delete(out string notice) + { + if (_selected.Equals(ByCharacter, StringComparison.OrdinalIgnoreCase)) + { + notice = "'By char' is the built-in loot profile and cannot be deleted."; + return false; + } + string fileName = _selected; + if (VtankStorage.IsAvailable) + VtankStorage.Delete(fileName); + _selected = ByCharacter; + WriteBinding(); + notice = $"Deleted loot profile {StripUtl(fileName)}."; + return true; + } + public void ClearCurrent(List target, LootSettings? settings = null) { target.Clear(); diff --git a/src/AcDream.Plugins.MossTank/MossTankMetaProfileStore.cs b/src/AcDream.Plugins.MossTank/MossTankMetaProfileStore.cs index 97511797..719e6c0f 100644 --- a/src/AcDream.Plugins.MossTank/MossTankMetaProfileStore.cs +++ b/src/AcDream.Plugins.MossTank/MossTankMetaProfileStore.cs @@ -261,6 +261,30 @@ internal sealed class MossTankMetaProfileStore return true; } + /// + /// Round 3 item 10: deletes the currently selected named Meta profile's + /// real .af file, then falls back to . + /// Refuses for itself — there is no file to + /// delete, only a reset (see ), matching + /// 's own contract. + /// + public bool Delete(out string notice) + { + if (_selected.Equals(ByCharacter, StringComparison.OrdinalIgnoreCase)) + { + notice = "'By char' is the built-in Meta profile and cannot be deleted."; + return false; + } + string fileName = _selected; + if (VtankStorage.IsAvailable) + VtankStorage.Delete(fileName); + _selected = ByCharacter; + _pendingLegacyBareName = null; + WriteBinding(); + notice = $"Deleted Meta profile {fileName}."; + return true; + } + public MetaProfile ClearCurrent() { var empty = new MetaProfile(); diff --git a/src/AcDream.Plugins.MossTank/MossTankPanel.cs b/src/AcDream.Plugins.MossTank/MossTankPanel.cs index cb6983af..a487505b 100644 --- a/src/AcDream.Plugins.MossTank/MossTankPanel.cs +++ b/src/AcDream.Plugins.MossTank/MossTankPanel.cs @@ -594,6 +594,7 @@ internal sealed partial class MossTankPanel public Action CopyLootProfile => () => CreateLootProfileCore(copyCurrent: true); public Action ClearLootProfile => ClearLootProfileCore; + public Action DeleteLootProfile => DeleteLootProfileCore; public Action CloseLootEditor => () => _lootEditorVisible = false; public Action SelectLootRule => SelectLootRuleCore; public Action SetLootExpressionDraft => value => @@ -756,6 +757,7 @@ internal sealed partial class MossTankPanel public Action CopyRouteProfile => () => CreateRouteProfileCore(copyCurrent: true); public Action ClearRouteProfile => ClearRouteProfileCore; + public Action DeleteRouteProfile => DeleteRouteProfileCore; public Action SetFollowTarget => CaptureFollowTarget; // ── Meta profile / editor ──────────────────────────────────────────── @@ -826,6 +828,7 @@ internal sealed partial class MossTankPanel public Action CreateMetaProfile => () => CreateMetaProfileCore(copyCurrent: false); public Action CopyMetaProfile => () => CreateMetaProfileCore(copyCurrent: true); public Action ClearMetaProfile => ClearMetaProfileCore; + public Action DeleteMetaProfile => DeleteMetaProfileCore; // ── Profiles tab ───────────────────────────────────────────────────── public IReadOnlyList MacroProfileNames => _profiles.AvailableNames; @@ -1425,6 +1428,17 @@ internal sealed partial class MossTankPanel _lootEditorNotice = $"Cleared {_lootProfiles.Selected}."; } + private void DeleteLootProfileCore() + { + if (!_lootProfiles.Delete(out string notice)) + { + _lootEditorNotice = notice; + return; + } + LoadLootProfile(); + _lootEditorNotice = notice; + } + private void LoadLootProfile() { if (!_lootProfiles.LoadCurrent( @@ -1784,6 +1798,17 @@ internal sealed partial class MossTankPanel _routeNotice = $"Cleared {_routeProfiles.Selected}."; } + private void DeleteRouteProfileCore() + { + if (!_routeProfiles.Delete(out string notice)) + { + _routeNotice = notice; + return; + } + LoadRouteProfile(); + _routeNotice = notice; + } + private void LoadRouteProfile() { if (!_routeProfiles.LoadCurrent(_navigationSettings, _host.Automation.Spells)) @@ -2380,6 +2405,17 @@ internal sealed partial class MossTankPanel _metaNotice = $"Cleared Meta profile {_metaProfiles.Selected}."; } + private void DeleteMetaProfileCore() + { + if (!_metaProfiles.Delete(out string notice)) + { + _metaNotice = notice; + return; + } + LoadMetaProfile(); + _metaNotice = notice; + } + private void LoadMetaProfile() { _metaProfile = _metaProfiles.LoadCurrent(); diff --git a/src/AcDream.Plugins.MossTank/MossTankRouteProfileStore.cs b/src/AcDream.Plugins.MossTank/MossTankRouteProfileStore.cs index 13121ceb..5035eb04 100644 --- a/src/AcDream.Plugins.MossTank/MossTankRouteProfileStore.cs +++ b/src/AcDream.Plugins.MossTank/MossTankRouteProfileStore.cs @@ -228,6 +228,30 @@ internal sealed class MossTankRouteProfileStore return true; } + /// + /// Round 3 item 10: deletes the currently selected named route's real + /// .af file, then falls back to . + /// Refuses for itself — there is no file to + /// delete, only a reset (see ), matching + /// 's own contract. + /// + public bool Delete(out string notice) + { + if (_selected.Equals(ByCharacter, StringComparison.OrdinalIgnoreCase)) + { + notice = "'By char' is the built-in route profile and cannot be deleted."; + return false; + } + string fileName = _selected; + if (VtankStorage.IsAvailable) + VtankStorage.Delete(fileName); + _selected = ByCharacter; + _pendingLegacyBareName = null; + WriteBinding(); + notice = $"Deleted route profile {Strip(fileName)}."; + return true; + } + /// /// Resets only the fields this store owns (Mode/Waypoints/FollowTarget) /// — Enabled/Priority/MinimumDistanceMeters/FollowAroundCorners/ diff --git a/src/AcDream.Plugins.MossTank/mosstank.xml b/src/AcDream.Plugins.MossTank/mosstank.xml index f89fcff1..6c697b2d 100644 --- a/src/AcDream.Plugins.MossTank/mosstank.xml +++ b/src/AcDream.Plugins.MossTank/mosstank.xml @@ -158,6 +158,9 @@ onclick="{CreateRouteProfile}" />