Merge branch 'claude/host-connection-top-bar-25d04e'
ci / build and test (push) Successful in 1m36s
ci / android head (push) Failing after 5s
ci / api image (push) Successful in 27s

This commit is contained in:
2026-08-03 14:56:12 +02:00
7 changed files with 62 additions and 27 deletions
@@ -30,7 +30,7 @@
The arithmetic is why. Header 56, strip 46, bar 64, and the terminal's own two rows on top of that: at
360dp the shell was framed by about a third of the display, all of it about somewhere the user was not.
What takes their place is one 52-pixel bar drawn by the surface itself, carrying back on the left and
What takes their place is one 35-pixel bar drawn by the surface itself, carrying back on the left and
the sessions and a + across from it. See TerminalScreen.axaml.
-->
@@ -40,8 +40,12 @@
Both are outside the ScrollViewer deliberately. They are the way out of this surface and the way to
another host, and a tenth tab must not be able to push either of them off the right-hand edge.
35 tall, a third off the 52 it opened at. Every height inside it came down with it — the icon squares
to 34, the pills to 30 — because a bar that shrank around controls that did not would only have moved
the clipping somewhere harder to see.
-->
<Border Grid.Row="0" Height="52" Background="{StaticResource Chrome}"
<Border Grid.Row="0" Height="35" Background="{StaticResource Chrome}"
BorderBrush="{StaticResource Border}" BorderThickness="0,0,0,1">
<Grid ColumnDefinitions="Auto,*,Auto">
@@ -49,8 +53,12 @@
Back, and it goes to the page this terminal was opened over rather than to Hosts by name. The
system back gesture already does exactly that — see PhoneShell.axaml.cs — and an arrow that
landed somewhere else would be the second of two answers to one question.
Height overridden and width left alone. Button.icon is a 44 square, which is taller than this bar;
the 44 that matters is the horizontal one, since nothing in a row of this shape is hard to hit
above or below.
-->
<Button Grid.Column="0" Classes="icon" Content="←" Margin="4,0,0,0"
<Button Grid.Column="0" Classes="icon" Content="←" Height="34" Margin="4,0,0,0"
Command="{Binding ShowScreenCommand}" CommandParameter="{Binding Screen}" />
<!--
@@ -67,16 +75,18 @@
<ItemsControl.ItemTemplate>
<DataTemplate x:DataType="vm:TerminalTabViewModel">
<!--
44 tall, where the session pills on the shell strip are 34. The difference is the close
cross: a pill you only select can be chip-sized, and a pill containing the control that ends
a shell cannot. This head's rule is 44 and this is the one control on the phone that is both
destructive and has no confirmation and no undo — see CloseTabAsync, which ends the session
the moment it is pressed.
30 tall, in a bar of 35. It was 44, and the argument for 44 was that a pill you only
select can be chip-sized while one containing the control that ends a shell cannot — the
close cross is the one control on this head that is both destructive and has neither
confirmation nor undo. That argument is now carried by width rather than by height: the
cross keeps its full 44-pixel column, and what it lost is 14 pixels of vertical slack in a
row where nothing sits above or below it to be hit by mistake.
-->
<Border Background="{StaticResource Panel}" BorderBrush="{StaticResource BorderMid}"
BorderThickness="1" CornerRadius="11" Height="44">
BorderThickness="1" CornerRadius="9" Height="30">
<StackPanel Orientation="Horizontal">
<Button Classes="row" MinHeight="42" Padding="13,0" CornerRadius="11"
<Button Classes="row" MinHeight="28" Padding="11,0" CornerRadius="9"
VerticalContentAlignment="Center"
Command="{Binding $parent[views:TerminalScreen].((vm:MainWindowViewModel)DataContext).SelectTabCommand}"
CommandParameter="{Binding}">
<StackPanel Orientation="Horizontal" Spacing="7" VerticalAlignment="Center">
@@ -95,13 +105,21 @@
The hairline down its left edge is not decoration. The two targets are flush inside one
pill, so without a visible seam there is nothing telling a thumb where "switch to this
shell" stops and "end it" starts.
Both alignments are stated, and the vertical one is not decoration either: Button.row
sets HorizontalContentAlignment and says nothing about the other axis, so the cross
was sitting against the top of its own column rather than in the middle of the pill.
It reads as a misprint, which for the control that ends a session is the wrong thing
to look like.
-->
<Button Classes="row" MinHeight="42" Width="44" Padding="0" CornerRadius="0,11,11,0"
HorizontalContentAlignment="Center"
<Button Classes="row" MinHeight="28" Width="44" Padding="0" CornerRadius="0,9,9,0"
HorizontalContentAlignment="Center" VerticalContentAlignment="Center"
BorderBrush="{StaticResource BorderMid}" BorderThickness="1,0,0,0"
Command="{Binding $parent[views:TerminalScreen].((vm:MainWindowViewModel)DataContext).CloseTabCommand}"
CommandParameter="{Binding}">
<TextBlock Text="×" Foreground="{StaticResource TextFaint}" FontSize="14" />
<TextBlock Text="×" Foreground="{StaticResource TextFaint}" FontSize="14"
HorizontalAlignment="Center" VerticalAlignment="Center"
TextAlignment="Center" />
</Button>
</StackPanel>
</Border>
@@ -121,8 +139,13 @@
not a claim to make without a screenshot. It is answered here rather than dodged — this is a sheet
at the bottom of the screen and opening it collapses the renderer outright, exactly as the
palette does on the desktop. Nothing is drawn over the WebView.
Plain Button.icon, the same as the arrow across from it, rather than the accent variant. The two
are a matched pair at either end of one bar — one leaves this surface, one adds to it — and an
accented + would rank itself above the way out. The accent fill belongs to the floating + on
HOSTS, which is the only action on its screen; this one is not.
-->
<Button Grid.Column="2" Classes="icon accent" Content="+" Margin="0,0,4,0"
<Button Grid.Column="2" Classes="icon" Content="+" Height="34" Margin="0,0,4,0"
Command="{Binding OpenConnectSheetCommand}" />
</Grid>
</Border>
@@ -192,7 +215,16 @@
then release, because holding a modifier while typing is not possible one-thumbed.
-->
<!-- Only with a pane to type into: the keys send bytes at a session, and a connecting tab has none. -->
<Border Grid.Row="2" IsVisible="{Binding IsTerminalShowing}" Height="50">
<!--
33 tall rather than 50, a third off it like the bar at the top, with the keys inside coming down from
38 to 30 for the reason the pills did: a row that shrank around its own contents would clip them.
The five pixels above it are a gap and not a border. The renderer is a native child view, so nothing
Avalonia draws can sit on top of it — a hairline between the two would have to be a row of its own —
and a terminal whose last line of output is flush against a row of grey keys reads as one surface
that has gone wrong rather than as two that are different things.
-->
<Border Grid.Row="2" IsVisible="{Binding IsTerminalShowing}" Height="33" Margin="0,5,0,0">
<Grid ColumnDefinitions="*,Auto">
<ScrollViewer Grid.Column="0" HorizontalScrollBarVisibility="Auto"
@@ -221,11 +253,11 @@
-->
<StackPanel Grid.Column="1" Orientation="Horizontal" Spacing="5" Margin="8,0,12,0"
VerticalAlignment="Center">
<Border Width="1" Height="26" Background="{StaticResource Border}" Margin="0,0,3,0"
<Border Width="1" Height="18" Background="{StaticResource Border}" Margin="0,0,3,0"
VerticalAlignment="Center" />
<Button Classes="row" MinHeight="38" Height="38" MinWidth="40" Padding="0" CornerRadius="9"
HorizontalContentAlignment="Center"
<Button Classes="row" MinHeight="30" Height="30" MinWidth="40" Padding="0" CornerRadius="9"
HorizontalContentAlignment="Center" VerticalContentAlignment="Center"
Background="{StaticResource Panel}" BorderBrush="{StaticResource BorderMid}"
BorderThickness="1"
Command="{Binding ShrinkTerminalFontCommand}"
@@ -233,8 +265,8 @@
<TextBlock Classes="mono" FontSize="13" Text="A" />
</Button>
<Button Classes="row" MinHeight="38" Height="38" MinWidth="40" Padding="0" CornerRadius="9"
HorizontalContentAlignment="Center"
<Button Classes="row" MinHeight="30" Height="30" MinWidth="40" Padding="0" CornerRadius="9"
HorizontalContentAlignment="Center" VerticalContentAlignment="Center"
Background="{StaticResource Panel}" BorderBrush="{StaticResource BorderMid}"
BorderThickness="1"
Command="{Binding EnlargeTerminalFontCommand}"
@@ -126,10 +126,13 @@ internal sealed partial class TerminalScreen : UserControl
VerticalAlignment = VerticalAlignment.Center,
},
// 44 wide as well as tall. The design draws them flexed across the width, which at 360dp
// with ten keys is 32 pixels each — under every thumb-target guideline there is.
// 44 wide, and that is the number that matters: the design draws these flexed across the
// width, which at 360dp with ten keys is 32 pixels each — under every thumb-target
// guideline there is. The height came down with the row it sits in, from 38 to 30, and it
// costs nothing a width does: the keys are a single row with the terminal above and the
// system's gesture bar below, so there is no neighbour a short press can land on instead.
MinWidth = 44,
Height = 38,
Height = 30,
Padding = new Thickness(10, 0),
CornerRadius = new CornerRadius(9),
Background = Palette("Panel"),