diff --git a/src/AcDream.Plugins.MossTank/AcDream.Plugins.MossTank.csproj b/src/AcDream.Plugins.MossTank/AcDream.Plugins.MossTank.csproj
index 92abd08a0..b593eb88b 100644
--- a/src/AcDream.Plugins.MossTank/AcDream.Plugins.MossTank.csproj
+++ b/src/AcDream.Plugins.MossTank/AcDream.Plugins.MossTank.csproj
@@ -22,6 +22,12 @@
PreserveNewest
+
+ PreserveNewest
+
+
+ PreserveNewest
+
diff --git a/src/AcDream.Plugins.MossTank/MossTankPanel.cs b/src/AcDream.Plugins.MossTank/MossTankPanel.cs
index 3e58f0c3b..185052e5d 100644
--- a/src/AcDream.Plugins.MossTank/MossTankPanel.cs
+++ b/src/AcDream.Plugins.MossTank/MossTankPanel.cs
@@ -347,17 +347,23 @@ internal sealed partial class MossTankPanel
public bool RouteTabEnabled => true;
public bool MetaTabEnabled => true;
- public bool OptionsVisible => OptionsSelected
- && !_lootEditorVisible
- && !_advancedOptionsVisible;
- public bool ProfilesVisible => ProfilesSelected && !_lootEditorVisible;
- public bool VitalsVisible => VitalsSelected && !_lootEditorVisible;
- public bool MonstersVisible => MonstersSelected && !_lootEditorVisible;
- public bool ItemsVisible => ItemsSelected && !_lootEditorVisible;
- public bool ConsumablesVisible => ConsumablesSelected && !_lootEditorVisible;
- public bool BuffsVisible => BuffsSelected && !_lootEditorVisible;
- public bool RouteVisible => RouteSelected && !_lootEditorVisible;
- public bool MetaVisible => MetaSelected && !_lootEditorVisible;
+ // Fix round A (2026-09-07): Advanced Options / Loot Editor used to be
+ // in-panel groups sharing this SAME window with the nine normal tabs,
+ // so a tab's own content had to hide while a popup was open to avoid
+ // drawing both at once. Now that both popups are their own separate
+ // plugin panels (MossTankPlugin.cs, mosstank-advanced.xml /
+ // mosstank-loot-editor.xml), they no longer share screen space with
+ // any tab body, so these guards are gone — a tab stays visible
+ // (or not) purely by tab selection again.
+ public bool OptionsVisible => OptionsSelected;
+ public bool ProfilesVisible => ProfilesSelected;
+ public bool VitalsVisible => VitalsSelected;
+ public bool MonstersVisible => MonstersSelected;
+ public bool ItemsVisible => ItemsSelected;
+ public bool ConsumablesVisible => ConsumablesSelected;
+ public bool BuffsVisible => BuffsSelected;
+ public bool RouteVisible => RouteSelected;
+ public bool MetaVisible => MetaSelected;
public bool LootEditorVisible => _lootEditorVisible;
public bool AdvancedOptionsVisible => _advancedOptionsVisible;
public IReadOnlyList AdvancedOptionNames => VtankOptionCatalog.Names;
diff --git a/src/AcDream.Plugins.MossTank/MossTankPlugin.cs b/src/AcDream.Plugins.MossTank/MossTankPlugin.cs
index 40f5fa461..bf8b565f4 100644
--- a/src/AcDream.Plugins.MossTank/MossTankPlugin.cs
+++ b/src/AcDream.Plugins.MossTank/MossTankPlugin.cs
@@ -52,6 +52,38 @@ public sealed class MossTankPlugin : IAcDreamPlugin
Path.Combine(directory, "mosstank.xml"),
_panel);
+ // Fix round A (2026-09-07): Advanced Options / Loot Editor were
+ // in-panel groups sharing the main window (forcing it to 350px
+ // tall with dead space under the nine normal tabs); they are now
+ // their own plugin panels, matching VTank's own separate popup
+ // windows (docs/research/vtank-kb/08-ui-views.md §1's secondary-
+ // view table). ShowInSidePanel=false — VTank's popups have no
+ // independent shelf/taskbar entry, they only exist while opened
+ // from the Options/Profiles tab checkbox
+ // (AdvancedOptionsVisible/LootEditorVisible). Each popup's own
+ // binding is the SAME flag the checkbox
+ // flips and the popup's own "Back" button clears, so closing the
+ // popup unchecks the box for free — no new IUiRegistry show/hide
+ // API needed (MarkupDocument.Build already re-evaluates a root
+ // 's VisibleSource every frame; see the two files' own
+ // header comments).
+ _host.Ui.AddPanel(
+ new PluginPanelDescriptor("advanced-options", "MossTank Advanced Options")
+ {
+ StartVisible = false,
+ ShowInSidePanel = false,
+ },
+ Path.Combine(directory, "mosstank-advanced.xml"),
+ _panel);
+ _host.Ui.AddPanel(
+ new PluginPanelDescriptor("loot-editor", "MossTank Loot Editor")
+ {
+ StartVisible = false,
+ ShowInSidePanel = false,
+ },
+ Path.Combine(directory, "mosstank-loot-editor.xml"),
+ _panel);
+
_commandRegistration = _host.Commands.Register(
"vt",
_panel.ExecuteVtankCommand);
diff --git a/src/AcDream.Plugins.MossTank/mosstank-advanced.xml b/src/AcDream.Plugins.MossTank/mosstank-advanced.xml
new file mode 100644
index 000000000..f8917bc64
--- /dev/null
+++ b/src/AcDream.Plugins.MossTank/mosstank-advanced.xml
@@ -0,0 +1,44 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/AcDream.Plugins.MossTank/mosstank-loot-editor.xml b/src/AcDream.Plugins.MossTank/mosstank-loot-editor.xml
new file mode 100644
index 000000000..5beb9718a
--- /dev/null
+++ b/src/AcDream.Plugins.MossTank/mosstank-loot-editor.xml
@@ -0,0 +1,101 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/AcDream.Plugins.MossTank/mosstank.xml b/src/AcDream.Plugins.MossTank/mosstank.xml
index 8eb01d34a..295fd10a2 100644
--- a/src/AcDream.Plugins.MossTank/mosstank.xml
+++ b/src/AcDream.Plugins.MossTank/mosstank.xml
@@ -7,16 +7,50 @@
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+ last VTank control instead.
+
+ Fix round A (2026-09-07, owner: legacy leftovers deleted): the
+ stacked ProfileNameDraft/RouteProfileNameDraft/MetaProfileNameDraft
+ field+"New"-button trio below the four VTank rows (three 20-tall
+ fields at zero vertical gap rendered as one solid black bar behind
+ three stacked "New" buttons — the owner's screenshot) is REMOVED;
+ Meta's naming already has a live field+button pair on the Meta tab
+ itself (mosstank.xml's Meta group), and Macro/Nav lose their only
+ in-UI CopyTo target-name entry as a result (a real, accepted
+ regression — see the commit body). The acdream-only "Loot engine"
+ classifier row (LootClassifierNames/SelectedLootClassifier, no VTank
+ equivalent per KB §5) and the ProfileLifecycleNotice label (the raw
+ macro-profile recovery path the owner saw rendered) are also
+ REMOVED — VTank's own Profiles tab has no notice/status control at
+ all. LootStatus is kept (general loot-load feedback, not itself the
+ reported leftover) but repositioned into the space this freed.
+ PROFILES_COL_SHIFT = 16px moves every column after the leftmost
+ checkbox/label (menu 140->156, CopyTo/New 280->296,
+ Clear/Delete-as-button 330->346, Mine-only/Show-Editor 440->456,
+ per-family Delete 530->546) — see the panel-level comment for why
+ (fixes "Enable Auto-Navigation" running into its dropdown). Every
+ gap between these columns is preserved exactly (a translation, not a
+ scale). -->
-
-
-
+
-
-
-
-
-
+
-
-
-
-
+
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
+
-
-
+
-
-
+
-
-
+
-
-
+
-
-
+
-
-
+
-
-
+
-
-
+
-
-
+
@@ -503,9 +504,9 @@
onclick="{AddRouteJump}" />
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/tests/AcDream.Plugins.MossTank.Tests/AcDream.Plugins.MossTank.Tests.csproj b/tests/AcDream.Plugins.MossTank.Tests/AcDream.Plugins.MossTank.Tests.csproj
index 5c71146f8..ebf03ae63 100644
--- a/tests/AcDream.Plugins.MossTank.Tests/AcDream.Plugins.MossTank.Tests.csproj
+++ b/tests/AcDream.Plugins.MossTank.Tests/AcDream.Plugins.MossTank.Tests.csproj
@@ -23,6 +23,12 @@
+
+
diff --git a/tests/AcDream.Plugins.MossTank.Tests/MossTankMarkupContractTests.cs b/tests/AcDream.Plugins.MossTank.Tests/MossTankMarkupContractTests.cs
index f0a956163..3dbebcb56 100644
--- a/tests/AcDream.Plugins.MossTank.Tests/MossTankMarkupContractTests.cs
+++ b/tests/AcDream.Plugins.MossTank.Tests/MossTankMarkupContractTests.cs
@@ -187,7 +187,14 @@ public sealed class MossTankMarkupContractTests
// buttons + 3 menus + 14 toggles + 3 equip buttons = 30 controls)
// is gone, replaced by 1 list + 23 columns + 1 field + 2 buttons
// (27) — net 205 -> 202.
- Assert.Equal(202, controls.Length);
+ // Fix round A (2026-09-07) moved the Advanced Options and Loot
+ // Editor groups OUT of mosstank.xml entirely into their own plugin
+ // panels (mosstank-advanced.xml / mosstank-loot-editor.xml, this
+ // contract only scans mosstank.xml) and deleted the Profiles tab's
+ // three stacked name-draft field+"New"-button rows and the
+ // acdream-only "Loot engine" classifier menu (owner: legacy
+ // leftovers) — net 202 -> 167.
+ Assert.Equal(167, controls.Length);
foreach (XElement control in controls)
{
@@ -274,15 +281,95 @@ public sealed class MossTankMarkupContractTests
// Campaign VT slice 7 S7.1: 856 wide, matching VTank's own
// mainView.xml content (856x210, docs/research/vtank-kb/
- // 08-ui-views.md §0). 350 tall (not 210) only because the
- // Advanced Options / Loot Editor popups moved to VTank's own
- // 392x300 / 268x300 popup geometry as separate in-panel groups —
- // see mosstank.xml's own panel-level comment.
+ // 08-ui-views.md §0). Fix round A (2026-09-07) moved the Advanced
+ // Options / Loot Editor popups OUT into their own plugin panels
+ // (mosstank-advanced.xml / mosstank-loot-editor.xml), so the main
+ // window is back to VTank's own proportion: 236 tall (42px header +
+ // tab strip, unchanged since S7.1, + 194px per-tab content,
+ // unchanged) with no dead space below the tabs — see mosstank.xml's
+ // own panel-level comment.
Assert.Equal(856f, Number(root, "w"));
- Assert.Equal(350f, Number(root, "h"));
+ Assert.Equal(236f, Number(root, "h"));
AssertWithinParent(root);
}
+ ///
+ /// Fix round A (2026-09-07): the Advanced Options and Loot Editor popups
+ /// moved out of mosstank.xml into their own plugin panel files. This
+ /// pins that both files still parse, still declare VTank's own popup
+ /// footprint (392x300 / 268x300, docs/research/vtank-kb/08-ui-views.md
+ /// §1's secondary-view table), every child fits its own declared bounds,
+ /// and every markup binding still resolves against a real
+ /// property — the same three guarantees
+ ///
+ /// and
+ /// give the main panel, now split across three files instead of one.
+ ///
+ [Theory]
+ [InlineData("mosstank-advanced.xml", 392f, 300f)]
+ [InlineData("mosstank-loot-editor.xml", 268f, 300f)]
+ public void SecondaryPopupPanelsFitTheirOwnBoundsAndEveryBindingResolves(
+ string fileName, float expectedWidth, float expectedHeight)
+ {
+ XDocument document = XDocument.Load(
+ Path.Combine(AppContext.BaseDirectory, fileName));
+ XElement root = Assert.IsType(document.Root);
+
+ Assert.Equal(expectedWidth, Number(root, "w"));
+ Assert.Equal(expectedHeight, Number(root, "h"));
+ AssertWithinParent(root);
+
+ PropertyInfo[] properties = typeof(MossTankPanel).GetProperties(
+ BindingFlags.Instance | BindingFlags.Public);
+ var byName = properties.ToDictionary(
+ static property => property.Name,
+ StringComparer.Ordinal);
+
+ foreach (XAttribute attribute in root.DescendantsAndSelf().Attributes())
+ {
+ string value = attribute.Value;
+ if (!value.Contains('{', StringComparison.Ordinal))
+ continue;
+ Assert.Matches("^\\{[^{}]+\\}$", value);
+ string name = value[1..^1];
+ Assert.True(
+ byName.ContainsKey(name),
+ $"Markup binding {value} on <{attribute.Parent?.Name}> in "
+ + $"{fileName} has no public MossTankPanel property.");
+ }
+ }
+
+ ///
+ /// Fix round A: neither popup's own "↑"/"↓" reorder buttons (Loot
+ /// Editor's MoveLootRuleUp/Down) render as blank slots — the retail
+ /// default UI font (0x40000000) has no glyph for those Unicode
+ /// characters, so they now carry the same DAT move-up/move-down icons
+ /// (0x060028FC/0x060028FD) the Monsters grid already uses, and no
+ /// <button> anywhere is left with the literal arrow text.
+ ///
+ [Fact]
+ public void NoButtonAnywhereUsesTheUnrenderableArrowGlyphs()
+ {
+ foreach (string fileName in new[]
+ {
+ "mosstank.xml", "mosstank-advanced.xml",
+ "mosstank-loot-editor.xml",
+ })
+ {
+ XDocument document = XDocument.Load(
+ Path.Combine(AppContext.BaseDirectory, fileName));
+ XElement root = Assert.IsType(document.Root);
+ foreach (XElement button in root.Descendants("button"))
+ {
+ string? text = (string?)button.Attribute("text");
+ Assert.False(
+ text is "↑" or "↓",
+ $"