Merge branch 'claude/group-creation-hosts-dragdrop-56c6a3'
ci / build and test (push) Canceled after 1m6s
ci / android head (push) Canceled after 0s
ci / api image (push) Canceled after 0s

This commit is contained in:
2026-08-03 15:50:12 +02:00
8 changed files with 432 additions and 147 deletions
+42 -31
View File
@@ -269,33 +269,32 @@ single-process test can reach.
Open the hosts screen without creating any group.
**Pass:** the grid is the flat wrap of host cards it always was — no GROUPS section above it, no headings
between the cards, no UNGROUPED, nothing saying the hosts are unfiled.
**Pass:** the grid is the flat wrap of host cards it always was — no GROUPS section above it, no chip on any
card, nothing saying the hosts are unfiled.
**Failure means:** the "invisible until used" property is gone, and every existing user gets a heading they
did not ask for. `RebuildSidebarRows` returns early when `Groups` is empty; that early return is the feature.
**Failure means:** the "invisible until used" property is gone, and every existing user gets a section they
did not ask for. `HasGroups` is what hides the row of group cards; `HostRowViewModel.HasGroup` hides the chip.
### 3.2 Filing hosts, and folding a heading
### 3.2 Filing hosts, and reading which group a card is in
Make two groups, file some hosts into each through the host editor, then click a heading.
Make two groups and file some hosts into each through the host editor.
**Pass:** the heading's chevron flips and its hosts disappear; the count on the heading does not change,
because it counts what is in the group rather than what is on screen. Clicking again brings them back.
**Also check:** clicking a heading does not change which host is selected — the drawer stays open on the
same machine and its EDIT and DELETE go on acting on it. This is asserted in a test, but the test drives the
view model directly; what it cannot see is whether the `ListBox` writes something else back through the
binding first.
**Pass:** every filed card carries the group's name as an accent chip under its address; the cards stay one
flat wrap, in one order, with nothing between them. **There is no heading and no fold on the desktop** — the
headings, their chevrons and UNGROUPED are the phone's, whose list has no room for a row of group cards.
**Then press a group card.** The grid narrows to that group's hosts, the card is marked as chosen, and
SHOW ALL appears beside GROUPS. Pressing it brings the rest back and unmarks the card.
**Failure means:** a full-width bar with a chevron between the cards is the old grouping coming back through
`SidebarRows` — the desktop grid binds `VisibleHosts`. See `HostsScreen.axaml`.
### 3.3 Deleting a group with hosts in it
Select a group with hosts and press DELETE.
**Pass:** the question names how many hosts are filed under it and says they stay. Agreeing removes the
group; the hosts reappear under UNGROUPED with everything else about them unchanged.
group; the hosts lose their chip and are otherwise unchanged.
**Failure means:** if the hosts vanish, the delete is rewriting host payloads, which it must not — see
`HostGroupRepository`.
@@ -305,8 +304,8 @@ group; the hosts reappear under UNGROUPED with everything else about them unchan
Make a group on machine A, file a host into it, sync. On machine B, sync, then delete the group and sync
again. Back on A, sync.
**Pass:** A shows the host under UNGROUPED. Open that host's editor: the group picker shows "(a group that is
no longer here)" and *keeps it selected*. Change the port and save.
**Pass:** A shows the host with no chip on its card, as though it were in no group. Open that host's editor:
the group picker shows "(a group that is no longer here)" and *keeps it selected*. Change the port and save.
**Failure means:** if the picker opened on "No group", saving has just unfiled the host — quietly, as a side
effect of an unrelated edit. That is the case `BuildGroupChoices` adds the placeholder for.
@@ -642,33 +641,45 @@ Start 7.1's slow connection and press GIVE UP (or the tab's cross) while it is s
opened — it is a real shell, and one running with nothing naming it would be worse than one that comes
back.
### 7.6 Dragging a host into a group · **least covered, like all drag and drop**
### 7.6 Dragging a host onto a group card · **least covered, like all drag and drop**
Make two groups and file a host into one. Drag a host card onto another group's heading; onto a host card
inside another group; and onto UNGROUPED.
Make two groups and file a host into one. Drag a host card up onto the other group's card.
**Pass:** whatever is under the pointer washes accent while the pointer is over it, the cursor shows a move
rather than a refusal, and the drop files the host — it moves under that heading and the counts on both
headings change. Dropping onto its own group's heading is refused while still in the air.
**Pass:** the group card under the pointer takes a two-pixel accent border while the pointer is over it, the
cursor shows a move rather than a refusal, and the drop files the host — the chip on its card changes to the
new group's name, the host counts under both group cards change, and the status line says where it went.
**Failure means:** headless Avalonia cannot synthesise a platform drag, so nothing about this gesture is
automated. The write it performs is: `MovingAHostToAGroup_FilesItAndLeavesItSelected`.
**Also check three refusals**, each of which must show the "no" cursor and mark nothing: over the card of the
group the host is *already* in; over another **host** card, which is deliberately not a target now that there
are no headings to say which group it would mean; and over the empty space around the cards.
**And getting a host back out** is the host's own editor — pick "No group" in its picker. There is no
UNGROUPED target on the desktop any more, because there is no UNGROUPED heading for it to be.
**And a drag held near the top or bottom edge of the grid scrolls it**, which is what makes this usable at
all with forty machines: the group cards are the first thing in the scroller, and a drag cannot use the
wheel. The pointer has to keep moving inside the band — a stationary pointer gets no drag events.
**Failure means:** headless Avalonia cannot synthesise a platform drag, so the picking up, the cursor and
the drop are covered by nothing. What *is* automated is the decision each drag event takes —
`HostGridTests.TheGroupCardsAreWhatAcceptsADroppedHost` raises a real `DragOver` over both kinds of card —
and the write at the end, `ShellFlowTests.MovingAHostToAGroup_FilesItAndLeavesItSelected`.
### 7.7 A click still selects, and a double click still connects
Click host cards; drag one a few pixels without releasing; double-click one. Then double-click a group
heading.
Click host cards; drag one a few pixels without releasing; double-click one. Then double-click the space
around the cards.
**Pass:** a click selects, a small movement starts nothing, and a double click connects. Double-clicking a
heading folds it and unfolds it again and connects to nothing.
**Pass:** a click selects, a small movement starts nothing, and a double click connects. Double-clicking the
space around the cards connects to nothing.
**Failure means:** the 5-pixel threshold in `HostsScreen.axaml.cs` is not doing its job — the same failure
as 2.16 on the other screen, and here it would make the grid unusable. A heading that connects means the
as 2.16 on the other screen, and here it would make the grid unusable. Empty space that connects means the
double-tap handler has lost its check that the pointer was over a card.
### 7.8 The highlight clears after a drag that goes nowhere
Drag a host over a heading and release outside the grid, or press Escape mid-drag.
Drag a host over a group card and release outside the grid, or press Escape mid-drag.
**Pass:** the wash goes away.
@@ -676,7 +687,7 @@ Drag a host over a heading and release outside the grid, or press Escape mid-dra
With host A selected, right-click host B and choose Delete.
**Pass:** the question names **B**. Then right-click a group heading.
**Pass:** the question names **B**. Then right-click the space around the cards.
**Pass:** no menu opens at all, and the host selection has not moved.