Merge branch 'main' into claude/m3-implementation-57f9d7
ci / build and test (push) Failing after 2s

Three files conflicted, and two of the resolutions are more than a choice of
side.

QuickConnectTests had both branches fixing the same build break — main's M2
merge left the shell's constructor with an ISftpSessionFactory nobody passed.
Main's version wins because it carries a comment saying why the palette never
needs a session.

VaultSession's conflict is adjacent edits: main added the remembered sign-in
members and this branch changed SyncAsync's summary from "the active vault" to
"one vault". Both kept.

VaultViewModel is the one that matters. Main taught the background pass to
report a sync that had to start over, on the grounds that a machine which
silently re-read a whole vault has had something happen to it; this branch
turned a pass into one report per readable vault. Taking either side alone
would have lost the other, so ResyncedFromStart is now one of the conditions
IsWorthReporting checks, per vault.

Merging also broke something neither branch could have caught alone, and the
build would not have said a word. SyncOnceAsync cleared LastSyncFailed
unconditionally, which was right while a pass was one vault and a failure was
an exception that never reached that line. A failure is now a report — one
unreachable team vault must not stop the others syncing — so the flag was being
cleared over a vault that had just failed, lighting the titlebar SYNCED. It is
computed from the report instead, in the one place both callers go through, so
the manual command gets it as well as the loop. The background pass still
swallows the message and keeps the fact, which is what
AnAutomaticPassThatFails_LeavesTheStatusAlone is there to hold it to.

Two comments the auto-merge left describing a world with one vault in it: the
SCOPES rail's, which said team vaults are refused by the access service, and
the host sidebar's "One heading, for one vault".
This commit is contained in:
2026-07-31 12:26:59 +02:00
42 changed files with 3708 additions and 155 deletions
+19 -3
View File
@@ -1,6 +1,7 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="using:DodoSSH.Client.App.ViewModels"
xmlns:views="using:DodoSSH.Client.App.Views"
x:Class="DodoSSH.Client.App.Views.HostSidebar"
x:DataType="vm:VaultViewModel">
@@ -35,8 +36,10 @@
</Border>
<!--
One heading, for one vault. The chevron folds the list away; the count is the collection's own, so it
follows the filter without a second number to keep in step.
One heading, which names the vault while there is one and says ALL VAULTS once a team's is readable
too — a heading that went on naming the personal vault over a list containing a team's hosts would be
a quiet lie, so the rows carry the vault name instead. The chevron folds the list away; the count is
the collection's own, so it follows the filter without a second number to keep in step.
-->
<Button Grid.Row="1" Classes="flat grouphead" Command="{Binding ToggleHostsCommand}"
HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch">
@@ -183,7 +186,7 @@
</Border>
<Border Grid.Row="4" Padding="10,8" BorderBrush="{StaticResource BorderSubtle}" BorderThickness="0,1,0,0"
IsVisible="{Binding !IsEditing}">
IsVisible="{Binding ShowsHostActions}">
<StackPanel Orientation="Horizontal" Spacing="6">
<Button Classes="ghost" Content="+ NEW HOST" Command="{Binding NewHostCommand}" />
<Button Classes="ghost" Content="EDIT" Command="{Binding EditSelectedHostCommand}" />
@@ -191,6 +194,19 @@
</StackPanel>
</Border>
<!--
The question DELETE asks, in the place the buttons were rather than under them. This strip is at the
bottom edge of a column whose middle is a list that has already taken every spare pixel, so a second
block below the first would push its own buttons off the window — the same reasoning that swaps the
unlock card for the sign-out card rather than stacking them. Swapping also means DELETE cannot be
pressed again while its own question is up; see VaultViewModel.ShowsHostActions.
-->
<Border Grid.Row="4" Padding="10,8" Background="{StaticResource DangerWash}"
BorderBrush="{StaticResource BorderSubtle}" BorderThickness="0,1,0,0"
IsVisible="{Binding IsConfirmingDeletion}">
<views:ConfirmDeleteCard />
</Border>
</Grid>
</UserControl>