Give hosts and terminals their own screen, and the rest of the vault another
ci / build and test (ubuntu) (pull_request) Canceled after 0s
ci / build (windows) (pull_request) Canceled after 0s

Rebuilds the client's shell from an imported design: a titlebar and nav rail
it draws itself, real multi-session tabs over the one WebView, a Ctrl+K host
search, and a vault screen that merges keys, passwords and pinned host keys
into one table. Hosts left the vault column for their own screen beside the
terminal, which is what the design asks for and turned out to be the better
split anyway.

Two screens the design shows have nothing behind them yet — file transfer
and teams — and say so plainly rather than rendering invented data; every
other gap between the design and this build is recorded in
docs/design-import-gaps.md.
This commit is contained in:
2026-07-31 08:39:37 +02:00
parent d162271a45
commit 9a76eced14
37 changed files with 4672 additions and 1347 deletions
@@ -0,0 +1,51 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="using:DodoSSH.Client.App.ViewModels"
x:Class="DodoSSH.Client.App.Views.StatusBar"
x:DataType="vm:MainWindowViewModel">
<!--
The strip along the bottom.
The design puts five things here: the active host and the bastion it is reached through, the open port
forwards, the SFTP transfer count, an auto-lock countdown, and the last sync time. Four of those five
have nothing behind them — there is no jump-host routing, no port forwarding, no file transfer and no
idle auto-lock — so what is left is the active session, the vault's last message, and the shortcut hint.
The last message is the load-bearing one, and it is why this bar is worth having at all: the vault's
status line used to be a cramped label in the account bar, and everything the application has to say
about a save, a sync, a refusal or a conflict goes through it.
-->
<Border Height="24" Background="{StaticResource Chrome}"
BorderBrush="{StaticResource Border}" BorderThickness="0,1,0,0">
<Grid ColumnDefinitions="Auto,*,Auto,Auto" Margin="12,0">
<!-- What is connected, and where. Absent rather than empty when nothing is. -->
<StackPanel Grid.Column="0" Orientation="Horizontal" Spacing="5" VerticalAlignment="Center"
Margin="0,0,14,0" IsVisible="{Binding SelectedTab, Converter={x:Static ObjectConverters.IsNotNull}}">
<Ellipse Classes="dot" Classes.live="{Binding SelectedTab.IsLive}" VerticalAlignment="Center" />
<TextBlock Classes="mono" Text="{Binding SelectedTab.Address}" FontSize="9.5"
Foreground="{StaticResource TextDim}" VerticalAlignment="Center" />
</StackPanel>
<!--
Everything the vault has to say. Trimmed rather than wrapped, because this bar is one line high and
a message that grew it would move the whole window's contents up.
-->
<TextBlock Grid.Column="1" Classes="mono" Text="{Binding Vault.Status}" FontSize="9.5"
Foreground="{StaticResource TextDim}" VerticalAlignment="Center"
TextTrimming="CharacterEllipsis" />
<TextBlock Grid.Column="2" Classes="mono" Text="{Binding SyncLabel}" FontSize="9.5"
Foreground="{StaticResource TextFaint}" VerticalAlignment="Center" Margin="14,0,0,0"
IsVisible="{Binding IsUnlocked}" />
<TextBlock Grid.Column="3" Classes="mono" Text="CTRL K SEARCH" FontSize="9.5"
Foreground="{StaticResource TextFaint}" VerticalAlignment="Center" Margin="14,0,0,0"
IsVisible="{Binding IsUnlocked}" />
</Grid>
</Border>
</UserControl>