Android: the redesign — a Vaults tab with hosts, keychain and snippets, card lists, a keychain that can add, and files that act on what is chosen
ci / build and test (push) Successful in 3m49s
ci / android head (push) Successful in 3m55s
ci / api image (push) Successful in 6s
ci / desktop nightly (push) Successful in 1m22s

The bottom bar is Vaults · Connections · Settings, drawn as Material 3 pills with icons from the embedded
Material font and a count of open sessions on Connections, which replaces the strip of shells above the
bar. Vaults is the open vault: its header carries the name as a switch to the vaults screen, the sync
light and a lock icon, and a Hosts · Keychain · Snippets segmented control sits under it. The keychain
and snippets move there from Settings (IsVaultSurface lights the tab; IsMoreSurface no longer lists
them), and back from either goes to the hosts. Settings becomes an account card and grouped sections:
security, vaults and history, files, this app.

Host rows are cards with a terminal tile and the live dot on its corner. The connect sheet names the
host and its address, labels its box, focuses it as it opens, reveals the password on request, and puts
CANCEL left and CONNECT right; every two-button row on the head now confirms on the right. While a
hosts-screen sheet is up the header and bar are dimmed too and a tap on them lowers it, and the bar
stands down while the software keyboard is up.

The keychain gains a floating + and a sheet of the desktop's five adds (generate a key, paste a key, a
password, a bucket, a tag) over the editors VaultViewModel already had, drawn as a full-screen page with
labels above the boxes; a chosen item gets EDIT and COPY PUBLIC KEY beside MOVE and DELETE. Snippets get
a floating + and cards that say RUNS or INSERTS. Files trade the seven-button block for a selection bar
(save, and a ⋮ sheet with rename, move, permissions and a red delete), folder and file tiles, an upload
button, and header actions for a terminal on the same host, refresh and close; a terminal's + sheet
opens SFTP on that terminal's own host.

The theme takes the audit's visual findings: Fluent's ClipToBounds is cleared on glowing buttons so the
glow is no longer cut to a square (the desktop's Button.accent too), prose is Montserrat with mono kept
for addresses and code, disabled secondary buttons are quieter than enabled ones, Fluent's dropdown,
checkbox, switch, focus and selection brushes come from the palette, touch no longer leaves hover fills
behind, and lists, nav entries and icon buttons carry names for TalkBack.

Builds clean; checked on the emulator end to end: unlock, the three vault views, adding and deleting a
password, connecting through the new sheet, files on the terminal's host with the selection bar and
its sheet. App tests 727 of 733 pass with 5 skipped; the one failure, TheCountsAPhoneShows_
FollowEveryForwardsState, passes three runs of three alone and is untouched here. Layout tests pass.
This commit is contained in:
jaap-jan committed 2026-10-04 15:50:09 +02:00
1 parent 6806e542f9
commit 5b32c76c95
24 files changed
+2022 -654

No files matched your search

@@ -67,15 +67,15 @@
held as ids rather than as rows, see VaultViewModel.chosenHostIds — so searching for the second half
of a selection and ticking it is a thing this screen supports rather than merely survives.
-->
<Border Grid.Row="0" Margin="14,10,14,4" Background="{StaticResource Field}"
BorderBrush="{StaticResource BorderMid}" BorderThickness="1" CornerRadius="10" Height="44">
<Border Grid.Row="0" Margin="16,10,16,4" Background="{StaticResource Field}"
BorderBrush="{StaticResource BorderMid}" BorderThickness="1" CornerRadius="12" Height="46">
<Grid ColumnDefinitions="Auto,*">
<TextBlock Grid.Column="0" Text="⌕" Foreground="{StaticResource TextFaint}" FontSize="13"
Margin="13,0,0,0" VerticalAlignment="Center" />
<TextBlock Grid.Column="0" Classes="icon" Text="&#xE8B6;" Foreground="{StaticResource TextFaint}"
FontSize="20" Margin="12,0,0,0" />
<TextBox Grid.Column="1" Text="{Binding HostFilter}" PlaceholderText="Search hosts"
Padding="9,0" VerticalContentAlignment="Center"
Background="Transparent" BorderThickness="0"
Foreground="{StaticResource Text}" FontFamily="{StaticResource MonoFont}" FontSize="12" />
Foreground="{StaticResource Text}" FontSize="14" />
</Grid>
</Border>
@@ -181,11 +181,11 @@
Text="{Binding ChosenBindingToBringNote}" />
<Grid ColumnDefinitions="*,8,*">
<Button Grid.Column="0" Classes="primary" Height="44"
<Button Grid.Column="2" Classes="primary" Height="44"
Content="{Binding ChosenHostVaultPanelVerb}"
Command="{Binding ConfirmSendChosenHostsToAVaultCommand}"
IsEnabled="{Binding !IsBusy}" />
<Button Grid.Column="2" Classes="secondary" Height="44" Content="CANCEL"
<Button Grid.Column="0" Classes="secondary" Height="44" Content="CANCEL"
Command="{Binding CancelSendChosenHostsToAVaultCommand}" />
</Grid>
</StackPanel>
@@ -224,9 +224,9 @@
Text="A group lends its port, its account and its key to every host filed under it that says nothing itself, so this can change what these machines dial. Nothing else about them moves." />
<Grid ColumnDefinitions="*,8,*">
<Button Grid.Column="0" Classes="primary" Height="44" Content="FILE"
<Button Grid.Column="2" Classes="primary" Height="44" Content="FILE"
Command="{Binding ConfirmRegroupChosenHostsCommand}" IsEnabled="{Binding !IsBusy}" />
<Button Grid.Column="2" Classes="secondary" Height="44" Content="CANCEL"
<Button Grid.Column="0" Classes="secondary" Height="44" Content="CANCEL"
Command="{Binding CancelRegroupChosenHostsCommand}" />
</Grid>
</StackPanel>
@@ -250,9 +250,9 @@
Text="{Binding PendingDeletion.Usage}" />
<Grid ColumnDefinitions="*,8,*" Margin="0,4,0,0">
<Button Grid.Column="0" Classes="danger" Height="44" Content="DELETE"
<Button Grid.Column="2" Classes="danger" Height="44" Content="DELETE"
Command="{Binding ConfirmDeleteCommand}" IsEnabled="{Binding !IsBusy}" />
<Button Grid.Column="2" Classes="secondary" Height="44" Content="KEEP" FontSize="10.5"
<Button Grid.Column="0" Classes="secondary" Height="44" Content="KEEP" FontSize="10.5"
Command="{Binding CancelDeleteCommand}" />
</Grid>
</StackPanel>
@@ -291,9 +291,9 @@
</CheckBox>
<Grid ColumnDefinitions="*,8,*" Margin="0,4,0,0">
<Button Grid.Column="0" Classes="danger" Height="44" Content="DELETE"
<Button Grid.Column="2" Classes="danger" Height="44" Content="DELETE"
Command="{Binding ConfirmDeleteCommand}" IsEnabled="{Binding !IsBusy}" />
<Button Grid.Column="2" Classes="secondary" Height="44" Content="KEEP" FontSize="10.5"
<Button Grid.Column="0" Classes="secondary" Height="44" Content="KEEP" FontSize="10.5"
Command="{Binding CancelDeleteCommand}" />
</Grid>
</StackPanel>
@@ -338,9 +338,9 @@
Text="The group goes with the groups inside it and every host filed under them, all re-encrypted with the other vault's key. Any group it is nested under stays behind, so it arrives at the top level, and the hosts' tags stay behind with it." />
<Grid ColumnDefinitions="*,8,*">
<Button Grid.Column="0" Classes="primary" Height="44" Content="MOVE"
<Button Grid.Column="2" Classes="primary" Height="44" Content="MOVE"
Command="{Binding ConfirmMoveGroupCommand}" IsEnabled="{Binding !IsBusy}" />
<Button Grid.Column="2" Classes="secondary" Height="44" Content="CANCEL"
<Button Grid.Column="0" Classes="secondary" Height="44" Content="CANCEL"
Command="{Binding CancelMoveGroupCommand}" />
</Grid>
</StackPanel>
@@ -374,11 +374,21 @@
<Style Selector="ListBoxItem">
<Setter Property="Padding" Value="0" />
<Setter Property="MinHeight" Value="0" />
<Setter Property="Margin" Value="10,1" />
<Setter Property="CornerRadius" Value="12" />
<Setter Property="Margin" Value="16,4" />
<Setter Property="CornerRadius" Value="16" />
<!--
A name for TalkBack. Without it the item announced its view model's type name —
"DodoSSH.Client.Shell.ViewModels.HostRowViewModel" (audit H4). Both row kinds in this list carry a
Label, and a reflection binding is what lets one setter serve the two of them.
-->
<Setter Property="AutomationProperties.Name" Value="{ReflectionBinding Label}" />
</Style>
<!--
◆ A host is a card since the 2026-10-04 redesign: Panel on a hairline at the 16 radius, where it was a
transparent row on the canvas. A list of machines is a list of things, each one a target, and a card
says where one ends and the next begins without a rule between them.
◆ The ticked row, filled. On a Border inside the item rather than on the item's own selected state,
because "ticked" is a property of the host and the list has no idea about it — the fill and the
tick at the right-hand end are the same fact drawn twice, which is deliberate: a fill alone is
@@ -386,11 +396,14 @@
360dp row.
-->
<Style Selector="Border.hostrow">
<Setter Property="Background" Value="Transparent" />
<Setter Property="CornerRadius" Value="12" />
<Setter Property="Background" Value="{StaticResource Panel}" />
<Setter Property="BorderBrush" Value="{StaticResource Border}" />
<Setter Property="BorderThickness" Value="1" />
<Setter Property="CornerRadius" Value="16" />
</Style>
<Style Selector="Border.hostrow.chosen">
<Setter Property="Background" Value="{StaticResource Active}" />
<Setter Property="BorderBrush" Value="{StaticResource TerminalTabAccent}" />
</Style>
</ListBox.Styles>
@@ -402,7 +415,7 @@
the filter, which is what a heading over a filtered list has to say.
-->
<DataTemplate DataType="vm:SidebarGroupHeader">
<Grid ColumnDefinitions="Auto,*,Auto,Auto" Margin="8,12,8,5">
<Grid ColumnDefinitions="Auto,*,Auto,Auto" Margin="4,14,0,2">
<TextBlock Grid.Column="0" Classes="detail" Text="{Binding Chevron}" VerticalAlignment="Center" />
<!--
@@ -419,8 +432,9 @@
</Border>
</StackPanel>
<TextBlock Grid.Column="2" Classes="detail" Text="{Binding Count}" FontSize="9"
VerticalAlignment="Center" />
<Border Grid.Column="2" Classes="tag outline" VerticalAlignment="Center">
<TextBlock Text="{Binding Count}" />
</Border>
<!--
◆ The phone's only way to do anything to a group. The desktop reaches all three from a menu on
@@ -435,25 +449,40 @@
Hidden on the ungrouped heading, which has no group behind it to act on.
-->
<Button Grid.Column="3" Classes="icon" FontSize="15" Content="⋯" Margin="4,0,0,0"
<Button Grid.Column="3" Classes="icon" Margin="4,0,0,0"
AutomationProperties.Name="{Binding Label, StringFormat='Group {0}: edit, move or delete'}"
IsVisible="{Binding GroupId, Converter={x:Static ObjectConverters.IsNotNull}}"
Command="{Binding $parent[views:HostsScreen].((vm:VaultViewModel)DataContext).OpenGroupSheetCommand}"
CommandParameter="{Binding}" />
CommandParameter="{Binding}">
<TextBlock Classes="icon" Text="&#xE5D4;" FontSize="20" />
</Button>
</Grid>
</DataTemplate>
<!-- A host. -->
<DataTemplate DataType="vm:HostRowViewModel">
<Border Classes="hostrow" Classes.chosen="{Binding IsChosen}">
<Grid ColumnDefinitions="Auto,*,Auto,Auto" MinHeight="56" Margin="14,12">
<Grid ColumnDefinitions="Auto,*,Auto,Auto" MinHeight="48" Margin="12,11">
<!-- Green means a terminal is open on this host right now. See the .dot style. -->
<Ellipse Grid.Column="0" Classes="dot" Classes.live="{Binding IsConnected}" Width="8" Height="8"
VerticalAlignment="Center" />
<!--
◆ The redesign's tile: a terminal mark on the accent's wash, with the live dot on its corner.
Green still means one thing only — a terminal is open on this host right now, see the .dot
style — and riding the tile it says which machine without spending a column on it.
-->
<Panel Grid.Column="0" Width="42" Height="42" VerticalAlignment="Center">
<Border CornerRadius="12" Background="{StaticResource AccentSoft}">
<TextBlock Classes="icon" Text="&#xEB8E;" FontSize="21" Foreground="{StaticResource AccentText}" />
</Border>
<Border Width="13" Height="13" CornerRadius="7" Background="{StaticResource Panel}"
HorizontalAlignment="Right" VerticalAlignment="Bottom" Margin="0,0,-3,-3"
IsVisible="{Binding IsConnected}">
<Ellipse Classes="dot live" Width="9" Height="9" />
</Border>
</Panel>
<StackPanel Grid.Column="1" Margin="12,0,0,0" Spacing="3" VerticalAlignment="Center">
<StackPanel Grid.Column="1" Margin="14,0,0,0" Spacing="4" VerticalAlignment="Center">
<StackPanel Orientation="Horizontal" Spacing="7">
<TextBlock Classes="mono" Text="{Binding Label}" FontSize="14" FontWeight="SemiBold"
<TextBlock Classes="mono" Text="{Binding Label}" FontSize="15" FontWeight="SemiBold"
TextTrimming="CharacterEllipsis" />
<Border Classes="tag outline" IsVisible="{Binding HasVaultBadge}">
<TextBlock Text="{Binding VaultBadge}" />
@@ -464,10 +493,10 @@
</Border>
</StackPanel>
<StackPanel Orientation="Horizontal" Spacing="6">
<TextBlock Classes="detail" FontSize="10.5" Text="{Binding Address}"
<TextBlock Classes="detail code" FontSize="11.5" Text="{Binding Address}"
TextWrapping="NoWrap" TextTrimming="CharacterEllipsis" />
<TextBlock Classes="detail" FontSize="10.5" Text="·" />
<TextBlock Classes="detail" FontSize="10.5" Text="{Binding Authentication}" />
<TextBlock Classes="detail" Text="·" />
<TextBlock Classes="detail" Text="{Binding Authentication}" />
</StackPanel>
<!--
@@ -499,8 +528,8 @@
Background="{StaticResource LiveWash}">
<TextBlock Text="live" Foreground="{StaticResource Live}" />
</Border>
<TextBlock Grid.Column="2" Text="›" Foreground="{StaticResource TextGhost}" FontSize="15"
VerticalAlignment="Center" IsVisible="{Binding !IsConnected}" />
<TextBlock Grid.Column="2" Classes="icon" Text="&#xE5CC;" Foreground="{StaticResource TextGhost}"
FontSize="22" IsVisible="{Binding !IsConnected}" />
<!--
◆ The tick, in a column of its own at the far end rather than in place of the dot or the
@@ -509,9 +538,8 @@
choosing a host hide a fact about it. An Auto column takes no width while the mark is
collapsed, so a list with nothing ticked is laid out exactly as it was.
-->
<TextBlock Grid.Column="3" Text="✓" Margin="10,0,0,0" FontSize="15"
Foreground="{StaticResource AccentText}" VerticalAlignment="Center"
IsVisible="{Binding IsChosen}" />
<TextBlock Grid.Column="3" Classes="icon" Text="&#xE86C;" Margin="10,0,0,0" FontSize="22"
Foreground="{StaticResource AccentText}" IsVisible="{Binding IsChosen}" />
</Grid>
</Border>
</DataTemplate>
@@ -604,9 +632,9 @@
Text="A host inside this group uses these wherever it says nothing itself. They are read at connect time rather than copied in, so changing one here changes every host that never overrode it." />
<Grid ColumnDefinitions="*,8,*">
<Button Grid.Column="0" Classes="primary" Height="44" Content="SAVE"
<Button Grid.Column="2" Classes="primary" Height="44" Content="SAVE"
Command="{Binding SaveGroupCommand}" />
<Button Grid.Column="2" Classes="secondary" Height="44" Content="CANCEL"
<Button Grid.Column="0" Classes="secondary" Height="44" Content="CANCEL"
Command="{Binding CancelGroupEditCommand}" />
</Grid>
</StackPanel>
@@ -628,10 +656,13 @@
twelfth machine while eleven are ticked for deletion is worse, because it does something and the
something is not what the screen is about.
-->
<Button Grid.Row="2" Classes="fab" Content="+" Margin="0,0,18,18"
<Button Grid.Row="2" Classes="fab" Margin="0,0,18,18"
HorizontalAlignment="Right" VerticalAlignment="Bottom"
AutomationProperties.Name="Add a host or a group"
IsVisible="{Binding ShowsAddButton}"
Command="{Binding OpenAddSheetCommand}" />
Command="{Binding OpenAddSheetCommand}">
<TextBlock Classes="icon" Text="&#xE145;" FontSize="26" />
</Button>
</Grid>
@@ -646,9 +677,10 @@
"Which kind of thing are you adding" has no wrong answer and no answer at all is one of them.
And it lives here rather than in PhoneShell. HostKeySheet sits up there because the files screen raises
it too; nothing but this screen raises this. The cost is that the scrim covers the screen and not the
64px nav bar underneath, so the four destinations stay lit and tappable — which for a menu is right:
navigating away is a perfectly good way to decide not to add anything.
it too; nothing but this screen raises this. The cost used to be that the scrim covered the screen and
not the header or the nav bar, which stayed lit and tappable round every sheet (audit M12). PhoneShell
now dims those two rows as well while any sheet here is up, and a tap on them lowers the sheet — see
PhoneShell.ShowsSheetScrim.
-->
<Panel IsVisible="{Binding IsAddSheetOpen}">
@@ -670,7 +702,7 @@
<Button Classes="row" Margin="0,10,0,0" Command="{Binding NewHostCommand}">
<StackPanel Spacing="3" VerticalAlignment="Center">
<TextBlock Classes="mono" FontSize="13.5" FontWeight="SemiBold" Text="New host" />
<TextBlock FontSize="14.5" FontWeight="SemiBold" Foreground="{StaticResource Text}" Text="New host" />
<TextBlock Classes="detail" FontSize="10.5"
Text="A machine to connect to, filed under a group if you want one." />
</StackPanel>
@@ -678,7 +710,7 @@
<Button Classes="row" Command="{Binding NewGroupCommand}">
<StackPanel Spacing="3" VerticalAlignment="Center">
<TextBlock Classes="mono" FontSize="13.5" FontWeight="SemiBold" Text="New group" />
<TextBlock FontSize="14.5" FontWeight="SemiBold" Foreground="{StaticResource Text}" Text="New group" />
<TextBlock Classes="detail" FontSize="10.5"
Text="A heading for hosts, and the port, user and key they fall back to." />
</StackPanel>
@@ -742,19 +774,19 @@
<Button Classes="row" IsVisible="{Binding HasOneChosenHost}"
Command="{Binding ConnectToChosenHostCommand}">
<TextBlock Classes="mono" FontSize="13.5" FontWeight="SemiBold" VerticalAlignment="Center"
<TextBlock FontSize="14.5" FontWeight="SemiBold" Foreground="{StaticResource Text}" VerticalAlignment="Center"
Text="Connect" />
</Button>
<Button Classes="row" IsVisible="{Binding HasOneChosenHost}"
Command="{Binding BrowseChosenHostCommand}">
<TextBlock Classes="mono" FontSize="13.5" FontWeight="SemiBold" VerticalAlignment="Center"
<TextBlock FontSize="14.5" FontWeight="SemiBold" Foreground="{StaticResource Text}" VerticalAlignment="Center"
Text="Connect via SFTP" />
</Button>
<Button Classes="row" Command="{Binding MoveChosenHostsToVaultCommand}">
<StackPanel Spacing="3" VerticalAlignment="Center">
<TextBlock Classes="mono" FontSize="13.5" FontWeight="SemiBold" Text="Move to vault" />
<TextBlock FontSize="14.5" FontWeight="SemiBold" Foreground="{StaticResource Text}" Text="Move to vault" />
<TextBlock Classes="detail" FontSize="10.5" TextWrapping="Wrap"
Text="Re-encrypted with the other vault's key. Groups and tags stay behind." />
</StackPanel>
@@ -762,19 +794,19 @@
<Button Classes="row" Command="{Binding CopyChosenHostsToVaultCommand}">
<StackPanel Spacing="3" VerticalAlignment="Center">
<TextBlock Classes="mono" FontSize="13.5" FontWeight="SemiBold" Text="Copy to vault" />
<TextBlock FontSize="14.5" FontWeight="SemiBold" Foreground="{StaticResource Text}" Text="Copy to vault" />
<TextBlock Classes="detail" FontSize="10.5" TextWrapping="Wrap"
Text="A second copy over there, and these stay where they are." />
</StackPanel>
</Button>
<Button Classes="row" Command="{Binding RegroupChosenHostsCommand}">
<TextBlock Classes="mono" FontSize="13.5" FontWeight="SemiBold" VerticalAlignment="Center"
<TextBlock FontSize="14.5" FontWeight="SemiBold" Foreground="{StaticResource Text}" VerticalAlignment="Center"
Text="Change group" />
</Button>
<Button Classes="row" Command="{Binding DuplicateChosenHostsCommand}">
<TextBlock Classes="mono" FontSize="13.5" FontWeight="SemiBold" VerticalAlignment="Center"
<TextBlock FontSize="14.5" FontWeight="SemiBold" Foreground="{StaticResource Text}" VerticalAlignment="Center"
Text="Duplicate" />
</Button>
@@ -782,7 +814,7 @@
<Button Classes="row" Command="{Binding DeleteChosenHostsCommand}">
<StackPanel Spacing="3" VerticalAlignment="Center">
<TextBlock Classes="mono" FontSize="13.5" FontWeight="SemiBold"
<TextBlock FontSize="14.5" FontWeight="SemiBold"
Foreground="{StaticResource Danger}" Text="Remove" />
<TextBlock Classes="detail" FontSize="10.5" TextWrapping="Wrap"
Text="Asks first, and says how many. There is no undo." />
@@ -816,34 +848,73 @@
the box the next tap reads, and it would satisfy the length check that decides whether to raise this at
all. See CancelConnectPassword.
-->
<Panel IsVisible="{Binding IsAskingForConnectPassword}">
<Panel x:Name="PasswordSheet" IsVisible="{Binding IsAskingForConnectPassword}">
<Button Classes="scrim" Command="{Binding CancelConnectPasswordCommand}" />
<!--
◆ The 2026-10-04 redesign's connect sheet. Three changes from the audit (M12, and the placeholder-as-
label finding), and one from the redesign itself:
· CANCEL on the left and CONNECT on the right, which is the order Android puts a confirming action
in — the sheet had them the other way round, and so did every two-button row on this head;
· the box has a label over it, so what it is for is still on screen once something is typed;
· it takes the focus as it opens — see HostsScreen.FocusThePasswordBox — so the keyboard is up
without a second tap on the one box the sheet exists for;
· the host is named at the top with its address under it, in the card shape the list draws it in,
and a ✕ beside it closes the sheet as the scrim does.
The eye beside the box reveals what was typed, for the one case a phone keyboard makes common: a
password typed blind on glass is a password typed wrong. It is TextBox.RevealPassword, so the secret
is never copied anywhere to be shown.
-->
<Border VerticalAlignment="Bottom" Background="{StaticResource Panel}"
BorderBrush="{StaticResource BorderMid}" BorderThickness="0,1,0,0"
CornerRadius="22,22,0,0" Padding="20,18,20,16">
<StackPanel Spacing="10">
CornerRadius="22,22,0,0" Padding="20,10,20,18">
<StackPanel Spacing="12">
<Border Width="38" Height="4" CornerRadius="2" Background="{StaticResource BorderMid}"
HorizontalAlignment="Center" Margin="0,0,0,6" />
HorizontalAlignment="Center" Margin="0,0,0,4" />
<!--
It names the host, as the bar did and for the same reason: the row it was tapped on is behind a
sheet and a scrim, and "which machine is this password for" is the one thing somebody about to
type a secret has to be sure of.
-->
<StackPanel Orientation="Horizontal" Spacing="8">
<TextBlock Classes="label" Text="CONNECT TO" />
<TextBlock Classes="mono" FontSize="11" VerticalAlignment="Center"
Text="{Binding SelectedHost.Label}" TextTrimming="CharacterEllipsis" />
</StackPanel>
<Grid ColumnDefinitions="Auto,12,*,Auto">
<Border Grid.Column="0" Width="42" Height="42" CornerRadius="12"
Background="{StaticResource AccentSoft}">
<TextBlock Classes="icon" Text="&#xEB8E;" FontSize="21" Foreground="{StaticResource AccentText}" />
</Border>
<StackPanel Grid.Column="2" Spacing="3" VerticalAlignment="Center">
<TextBlock Classes="mono" FontSize="15" FontWeight="SemiBold"
Text="{Binding SelectedHost.Label}" TextTrimming="CharacterEllipsis" />
<TextBlock Classes="detail code" Text="{Binding SelectedHost.Address}"
TextWrapping="NoWrap" TextTrimming="CharacterEllipsis" />
</StackPanel>
<Button Grid.Column="3" Classes="icon" AutomationProperties.Name="Close"
Command="{Binding CancelConnectPasswordCommand}">
<TextBlock Classes="icon" Text="&#xE5CD;" FontSize="22" />
</Button>
</Grid>
<TextBox Classes="field secret" Text="{Binding ConnectPassword}" PlaceholderText="password">
<TextBox.KeyBindings>
<KeyBinding Gesture="Enter" Command="{Binding ConnectCommand}" />
</TextBox.KeyBindings>
</TextBox>
<StackPanel Spacing="6">
<TextBlock Classes="label" Text="PASSWORD" />
<Grid ColumnDefinitions="*,6,Auto">
<TextBox x:Name="PasswordBox" Grid.Column="0" Classes="field secret" Height="48"
Text="{Binding ConnectPassword}" PlaceholderText="the account's password"
AutomationProperties.Name="Password">
<TextBox.KeyBindings>
<KeyBinding Gesture="Enter" Command="{Binding ConnectCommand}" />
</TextBox.KeyBindings>
</TextBox>
<ToggleButton Grid.Column="2" Classes="reveal" Width="48" Height="48"
AutomationProperties.Name="Show the password"
IsChecked="{Binding #PasswordBox.RevealPassword}">
<TextBlock Classes="icon" Text="&#xE8F4;" FontSize="21" />
</ToggleButton>
</Grid>
</StackPanel>
<!--
The tick is the phone's whole answer to storing a password, and on this head it is the only one:
@@ -853,15 +924,15 @@
quietly lives.
-->
<CheckBox IsChecked="{Binding RemembersConnectPassword}" MinHeight="44">
<TextBlock Classes="mono" FontSize="11.5" TextWrapping="Wrap"
<TextBlock FontSize="13.5" TextWrapping="Wrap" Foreground="{StaticResource Text}"
Text="Remember this password for this host" />
</CheckBox>
<Grid ColumnDefinitions="*,8,*">
<Button Grid.Column="0" Classes="primary" Height="44" Content="CONNECT"
Command="{Binding ConnectCommand}" IsEnabled="{Binding !IsBusy}" />
<Button Grid.Column="2" Classes="secondary" Height="44" Content="CANCEL"
<Grid ColumnDefinitions="*,10,*">
<Button Grid.Column="0" Classes="secondary" Height="48" Content="CANCEL"
Command="{Binding CancelConnectPasswordCommand}" />
<Button Grid.Column="2" Classes="primary" Height="48" Content="CONNECT"
Command="{Binding ConnectCommand}" IsEnabled="{Binding !IsBusy}" />
</Grid>
</StackPanel>
@@ -912,7 +983,7 @@
Command="{Binding EditGroupFromHeadingCommand}"
CommandParameter="{Binding GroupSheet}">
<StackPanel Spacing="3" VerticalAlignment="Center">
<TextBlock Classes="mono" FontSize="13.5" FontWeight="SemiBold" Text="Edit" />
<TextBlock FontSize="14.5" FontWeight="SemiBold" Foreground="{StaticResource Text}" Text="Edit" />
<TextBlock Classes="detail" FontSize="10.5"
Text="Its name, where it sits, and the port, user and key its hosts fall back to." />
</StackPanel>
@@ -922,7 +993,7 @@
Command="{Binding MoveGroupFromHeadingCommand}"
CommandParameter="{Binding GroupSheet}">
<StackPanel Spacing="3" VerticalAlignment="Center">
<TextBlock Classes="mono" FontSize="13.5" FontWeight="SemiBold"
<TextBlock FontSize="14.5" FontWeight="SemiBold" Foreground="{StaticResource Text}"
Text="Move to another vault" />
<TextBlock Classes="detail" FontSize="10.5" TextWrapping="Wrap"
Text="Takes the groups inside it and every host filed under them, re-encrypted with the other vault's key." />
@@ -935,7 +1006,7 @@
Command="{Binding DeleteGroupFromHeadingCommand}"
CommandParameter="{Binding GroupSheet}">
<StackPanel Spacing="3" VerticalAlignment="Center">
<TextBlock Classes="mono" FontSize="13.5" FontWeight="SemiBold"
<TextBlock FontSize="14.5" FontWeight="SemiBold"
Foreground="{StaticResource Danger}" Text="Delete" />
<TextBlock Classes="detail" FontSize="10.5" TextWrapping="Wrap"
Text="Asks first, and asks separately whether the hosts filed under it go too." />
@@ -1066,10 +1137,10 @@
<TextBlock Classes="detail" TextWrapping="Wrap"
Text="Added to the keychain as soon as you press ADD, so it stays even if you leave this host without saving." />
<Grid ColumnDefinitions="*,8,*">
<Button Grid.Column="0" Classes="primary" Content="ADD" MinHeight="44"
<Button Grid.Column="2" Classes="primary" Content="ADD" MinHeight="44"
HorizontalAlignment="Stretch" HorizontalContentAlignment="Center"
Command="{Binding AddEditorCredentialCommand}" />
<Button Grid.Column="2" Classes="secondary" Content="CANCEL" MinHeight="44"
<Button Grid.Column="0" Classes="secondary" Content="CANCEL" MinHeight="44"
HorizontalAlignment="Stretch" HorizontalContentAlignment="Center"
Command="{Binding CancelEditorCredentialCommand}" />
</Grid>
@@ -1265,9 +1336,9 @@
IsVisible="{Binding Status, Converter={x:Static StringConverters.IsNotNullOrEmpty}}" />
<Grid ColumnDefinitions="*,8,*" Margin="0,6,0,0">
<Button Grid.Column="0" Classes="primary" Height="44" Content="SAVE"
<Button Grid.Column="2" Classes="primary" Height="44" Content="SAVE"
Command="{Binding SaveHostCommand}" />
<Button Grid.Column="2" Classes="secondary" Height="44" Content="CANCEL"
<Button Grid.Column="0" Classes="secondary" Height="44" Content="CANCEL"
Command="{Binding CancelEditCommand}" />
</Grid>
</StackPanel>