Records this round's two owner-reported fixes (the fabricated Buffs-tab
middle strip removed; Advanced Options tooltips removed) against the
already-closed slice 7 campaign, with commit references and the
MossTank/App test-count deltas.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Campaign VT slice 7: window at VTank's proportions (resizable, anchored),
Options/Profiles/Vitals/Monsters/Items/Consumables/Buffs/Route/Meta
transcribed from mainView.xml with VTank's per-cell behaviors, Advanced
Options in VTank's click model, the buff picker and meta-editor popups,
VTank's 27 recalls wired to routes, plain dropdowns/sliders with retail
scrollbar chrome. Two Opus lenses + rounds A–F + owner live looks; final
combined re-check MERGE-READY.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
# Conflicts:
# docs/plans/2026-09-07-campaign-vt-slice7-tabs.md
Records round E (the architecture re-check of round D): all six items
fixed, commit SHAs, test-count deltas per item, and the two real test-
methodology bugs plus one test-harness false positive found while
building D-6's resolved-geometry pin.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
#490 part 2 (plugin layout persistence has no revision bump) is fixed at
05f22d46f (RetailWindowManager.ComputeAuthoredGeometryRevision +
MigrateAuthoredGeometry's inequality comparison + MountPlugins wiring).
Part 1 (StartVisible=false + ShowInSidePanel=false is permanently
unshowable) remains open -- not touched by this work.
Also adds one sentence to docs/plugin-ui-markup.md's "Resizable panels and
anchors" section: changing a panel's authored w/h/minw/minh/resizable in a
later plugin update resets every user's stored size to the new default
exactly once, automatically -- no plugin-author call site to remember.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Slice-7 resemblance re-check found UiMarkupList's column-less items= mode
and its <column> mode disagreed on whether a selected row gets a
persistent SelectedColor fill, so a plugin's Buffs lists highlighted a
row while the Monsters/Meta grids looked different for the same widget.
Real VVS lists (VTank's HudList) draw no such fill at all.
UiMarkupList.SelectionBandEnabled (default false) now gates the fill in
both OnDraw's legacy branch and DrawColumns; MarkupDocument parses
<list selectionband="true"> (same literal-bool convention as
openupward/clearonsubmit) to opt a single list back in. selected/onchange
semantics and scroll-into-view of the selected row are unchanged.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Records the merge of claude/latest-main-sync-497549 plus round D's
four fix items (Advanced Options category names, VTank's click model,
the full 26-recall table, resizable/enlarged main panel) on the panel
worktree, with commit SHAs, test-count deltas, and the carried
deviations (#491, the RouteRecallKind-ordinal migration risk).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Merges claude/latest-main-sync-497549 into the slice-7 panel worktree
so round D can build on both: b71a8ea37 (retail scrollbar chrome on
plain <menu> popups and overflowing <list>s — chat/inventory sprite
ids, always-scrollable single-column menu) and 2e63391cc (<panel
resizable minw minh> + anchor="left top right bottom" markup).
Conflicts resolved keeping both intents:
- Ledger (docs/plans/2026-09-07-campaign-vt-slice7-tabs.md): unioned
both branches' entries into one chronological timeline instead of
picking a side.
- docs/plugin-ui-markup.md: kept both attribute additions per element
(slider min/max/style, menu scroll/style) AND anchor on every row.
- src/AcDream.App/UI/UiMarkupList.cs: kept this branch's fix round B
item 10 (VVS HudList grids have no row-selection highlight) over the
sync branch's older SelectedColor band draw in the <column> grid
path — the legacy single-column list path is unaffected either way.
- src/AcDream.App/UI/MarkupDocument.cs: the auto-merge left two
`Scrollable =` initializers on the same <menu> object (CS1912).
Kept the sync branch's `Scrollable = true` (VTank's HudCombo is
always a single scrolling column, never a wrapping grid) and
dropped this branch's `Scrollable = B(el, "scroll", false)` opt-in,
since the owner-driven always-scrollable design supersedes the
S7.2 opt-in one. Updated MarkupDocumentTests.cs to match: removed
Build_MenuWithNoScrollAttribute_KeepsScrollableFalse (asserted the
now-false opt-in default) and
Menu_Scroll_DrawsAPlainFlatThumbFillWhenTheMarkupItemCountOverflowsTheVisibleRows
(asserted a flat DrawFill thumb; the scrollbar is sprite-chrome for
every menu style now) — both fully superseded by
Menu_Markup_IsAlwaysScrollable_WithRetailScrollbarChromeWired and
UiMenuPlainStyleTests.Plain_OpenPopup_ScrollableOverflow_
DrawsRetailScrollbarChrome_RowsStayPlain.
Verified: dotnet build AcDream.slnx -c Release green; MossTank suite
678/678; App markup/plugin filter 242/242 (241 before this commit's
test-file trim, +1 net from the merge's own new tests, 0 red).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
EveryInteractiveControlDeclaresARealHandlerBinding asserted an exact
Assert.Equal(156, controls.Length), backed by ~60 lines of running
commentary recording every markup edit that ever bumped the number. That
count was a pure change detector: it carried no signal the test's own
per-control handler/enabled loop couldn't already catch on its own, and
it forced an edit to THIS test file every time an unrelated tab gained
or lost a single control.
Replaced the exact count with Assert.NotEmpty(controls) — it still
guards the selector itself (a broken interactive-element-name filter
that matched nothing would otherwise pass the loop vacuously) — and kept
the real assertion (the per-control handler/enabled loop) unchanged.
Moved the removed count's full history into this same commit's ledger
entry in docs/plans/2026-09-07-campaign-vt-slice7-tabs.md, per item
F13's own instruction.
Mutation named: temporarily broke the controls selector (appended
`&& false` to the interactive-element filter) and confirmed
Assert.NotEmpty fails ("Collection was empty") before restoring it.
MossTank suite holds at 678 (no test added or removed, one assertion
replaced). Full solution build green; App markup/plugin filter 203/203.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Adds the "Resizable panels and anchors" section to plugin-ui-markup.md:
the resizable/minw/minh attribute table for <panel>, the anchor grammar
for every element that now supports it, the AnchorEdges semantics
(left top default, left right / top bottom stretch, right/bottom pin-
and-move), group-relative child anchoring, and one worked example
(a resizable panel with a group and list that both stretch on drag).
Also updates the Elements table, the "every registered window" intro
paragraph, the ApplyCommon common-attributes paragraph, and the Testing
conventions section to reference the new grammar and its test coverage.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Owner live-client report 2026-09-07: "For scrollable dropdown or the meta
window we use the same assets as we do in for example chat or inventory
window."
<menu> markup wiring (MarkupDocument.cs): a plugin <menu> is now always
Scrollable (single-column, VTank HudCombo shape) instead of wrapping
overflow into more grid columns, with PopupScrollbarHideWhenDisabled=true
so the bar is entirely absent while the item count fits the "rows"
window. RetailScrollbarChrome.ApplyToMenuPopup wires the same chrome ids
the previous commit taught DrawScrollablePopupPlain to draw, for both
style="plain" and style="retail" markup menus.
<list> markup (UiMarkupList.cs / MarkupDocument.cs): a plugin <list>
(single-column or <column> multi-column) that overflows its own row
viewport now draws the retail scrollbar chrome at its right edge (VVS's
own placement, 16px wide) instead of being wheel-scroll-only with no
visible bar. The reserved 16px column only exists while rows actually
overflow, in both column-layout modes (ComputeColumnLayout receives the
already-shrunk width so the last/auto column absorbs the remainder
correctly); the bar is fully interactive (up/down arrows, track paging,
thumb drag) via a small UiScrollable projection kept in sync with the
list's own _topRow, which stays the single source of truth. Wheel
scrolling and a no-resolver hand-built list (draws nothing, no crash) are
unchanged.
Mutation shown to fail first: new
UiMarkupListScrollbarTests/MarkupDocumentTests cases were written against
pre-change UiMarkupList/MarkupDocument and failed (no scrollbar sprites
ever emitted since UiMarkupList had no SpriteResolve property at all, and
<menu> markup never set Scrollable) before the implementation landed;
after: SingleColumn_Overflowing_DrawsRetailScrollbarChromeAtRightEdge and
Columns_Overflowing_ReservesSixteenPixels_LastColumnShrinksAccordingly
pin sprite ids + exact reserved-width geometry,
*_ContentFits_DrawsNo(Scrollbar|ReservationLastColumnKeepsFullRemainder)
pin the no-overflow/no-bar case, *_UpArrowClick_ScrollsUpByOneRow and
ThumbDrag_MovesTopRowAndIsReadableByASubsequentClick pin interactivity via
a following row click resolving to the moved position (mirroring
MarkupListColumnsTests' own wheel-scroll pin), and the four new
MarkupDocumentTests menu cases pin Scrollable/PopupScrollbarHideWhenDisabled/
the six chrome-id properties plus an end-to-end open-popup draw for both
the overflowing (draws chrome) and non-overflowing (draws none) cases.
Every pre-existing MarkupListColumnsTests/MarkupDocumentTests case stays
green unchanged (none of their fixtures overflow their own viewport).
docs/plugin-ui-markup.md updated: the <menu> style paragraph and a new
<list> "Scrollbar" section describe the new chrome + auto-reservation, and
the PITCH-transcription guidance is corrected to say the 16px scrollbar
column is now automatic (no more manual fold-in/double-reservation advice).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Plugin markup's <slider> always drew RetailScrollbarChrome's sprite
track/thumb, the same "big gold DAT art next to a plain plugin panel"
mismatch <menu style> already fixed for dropdowns. <slider style="..."> now
uses the identical plain/retail grammar (ValidateArtStyle, renamed and
generalized from the menu-only ValidateMenuStyle): plain (the default)
draws a flat dark track, 1px border, and a small flat nub via new
UiScrollbar.RetailArt=false + DrawPlainScalar — no SpriteResolve dependency
at all; style="retail" keeps RetailScrollbarChrome.ApplyHorizontal exactly
as before. RetailArt defaults to true on UiScrollbar itself, so every
non-plugin caller of this widget (retail LayoutDesc import, chat opacity
sliders, etc.) is byte-for-byte unaffected — only <slider>'s own
MarkupDocument case sets it false by default.
Every existing MossTank <slider> (Vitals' nine sliders, Buffs, Items'
Refill Worn Mana) has no style attribute, so they all switch to the plain
look automatically — consistent with the whole campaign's "no gold art"
direction, no XML changes needed.
Fixed a red pin this change created: Slider_MinMax_DrawsTheThumbAtTheRescaledNormalizedPosition
asserted the retail sprite thumb on a slider with no style attribute, which
now builds plain by default — opted it into style="retail" (same fix
shape as item 1's menu-scroll pin) and added a plain sibling,
Slider_NoStyleAttribute_DrawsAPlainFlatNubAtTheRescaledNormalizedPosition.
Mutation check: hardcoding DrawPlainScalar's horizontal nub x to 0 turned
the new plain test red ("expected a plain flat nub offset right of the
origin at 25%"); restoring the real ScalarPosition-driven x turns it green.
Documented <slider style> in docs/plugin-ui-markup.md, mirroring the
existing <menu style> paragraph.
tests/AcDream.Plugins.MossTank.Tests: 671/671 (unchanged — pure App-layer
rendering change, MossTank markup only sets no/default style).
tests/AcDream.App.Tests --filter Markup|Plugin|UiMenu|Slider: 278/3 skipped/281 (was 277/3/280, +1 new test).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Recaptured Items/Consumables/Buffs/Route/Meta screenshots (stale since
before this sub-slice) plus the new buff picker popup against a live
local ACE, isolated ACDREAM_CONFIG_DIR/ACDREAM_DATA_DIR (fresh authored
window positions, no stale-persisted-layout override), and an
ACDREAM_UI_PROBE_SCRIPT route through the five changed tabs. All six
confirm plain controls, no overlapping captions, correct grid rendering,
and real DAT move-icon art. Records the three S7.4-S7.6 commit SHAs and
their deviations in the ledger.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Follow-up to the UiMenu popup fix: the existing "menu style" paragraph in
plugin-ui-markup.md only described the closed-state button face swap from
the earlier S7 fix. Extend it to say the plain style now covers the whole
menu (closed AND open) — flat popup chrome matching <list>, a lighter hover
fill, no checkmark, and a plain scrollbar past the row cap — so a plugin
author reading the doc doesn't assume style="plain" only affects the
closed face.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Records the merge + three fix-round-A commits (grid scaling/Profiles
cleanup/popup split, the App.csproj plugin-copy-target bug, the
StartVisible visibility fix + duplicate-title cleanup), the real
DAT-font measurement that superseded the 07:55 lead's row-pitch theory,
and the carried CopyTo-naming deviation for the next reviewer.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Live-verified two real defects the previous commit's design didn't catch
without a running client:
1. Both new popups (mosstank-advanced.xml, mosstank-loot-editor.xml) were
registered with StartVisible=false, on the theory that the markup's own
visible="{AdvancedOptionsVisible}"/"{LootEditorVisible}" binding would be
the sole gate. It is NOT: RetailUiRuntime.MountPlugins wraps every
plugin window's markup availability in a PluginWindowVisibilityController
that ANDs it with a SEPARATE "requested visible" axis seeded from
StartVisible, flipped back to true only by OnShown() — the shelf's
un-minimize click. A ShowInSidePanel=false window has no shelf entry to
click, so StartVisible=false left "requested visible" permanently false
regardless of the markup binding, and both popups never rendered no
matter how many times the checkbox was toggled (confirmed live: checkbox
showed checked/green, no window appeared). Fix: StartVisible=true, which
keeps "requested visible" true from construction (OnHidden only ever
latches it back to false on a genuine minimize, explicitly no-opping
when the hide was caused by availability itself going false — exactly
what closing via "Back" does), leaving the markup binding as the sole
effective gate, same as the main panel's own always-true
ShowInSidePanel=true default.
2. Once visible, both popups showed garbled overlapping text at their top
edge ("AdvancedOptionsced Options — complete VTank settings table",
"Loot profileRules(first match wins)"): each file's own in-content
descriptive label duplicated the window's OS-level title bar
(MarkupDocument.Build auto-renders <panel title="..."> at a fixed (8,4)
— the exact corner the in-content label used to own back when these
were in-panel groups with no title bar of their own). Fix: dropped both
redundant labels; the window's own title ("Advanced Options" caption
duplicated as "MossTank Advanced Options"/"MossTank Loot Editor") is now
the only thing there.
Also repositioned both popups' default (x,y) from the placeholder
(440,60) — which overlapped the main panel — to (253,405)/(660,405),
below the main panel, side by side, both fitting within a 720-tall client.
Screenshots recaptured end to end against a live local ACE
(127.0.0.1:9000, testaccount/+Acdream) with the rebuilt Release exe from
this worktree, using an isolated ACDREAM_CONFIG_DIR/ACDREAM_DATA_DIR (no
stale persisted window layout from earlier probe runs — RetailWindowLayoutPersistence
keys plugin windows by name with authoredGeometryRevision=0, so a
previously-opened window's persisted x/y wins over any new authored
default forever; using a scratch config directory sidesteps that rather
than editing the real %APPDATA%\acdream\settings.json). tab-options.png,
tab-profiles.png, tab-vitals.png, and tab-monsters.png confirm the fix
round A grid-scaling and Profiles-cleanup commit's fixes hold on a live
client (no overlap, no gold buttons, no stacked New buttons, no dead
window space); popup-advanced.png and popup-loot-editor.png confirm both
popups now open as genuinely separate windows with clean single titles and
working move-up/move-down DAT icons (no blank button slots).
MossTank suite still 654/654; App markup/plugin/menu filter still
237/237 (no test changes in this commit — the earlier commit's
SecondaryPopupPanelsFitTheirOwnBoundsAndEveryBindingResolves already
covers both files' structure and bindings, and neither the visibility
seeding fix nor the label removal changes what those pins check).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Owner 2026-09-07: plugin output and 'Unknown command' land in the chat
scroll, not the SpewBox. Plugin text = Decal/VTank-faithful; the
unknown-command re-route is recorded as AD-124 (retail types it 0x1A).
Bad-argument refusals of real retail commands stay SpewBox-only (AP-183).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Brings cfa703065's owner-driven fix (plugin <menu> renders VTank's plain
flat combo by default; style="retail" opts back into the gold pushbutton
art) into the S7.1-S7.3 window/tabs work so the fix round below can build
on both. Resolved conflicts: the ledger keeps both worktrees' entries in
chronological order (07:55/08:05 owner-read entries appended after this
worktree's own S7.1/S7.2/S7.3 entries); plugin-ui-markup.md's <menu> row
lists both new attributes (scroll, style); MarkupDocumentTests.cs keeps
both new test blocks (slider min/max + menu scroll from this worktree,
menu style from latest-main-sync).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Owner direction 2026-09-07 (verbatim): "Unknown commands like /vt or
stuff from plugins shall now go to the SpewBox. They should go to the
chatbox." Retail itself types ChatCommandRouter's "Unknown command"
refusals as ClientLocal (0x1A) -- the bit every ChatInterface window's
default filter excludes, so they only ever reached the transient
SpewBox overlay and left no transcript record.
Three call sites in ChatCommandRouter.Submit/EmitVerbHelp now call
IChatCommandFeedback.ShowSystemMessage (chat scroll, retail
Default/0x00) instead of ShowInterfaceText (SpewBox): the degenerate-
prefix "Unknown command: {verb}." guard, EmitVerbHelp's confirmed-
null-help branch, and EmitVerbHelp's unresolved-verb fallback. Every
OTHER 0x1A refusal in this file (AP-183 bad-argument refusals of REAL
retail commands -- lifestone, marketplace, channel list/on/off,
allegiance, house, the generic HandleFailureEvent(0x26) fallback,
DoStupidChannelHack, DoReply) is unchanged and still SpewBox-only --
the owner named only unknown commands and plugin text.
This is a deliberate deviation from retail's own 0x1A typing, recorded
as register row AD-124 (also covers the sibling plugin-text change in
a follow-up commit). docs/ISSUES.md #363/#367 get a one-line note
under each pointing at the re-route; their CLOSED status is untouched.
Mutation check: temporarily reverted all three ShowSystemMessage call
sites back to ShowInterfaceText and confirmed the 3 new/changed pinned
tests fail (Assert.Single() on an empty chat log) while the AP-183
boundary test (real command, bad args, still SpewBox) continues to
pass -- see ChatCommandRouterFeedbackRoutingTests.cs and the updated
ChatCommandRouterTests.cs/RetailCommandHelpTableTests.cs assertions.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Records commit 57ced0aff (the VTank 23-column Monsters grid) in the
slice-7 ledger, including the Weapon/Offhand roster-cycle deviation and
the full-suite pass counts.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
N4 + summary: /status and /quit are console-intercepted verbs (they
never reach ChatCommandRouter), unlike @status which is a real server
command and still passes through untouched. Records the full S1-S7/
N1-N5 fix-round outcome, final Headless (207/1/208) and App LaunchOptions
(4/4) suite counts, and the one-commit-per-item/mutation-shown-to-fail
discipline used throughout.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Adds style to the <menu> attribute row and one sentence explaining why
plain is now the default (owner report: retail's gold pushbutton art
read as an out-of-place button next to a plugin's own dark list boxes)
and what style="retail" opts back into.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>