Give the phone its pins: an editor section and chips on Files

The data was never the gap — HostSecret.PinnedPaths syncs and merges on both
heads, and the desktop's drawer has staged it since v5 — the phone just had
nowhere to add, remove or use a pin. Now it has both halves.

The host editor page gains a QUICK ACCESS section over the same shared
staging the drawer binds (EditorPinnedPaths, AddEditorPin, RemoveEditorPin),
with the remove target at this head's 44dp touch floor rather than the
desktop's 22-pixel close box, and no folder glyph because this head embeds no
icon font for one. The page also gains a Status line of its own: the add
command's five refusals speak through Status, and this page covers the screen
that normally draws it — a refusal nothing shows is no refusal at all.

The Files screen draws the connected host's pins as chips between the
breadcrumb and the listing, each running GoRemoteCommand exactly as a crumb
does. They are captured at connect, like ConnectedTo and the session facts
before them; a bucket gets none, having no HostSecret to pin anything on.
Covered headlessly in ShellFlowTests — connect populates, disconnect clears,
a bucket stays empty — and by manual checks 8.18 and 8.19, whose phase
preamble also stops claiming thirteen checks when it lists twenty-one.
This commit is contained in:
jaap-jan committed 2026-08-08 23:09:57 +02:00
1 parent 242280ce6b
commit dbf6ce1bcf
6 files changed
+329 -5

No files matched your search

@@ -48,7 +48,7 @@
the refusal has no continue button here either.
-->
<Grid RowDefinitions="Auto,Auto,Auto,Auto,*,Auto">
<Grid RowDefinitions="Auto,Auto,Auto,Auto,Auto,*,Auto">
<!-- ============ header ============ -->
<Grid Grid.Row="0" ColumnDefinitions="Auto,Auto,*,Auto" Height="56" Margin="8,0">
@@ -199,8 +199,48 @@
</StackPanel>
</ScrollViewer>
<!-- ============ pins ============ -->
<!--
◆ The phone's answer to the desktop's QUICK ACCESS sidebar — the same PinnedPathList this host was
connected with, drawn where this head actually browses files rather than beside a terminal it has no
strip for. See TransfersViewModel.ConnectedPinnedPaths for why this is a snapshot taken at connect
rather than a live follow of the host row: a pin added or removed mid-session shows up here on the
next connect, not while this one is still open.
Its own row rather than folded into the breadcrumb above: the two scroll independently and mean
different things — the breadcrumb is where this pane is, the chips are places it can jump to — and a
shared row would make ↑ look like one more pin among several.
Gone rather than empty when nothing is pinned or nothing is connected, which HasConnectedPins already
answers: an empty scrolling strip under the breadcrumb would read as a loading row rather than as
"this host has nothing pinned".
-->
<ScrollViewer Grid.Row="4" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Disabled"
IsVisible="{Binding HasConnectedPins}" Margin="0,0,0,4">
<ItemsControl ItemsSource="{Binding ConnectedPinnedPaths}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate><StackPanel Orientation="Horizontal" Spacing="6" Margin="14,0" /></ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate x:DataType="x:String">
<!--
Classes="row" at its default 44dp MinHeight rather than the breadcrumb's own 36 — these chips
are the destination, not the trail behind it, and a target worth a deliberate tap gets the
full touch floor this head holds everything else to. Not trimmed: a chip scrolls sideways with
the row rather than being squeezed to fit it, so the full path is always what a tap commits to.
-->
<Button Classes="row" MinHeight="44" Padding="11,0" CornerRadius="10"
Command="{Binding $parent[views:FilesScreen].((vm:TransfersViewModel)DataContext).GoRemoteCommand}"
CommandParameter="{Binding}">
<TextBlock Classes="mono" FontSize="12" Text="{Binding}" />
</Button>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
<!-- ============ the listing ============ -->
<Panel Grid.Row="4">
<Panel Grid.Row="5">
<TextBlock Classes="body" IsVisible="{Binding !IsConnected}" Margin="24,12"
VerticalAlignment="Top" Text="{Binding Status}" />
@@ -266,7 +306,7 @@
<!-- ============ what to do with the chosen entry ============ -->
<!-- DeepChrome rather than Chrome, since v5 — see the remark on PhoneShell's vault header. -->
<Border Grid.Row="5" IsVisible="{Binding IsConnected}" Background="{StaticResource DeepChrome}"
<Border Grid.Row="6" IsVisible="{Binding IsConnected}" Background="{StaticResource DeepChrome}"
BorderBrush="{StaticResource Border}" BorderThickness="0,1,0,0" Padding="12,10">
<StackPanel Spacing="9">