Public Access
The phone's Connections screen no longer draws the user@host + password box, and the desktop never did, so nothing bound VaultViewModel.ManualTarget, ManualPassword, ManualStatus or ConnectManuallyCommand any more, nor MainWindowViewModel.ConnectToRecentCommand, whose address branch wrote into that box. They are gone, and so is VaultViewModel.AskAboutHost, whose one caller was ConnectToRecent. TryParseManualTarget, ManualEndpoint and ConnectToEndpointAsync stay: the prompt, the desktop's Ctrl+K palette and the phone's hosts search all use them. The reasoning the removed members carried moved onto ConnectToEndpointAsync and ChoosePromptRecentAsync. The tests whose subject is still live now go through the prompt instead of the box: a typed address dialled exactly as typed, port 22 by default, the refusal sentences, the host-key retry replaying the attempt that raised it, the host-key card staying over the surface it was typed on, the session facts for a typed password, and no forwards for a typed address. The recent-row cases now drive ChoosePromptRecentCommand. Two cases existing prompt tests already covered were deleted. Manual check 11.8 now describes recent rows landing on the prompt.
210 lines
13 KiB
XML
210 lines
13 KiB
XML
<UserControl xmlns="https://github.com/avaloniaui"
|
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
|
xmlns:vm="using:DodoSSH.Client.Shell.ViewModels"
|
|
x:Class="DodoSSH.Client.App.Views.QuickConnect"
|
|
x:DataType="vm:MainWindowViewModel">
|
|
|
|
<!--
|
|
Ctrl+K: type a few characters of a host's name and press Enter to connect to it — or type user@host, press
|
|
Enter, and give a password, to connect to a machine that is not saved at all.
|
|
|
|
The design's box promises "search hosts · run command". Only the first half is here, because there is no
|
|
snippet or saved-command item type in the vault for the second half to run — see
|
|
docs/design-import-gaps.md.
|
|
|
|
It connects through the vault's own ConnectCommand rather than opening a session itself, so it inherits
|
|
every refusal that path already makes: a key or credential that has been deleted on another machine, a
|
|
host with no username, a host key that has changed. A second connect path would be a second place for
|
|
those to be forgotten.
|
|
|
|
While this is open the shell collapses the terminal's WebView, and that is not a visual nicety — the
|
|
WebView is a native child window that composites above anything Avalonia draws in the same rectangle, so
|
|
an overlay across the middle of this window would be painted underneath it and take no clicks.
|
|
-->
|
|
|
|
<!--
|
|
The wash is named and takes a press, because clicking away from a palette is how every palette closes.
|
|
The handler answers only for presses whose source is the wash itself, which is what separates "outside"
|
|
from "inside": a press anywhere on the card below reports that control as its source and bubbles through
|
|
here on its way to the window.
|
|
-->
|
|
<!-- 60% of Canvas. Written out because a scrim is a brush with an alpha, and the palette holds no alpha
|
|
variant of a surface — see Palette.axaml, where the only pre-multiplied brushes are the accent washes. -->
|
|
<Border x:Name="Backdrop" Background="#9905050A" PointerPressed="OnBackdropPressed">
|
|
<Border Width="640" VerticalAlignment="Top" Margin="0,160,0,0"
|
|
Background="{StaticResource Chrome}" BorderBrush="{StaticResource BorderMid}"
|
|
BorderThickness="1" CornerRadius="14">
|
|
<StackPanel>
|
|
|
|
<StackPanel x:Name="QueryStep" IsVisible="{Binding IsAskingForSearchQuery}">
|
|
|
|
<!--
|
|
">_" rather than the bare ">" v4 drew: the mock's own prompt glyph, and the underscore is what
|
|
reads as a cursor waiting for the first keystroke rather than a stray angle bracket. There is
|
|
still no drawn caret in the field beside it, for the reason TextBox.address in this file gives —
|
|
it already has a real one.
|
|
-->
|
|
<Border Padding="16,14" BorderBrush="{StaticResource BorderSubtle}" BorderThickness="0,0,0,1">
|
|
<Grid ColumnDefinitions="Auto,*">
|
|
<TextBlock Grid.Column="0" Classes="mono" Text=">_" FontSize="14" FontWeight="Bold"
|
|
Foreground="{StaticResource Accent}" VerticalAlignment="Center" />
|
|
<TextBox Grid.Column="1" x:Name="Query" Text="{Binding SearchText}"
|
|
PlaceholderText="search hosts, or type user@host" Margin="10,0,0,0"
|
|
FontFamily="{StaticResource MonoFont}" FontSize="14"
|
|
Background="Transparent" BorderThickness="0" />
|
|
</Grid>
|
|
</Border>
|
|
|
|
<!--
|
|
The typed address, above the saved hosts: what was typed was an address, so the row that dials it
|
|
is the one the query is most plainly about. It is lit by IsAdHocSelected rather than by the list's
|
|
own selection, because it is not a host and the list below holds hosts only — see the remark on
|
|
MainWindowViewModel.IsAdHocSelected. Classes="qcrow adhoc" shares the list rows' look; the
|
|
selected state is a class here because there is no ListBoxItem around it to carry :selected.
|
|
|
|
"not saved" in the corner, where a saved row says how it authenticates: that is this row's answer
|
|
to the same question, and the one thing worth knowing before choosing it.
|
|
-->
|
|
<Border x:Name="AdHocRow" Classes="qcrow adhoc" Classes.selected="{Binding IsAdHocSelected}"
|
|
Height="44" CornerRadius="9" Padding="10,0" Margin="8,6,8,2"
|
|
IsVisible="{Binding HasAdHocTarget}" Cursor="Hand">
|
|
<Grid ColumnDefinitions="Auto,*,Auto">
|
|
<TextBlock Grid.Column="0" Classes="mono qcrow-text" Text=">_" FontSize="12"
|
|
FontWeight="Bold" VerticalAlignment="Center" />
|
|
<StackPanel Grid.Column="1" Spacing="1" Margin="10,0,0,0" VerticalAlignment="Center">
|
|
<TextBlock Classes="mono qcrow-text" Text="{Binding AdHocAddress}" FontSize="13"
|
|
FontWeight="Medium" TextTrimming="CharacterEllipsis" />
|
|
<TextBlock Classes="mono qcrow-subtext" Text="connect with a password" FontSize="11.5" />
|
|
</StackPanel>
|
|
<TextBlock Grid.Column="2" Classes="mono qcrow-subtext" Text="not saved" FontSize="11"
|
|
Margin="10,0,0,0" VerticalAlignment="Center" />
|
|
</Grid>
|
|
</Border>
|
|
|
|
<!-- Why a query that looks like an address has no row to dial it — a missing port number, say. -->
|
|
<TextBlock Classes="hint" FontSize="12" Margin="18,12,18,4"
|
|
Foreground="{StaticResource WarnText}"
|
|
Text="{Binding AdHocRefusal}"
|
|
IsVisible="{Binding HasAdHocRefusal}" />
|
|
|
|
<!--
|
|
Arrow keys move the selection and Enter takes it; the code-behind owns both, because a ListBox that
|
|
took focus would take the arrow keys away from the box being typed into.
|
|
|
|
A click connects rather than merely selecting, which is what a list of hosts under a search box
|
|
looks like it does — the keyboard route already treats choosing a row and connecting to it as one
|
|
act, and a pointer that only highlighted would leave the palette open over a choice already made.
|
|
The gesture is wired in the code-behind, as the sidebar's double-click is.
|
|
|
|
Classes="tiles" clears the theme's full-bleed hover and selection wash the way the hosts grid's own
|
|
cards do — see the remark on Border.qcrow in App.axaml, which is what paints every state here
|
|
instead. Rows rather than cards, but the same reason applies: a square wash behind a rounded shape
|
|
shows its corners.
|
|
-->
|
|
<ListBox x:Name="Results" Classes="tiles" MaxHeight="320" Focusable="False"
|
|
ItemsSource="{Binding SearchResults}"
|
|
SelectedItem="{Binding SelectedSearchResult}">
|
|
<ListBox.ItemTemplate>
|
|
<DataTemplate x:DataType="vm:HostRowViewModel">
|
|
<Border Classes="qcrow" Height="44" CornerRadius="9" Padding="10,0" Margin="8,2">
|
|
<Grid ColumnDefinitions="Auto,*,Auto">
|
|
<!--
|
|
Green when this host has a terminal open right now, grey otherwise — the same two-state
|
|
dot the hosts grid draws, and the same reason: nothing here pings a machine, so a third
|
|
colour would be a claim this application never checks. See Ellipse.dot in App.axaml.
|
|
-->
|
|
<Ellipse Grid.Column="0" Classes="dot" Classes.live="{Binding IsConnected}"
|
|
Width="8" Height="8" VerticalAlignment="Center" />
|
|
<StackPanel Grid.Column="1" Spacing="1" Margin="10,0,0,0" VerticalAlignment="Center">
|
|
<TextBlock Classes="mono qcrow-text" Text="{Binding Label}" FontSize="13"
|
|
FontWeight="Medium" TextTrimming="CharacterEllipsis" />
|
|
<TextBlock Classes="mono qcrow-subtext" Text="{Binding Address}" FontSize="11.5"
|
|
TextTrimming="CharacterEllipsis" />
|
|
</StackPanel>
|
|
<!--
|
|
Two readings stacked against the two on the left, and in the same order: what this row
|
|
IS above, how it is reached below.
|
|
|
|
◆ THE VAULT, and only when there is more than one to be in — HostRowViewModel.VaultBadge
|
|
is empty otherwise, which collapses the line and leaves the kind word centred as it was.
|
|
The palette searches every readable vault at once, so without it two hosts a team and a
|
|
person both call "prod-db" are two identical rows, and Enter takes whichever the ranking
|
|
happened to put first. Trimmed against a cap because the column is Auto-sized: a long
|
|
vault name would otherwise take its width out of the host name beside it.
|
|
|
|
credential / key / password — never the mock's SSH/SFTP kind column. Every palette
|
|
connect here is SSH, so printing that word would be a constant dressed up as a reading;
|
|
see hosts-v5-design-spec.md's deviations and HostRowViewModel.Authentication.
|
|
-->
|
|
<StackPanel Grid.Column="2" Spacing="1" Margin="10,0,0,0" VerticalAlignment="Center">
|
|
<TextBlock Classes="mono qcrow-subtext" Text="{Binding VaultBadge}" FontSize="10"
|
|
HorizontalAlignment="Right" MaxWidth="150"
|
|
TextTrimming="CharacterEllipsis" IsVisible="{Binding HasVaultBadge}" />
|
|
<TextBlock Classes="mono qcrow-subtext" Text="{Binding Authentication}"
|
|
FontSize="11" HorizontalAlignment="Right" />
|
|
</StackPanel>
|
|
</Grid>
|
|
</Border>
|
|
</DataTemplate>
|
|
</ListBox.ItemTemplate>
|
|
</ListBox>
|
|
|
|
<!--
|
|
Names the other thing the box takes, at the moment somebody is most likely to want it: a name that
|
|
matches nothing is often a machine that was never saved.
|
|
-->
|
|
<TextBlock Classes="hint" FontSize="12" Margin="18,16"
|
|
Text="No host matches that. Type user@host to connect without saving it."
|
|
IsVisible="{Binding HasNothingToOffer}" />
|
|
|
|
<Border Padding="16,10" BorderBrush="{StaticResource BorderSubtle}" BorderThickness="0,1,0,0">
|
|
<TextBlock Classes="mono" FontSize="10" Foreground="{StaticResource TextFaint}"
|
|
Text="↑ ↓ to choose · ENTER or click to connect · ESC to close" />
|
|
</Border>
|
|
|
|
</StackPanel>
|
|
|
|
<!--
|
|
============ the typed address's password ============
|
|
|
|
The second step, in the same card, so the keyboard never leaves the palette: Enter on the address
|
|
comes here, Enter here dials, Escape goes back to the address rather than closing. The box carries
|
|
PasswordChar and nothing is written anywhere — see MainWindowViewModel.IsAskingForAdHocPassword,
|
|
and VaultViewModel.ConnectToEndpointAsync for why a typed password is the only way in.
|
|
-->
|
|
<StackPanel x:Name="PasswordStep" IsVisible="{Binding IsAskingForAdHocPassword}">
|
|
|
|
<Border Padding="16,14" BorderBrush="{StaticResource BorderSubtle}" BorderThickness="0,0,0,1">
|
|
<Grid ColumnDefinitions="Auto,*">
|
|
<TextBlock Grid.Column="0" Classes="mono" Text=">_" FontSize="14" FontWeight="Bold"
|
|
Foreground="{StaticResource Accent}" VerticalAlignment="Center" />
|
|
<TextBlock Grid.Column="1" Classes="mono" Text="{Binding AdHocAddress}" Margin="10,0,0,0"
|
|
FontSize="14" Foreground="{StaticResource Text}" VerticalAlignment="Center"
|
|
TextTrimming="CharacterEllipsis" />
|
|
</Grid>
|
|
</Border>
|
|
|
|
<StackPanel Margin="16,14" Spacing="8">
|
|
<TextBox x:Name="Password" Text="{Binding AdHocPassword}" PasswordChar="•"
|
|
PlaceholderText="password" FontSize="14" Height="38" />
|
|
|
|
<TextBlock Classes="hint" FontSize="12" Foreground="{StaticResource WarnText}"
|
|
Text="{Binding AdHocRefusal}" IsVisible="{Binding HasAdHocRefusal}" />
|
|
|
|
<TextBlock Classes="hint" FontSize="12"
|
|
Text="Nothing here is saved. A machine worth keeping belongs on HOSTS, where it can carry a key." />
|
|
</StackPanel>
|
|
|
|
<Border Padding="16,10" BorderBrush="{StaticResource BorderSubtle}" BorderThickness="0,1,0,0">
|
|
<TextBlock Classes="mono" FontSize="10" Foreground="{StaticResource TextFaint}"
|
|
Text="ENTER to connect · ESC to go back" />
|
|
</Border>
|
|
|
|
</StackPanel>
|
|
|
|
</StackPanel>
|
|
</Border>
|
|
</Border>
|
|
|
|
</UserControl>
|