Give the window its v5b chrome and each session surface its own shell

This commit is contained in:
2026-08-08 00:49:59 +02:00
parent 1b76c51fbb
commit 43c939b697
30 changed files with 4433 additions and 1772 deletions
+315 -113
View File
@@ -305,6 +305,22 @@
<Setter Property="Background" Value="{StaticResource ChromeHover}" />
</Style>
<!--
The titlebar's search pill. A plain <c>Border</c> rather than a styled <c>Button</c> template, because
the pill sits inside a <c>Button.flat</c> that already owns the click — see <c>TitleBar.axaml</c> — and
what changes under the pointer is the pill's own border, not a fill behind it. Declared on the parent
button's <c>:pointerover</c> rather than the border's own, because a <c>Border</c> has no pointer state
of its own to key a selector on.
-->
<Style Selector="Border.searchpill">
<Setter Property="Background" Value="{StaticResource SearchPill}" />
<Setter Property="BorderBrush" Value="{StaticResource BorderMid}" />
<Setter Property="BorderThickness" Value="1" />
</Style>
<Style Selector="Button.search:pointerover Border.searchpill">
<Setter Property="BorderBrush" Value="{StaticResource Accent}" />
</Style>
<!--
Button.grouphead was here: the padding on the fold-away group heading that used to sit between the host
cards. The headings went when the grid became cards and the group cards above it became the thing that
@@ -331,178 +347,301 @@
<Setter Property="Foreground" Value="{StaticResource Text}" />
</Style>
<!--
The nav rail. The active destination is marked with an accent bar down its left edge and a wash
behind it, which is the design's whole idiom for "you are here" — the same two marks a selected list
row carries, so the window has one vocabulary for selection rather than one per control.
-->
<!--
A destination in the sidebar. v2 turns these from 54-pixel stacked labels into 32-pixel rows with a
glyph, a word and a count, and the active one becomes a filled rounded row rather than a label with an
accent bar down its left edge. The bar is gone because a filled row at this width already reads as
chosen, and the bar was carrying that on its own when there was no room for a fill.
The nav rail. v5b redraws it against TitleBar.dc.html's sibling NavRail.dc.html rather than against
the v2 mock this replaces — see the comment block at the top of NavRail.axaml for what moved and why.
The active destination is now a filled accent row rather than a wash with a bar down its edge; the
idiom is otherwise the same one the switcher below and a selected list row already use.
TextGhost rather than TextFaint for the resting label: the mock computes rgb(124,127,152) for an
inactive row, which is TextGhost's own #7C7F98 exactly, not TextFaint's paler #9C9EB4.
-->
<Style Selector="Button.nav">
<Setter Property="Height" Value="32" />
<Setter Property="Padding" Value="10,0" />
<Setter Property="Height" Value="35" />
<Setter Property="Padding" Value="11,8" />
<Setter Property="HorizontalAlignment" Value="Stretch" />
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
<Setter Property="HorizontalContentAlignment" Value="Left" />
<Setter Property="VerticalContentAlignment" Value="Center" />
<Setter Property="CornerRadius" Value="8" />
<Setter Property="FontSize" Value="13" />
<Setter Property="FontSize" Value="10.5" />
<Setter Property="LetterSpacing" Value="0.1" />
<Setter Property="FontWeight" Value="Medium" />
<Setter Property="Foreground" Value="{StaticResource TextDim}" />
<Setter Property="Foreground" Value="{StaticResource TextGhost}" />
</Style>
<Style Selector="Button.nav /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="Foreground" Value="{StaticResource TextDim}" />
<Setter Property="Foreground" Value="{StaticResource TextGhost}" />
<Setter Property="BorderThickness" Value="0" />
<Setter Property="CornerRadius" Value="8" />
</Style>
<Style Selector="Button.nav:pointerover /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="Foreground" Value="{StaticResource Text}" />
<Setter Property="Background" Value="{StaticResource Hover}" />
<Setter Property="Background" Value="{StaticResource Track}" />
</Style>
<Style Selector="Button.nav.active">
<Setter Property="FontWeight" Value="SemiBold" />
</Style>
<Style Selector="Button.nav.active /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="Foreground" Value="{StaticResource AccentText}" />
<Setter Property="Background" Value="{StaticResource Active}" />
<Setter Property="Foreground" Value="{StaticResource Text}" />
<Setter Property="Background" Value="{StaticResource Accent}" />
</Style>
<!--
The three parts of a sidebar row. Separate classes rather than inline setters because nine rows draw
them and the day one of the three moves is the day eight of them would not have.
The glyph and the label inherit the row's own foreground, so they light with it; the count does not,
because a number that lit with its row would compete with the word beside it for the same emphasis.
The two parts of a sidebar row: a 19-pixel Material glyph and its word, ten pixels apart. v2's third
column — a count read off the vault — left with the row it decorated; see the remark on
<c>Button.nav</c> above for where that number went and why it is not replaced here.
-->
<Style Selector="TextBlock.navicon">
<Setter Property="FontSize" Value="14" />
<Setter Property="Width" Value="20" />
<Setter Property="FontFamily" Value="{StaticResource IconFont}" />
<Setter Property="FontSize" Value="19" />
<Setter Property="VerticalAlignment" Value="Center" />
</Style>
<Style Selector="TextBlock.navlabel">
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="TextTrimming" Value="CharacterEllipsis" />
</Style>
<Style Selector="TextBlock.navcount">
<Setter Property="FontFamily" Value="{StaticResource MonoFont}" />
<Setter Property="FontSize" Value="11" />
<Setter Property="Foreground" Value="{StaticResource TextFaint}" />
<Setter Property="VerticalAlignment" Value="Center" />
</Style>
<!--
A terminal tab, and since v2 a pill rather than a filing-cabinet tab: 30 tall inside a 42 strip, its
own rounded outline, and the active one filled instead of marked along an edge. The edge mark is gone
because a pill has no edge to share with its neighbour — the gap between two of them is the divider
the old top-and-right border was standing in for.
The segmented SSH / SFTP / S3 switcher at the rail's head. A track the width of the rail's own content
column, 2 pixels of padding holding three equal segments — so the three are Buttons with
HorizontalAlignment="Stretch" inside a Grid of three equal columns, the same "buttons carry no state"
reasoning <c>NavRail.axaml</c>'s own remarks give for every row beneath them.
-->
<Style Selector="Button.tab">
<!-- Less on the right than the left: the close box lives inside the tab and brings its own margin. -->
<Setter Property="Padding" Value="12,0,8,0" />
<Setter Property="Height" Value="30" />
<Setter Property="Margin" Value="0,0,6,0" />
<Setter Property="VerticalAlignment" Value="Center" />
<Style Selector="Border.navtrack">
<Setter Property="Height" Value="31" />
<Setter Property="CornerRadius" Value="8" />
<Setter Property="FontSize" Value="13" />
<Setter Property="FontWeight" Value="Medium" />
<Setter Property="Foreground" Value="{StaticResource TextDim}" />
<Setter Property="Background" Value="{StaticResource Track}" />
<Setter Property="Padding" Value="2" />
</Style>
<!--
◆ ORDER IS THE BEHAVIOUR HERE. The base pill comes first, then its hover, then the exceptions — and
that ordering is the fix for a bug this arrangement had when v2 first drew it.
A pill paints its own Background, which the flat tab it replaced did not. That one detail moves where
the hover has to live: `Button.flat:pointerover` is declared far above and used to supply it, and the
moment `.tab`'s template rule set a Background of its own, the later declaration won and every tab in
the strip lost its pointer feedback silently. The `+` lost more than that — it sits below as an
exception, so a `.tab` rule declared after it was overriding the very thing that made it an exception,
and it drew as a filled outlined pill contradicting the comment above it.
Avalonia has no specificity; the later declaration wins. The same trap is recorded further down this
file for Border.rowmark. Anything added below must be an exception to what is above it, never a
restatement of the base.
-->
<Style Selector="Button.tab /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="Foreground" Value="{StaticResource TextDim}" />
<Setter Property="Background" Value="{StaticResource Panel}" />
<Setter Property="BorderBrush" Value="{StaticResource Border}" />
<Setter Property="BorderThickness" Value="1" />
<Setter Property="CornerRadius" Value="8" />
<Style Selector="Button.navseg">
<Setter Property="HorizontalAlignment" Value="Stretch" />
<Setter Property="HorizontalContentAlignment" Value="Center" />
<Setter Property="VerticalContentAlignment" Value="Center" />
<Setter Property="Height" Value="27" />
<Setter Property="CornerRadius" Value="7" />
<Setter Property="FontSize" Value="10.5" />
<Setter Property="FontWeight" Value="Bold" />
<Setter Property="LetterSpacing" Value="0.1" />
<Setter Property="Foreground" Value="{StaticResource TextGhost}" />
</Style>
<Style Selector="Button.tab:pointerover /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="Background" Value="{StaticResource Hover}" />
<Style Selector="Button.navseg /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="Foreground" Value="{StaticResource TextGhost}" />
<Setter Property="Background" Value="Transparent" />
<Setter Property="BorderThickness" Value="0" />
<Setter Property="CornerRadius" Value="7" />
</Style>
<Style Selector="Button.navseg.active /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="Foreground" Value="{StaticResource Text}" />
<Setter Property="Background" Value="{StaticResource Accent}" />
</Style>
<!--
One of the strip's three fixed tabs — Vaults, SFTP, S3. A pill in every respect except that it has no
close box, so it takes its padding back on the right: the base rule is short there to leave room for
the cross a terminal tab carries inside itself, and a fixed tab with the same asymmetry sits visibly
off-centre beside one that has a reason for it.
Nothing else differs, deliberately. These are tabs and have to read as tabs — the whole point of the
strip is that "where the window is" is one row of one kind of control.
The rail's foot: the user chip that opens the popover. Flat until the pointer arrives, the same idiom
every other row in this rail follows, at its own height and radius because it is a chip rather than a
destination — see <c>NavRail.axaml</c> for what it opens and why a <c>Flyout</c> is safe here.
-->
<Style Selector="Button.tab.fixed">
<Setter Property="Padding" Value="12,0" />
<Style Selector="Button.navuser">
<Setter Property="Height" Value="34" />
<Setter Property="Padding" Value="13,7" />
<Setter Property="HorizontalAlignment" Value="Stretch" />
<Setter Property="HorizontalContentAlignment" Value="Left" />
<Setter Property="CornerRadius" Value="12" />
</Style>
<Style Selector="Button.navuser /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="BorderThickness" Value="0" />
<Setter Property="CornerRadius" Value="12" />
</Style>
<Style Selector="Button.navuser:pointerover /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="Background" Value="{StaticResource Track}" />
</Style>
<!--
The button that opens a connection. A tab in every respect but the marks a tab carries: no active
A row inside the user popover: a vault switch, "New vault", Settings, Preferences, Vaults, Logout. All
six share one shape — flat, a track fill under the pointer, 8 pixels of rounding — because the popover
draws them as one list and a row that looked different from its neighbours would read as a separator
that is not one.
-->
<Style Selector="Button.poprow">
<Setter Property="HorizontalAlignment" Value="Stretch" />
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
<Setter Property="Padding" Value="11,4" />
<Setter Property="CornerRadius" Value="8" />
<Setter Property="MinHeight" Value="20" />
</Style>
<Style Selector="Button.poprow /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="BorderThickness" Value="0" />
<Setter Property="CornerRadius" Value="8" />
</Style>
<Style Selector="Button.poprow:pointerover /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="Background" Value="{StaticResource Track}" />
</Style>
<!--
The check square beside a shown vault in the popover — magenta rather than the accent, because the
accent already means "press this" everywhere else in the window and a vault switch is a fact, not an
action waiting to be taken. See Palette.axaml's remark on Magenta.
-->
<Style Selector="Border.vaultcheck">
<Setter Property="Width" Value="12" />
<Setter Property="Height" Value="12" />
<Setter Property="CornerRadius" Value="3" />
<Setter Property="Background" Value="{StaticResource Magenta}" />
<Setter Property="HorizontalAlignment" Value="Center" />
<Setter Property="VerticalAlignment" Value="Center" />
</Style>
<!--
── v5b: SESSION TAB ROW ─────────────────────────────────────────────────────────────────────────────
The tab strip's replacement, per <c>Terminal.dc.html</c>/<c>SFTP.dc.html</c>: rounded only at the top,
fused visually to the bordered container it sits above rather than a free-floating pill — see
<c>SessionTabRow.axaml</c>. It went from a 42-pixel strip spanning the window to a 38-pixel row inside
each of the two screens that carry one, which is why the layout budget in <c>LayoutHarness</c> no
longer subtracts a tab strip's height from every screen: only these two now pay it, out of their own
26-pixel padded column rather than out of the window's own chrome.
◆ ORDER IS THE BEHAVIOUR HERE, as it was for the pill this replaces. The base tab comes first, then its
hover, then <c>.active</c> — each later rule is an exception to what came before it, because Avalonia
has no specificity and settles two rules matching one element by declaration order alone.
-->
<Style Selector="Button.sesstab">
<Setter Property="Padding" Value="16,10,12,10" />
<Setter Property="Height" Value="38" />
<Setter Property="MinHeight" Value="0" />
<Setter Property="MinWidth" Value="0" />
<Setter Property="VerticalAlignment" Value="Bottom" />
<Setter Property="VerticalContentAlignment" Value="Center" />
<Setter Property="CornerRadius" Value="9,9,0,0" />
<Setter Property="FontFamily" Value="{StaticResource MonoFont}" />
<Setter Property="FontSize" Value="13.5" />
<Setter Property="FontWeight" Value="Medium" />
<Setter Property="Foreground" Value="{StaticResource TextFaint}" />
</Style>
<Style Selector="Button.sesstab /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="Background" Value="Transparent" />
<Setter Property="BorderThickness" Value="0" />
<Setter Property="CornerRadius" Value="9,9,0,0" />
</Style>
<Style Selector="Button.sesstab:pointerover /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="Background" Value="{StaticResource Track}" />
</Style>
<!--
The tab whose pane the container below is showing: DeepChrome body — the same value the container's
own host header and status bar are painted in, which is what fuses the two into one shape rather than
a tab floating above a separate box — and a 2px top border in one of two colours. Which colour is set
by the row's own <c>Classes="sftp"</c>, from the usage site in <c>MainWindow.axaml</c>: unmarked is the
terminal row's <c>TerminalTabAccent</c>, and <c>.sftp</c> is the SFTP row's <c>Magenta</c> — see the
remark on both keys in <c>Palette.axaml</c>. Marked on <c>TerminalTabViewModel.IsSelected</c> rather
than <c>IsShowing</c>, unlike the strip this replaces: the SFTP row's own active tab is not "the pane
the terminal surface is showing" at all, so the narrower flag would leave it permanently dark. See
<c>MainWindowViewModel.SelectFilesHostCommand</c>.
-->
<Style Selector="Button.sesstab.active">
<Setter Property="Foreground" Value="{StaticResource Text}" />
<Setter Property="FontWeight" Value="Bold" />
</Style>
<Style Selector="Button.sesstab.active /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="Background" Value="{StaticResource DeepChrome}" />
<Setter Property="BorderThickness" Value="0,2,0,0" />
<Setter Property="BorderBrush" Value="{StaticResource TerminalTabAccent}" />
</Style>
<Style Selector=".sftp Button.sesstab.active /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="BorderBrush" Value="{StaticResource Magenta}" />
</Style>
<!--
The button that opens a connection, reusing the strip's own shape rather than a new one: no active
state, because it is never the thing showing, and no outline, because it is not one of the things
being chosen between. Both of those are cleared rather than merely omitted — the base rule above sets
a fill and a border, so an exception has to say so.
being chosen between.
-->
<Style Selector="Button.tab.plus">
<Style Selector="Button.sesstab.plus">
<Setter Property="Padding" Value="0" />
<Setter Property="Width" Value="28" />
<Setter Property="Height" Value="28" />
<Setter Property="MinWidth" Value="0" />
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="Foreground" Value="{StaticResource TextFaint}" />
</Style>
<Style Selector="Button.tab.plus /template/ ContentPresenter#PART_ContentPresenter">
<Style Selector="Button.sesstab.plus /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="Background" Value="Transparent" />
<Setter Property="BorderThickness" Value="0" />
<Setter Property="CornerRadius" Value="8" />
</Style>
<Style Selector="Button.tab.plus:pointerover /template/ ContentPresenter#PART_ContentPresenter">
<Style Selector="Button.sesstab.plus:pointerover /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="Foreground" Value="{StaticResource Text}" />
<Setter Property="Background" Value="{StaticResource Hover}" />
</Style>
<Style Selector="Button.tab.active /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="Background" Value="{StaticResource Active}" />
<Setter Property="Foreground" Value="{StaticResource Text}" />
<Setter Property="BorderBrush" Value="{StaticResource BorderMid}" />
<Setter Property="Background" Value="{StaticResource Track}" />
</Style>
<!--
The two halves of the Vaults tab: the tab itself, and the caret that opens its menu. Two buttons
because they do two things, drawn as one pill because they are one destination — so the pair meets in
the middle with no gap, no doubled border down the join, and the outer corners rounded as any tab's
are.
Both the Button and its ContentPresenter carry a CornerRadius above, so both have to be squared here:
setting only one leaves a rounded outline inside a square hit area, which shows as a hairline of the
strip's background cutting through the join.
After Button.tab.fixed rather than beside it, because .caret takes that rule's padding back to zero
and Avalonia has no specificity — the later declaration is the one that wins. See the ordering note
above.
── v5b: THE HOST HEADER'S "OPEN SFTP" / "OPEN TERMINAL" GHOST BUTTON ───────────────────────────────
A ghost button distinct from <c>Button.ghost</c> above: this one's resting border is <c>BorderHover</c>
rather than <c>BorderMid</c> — the mock's own inset ring for this one control — and the design gives it
no filled hover, only the border turning to the accent. See <c>SessionHeader.axaml</c>.
-->
<Style Selector="Button.tab.split">
<Setter Property="Margin" Value="0" />
<Setter Property="CornerRadius" Value="8,0,0,8" />
<Style Selector="Button.headerghost">
<Setter Property="Height" Value="32" />
<Setter Property="MinHeight" Value="0" />
<Setter Property="Padding" Value="16,0" />
<Setter Property="HorizontalContentAlignment" Value="Center" />
<Setter Property="VerticalContentAlignment" Value="Center" />
<Setter Property="FontSize" Value="12.5" />
<Setter Property="FontWeight" Value="SemiBold" />
<Setter Property="Foreground" Value="{StaticResource TextDim}" />
</Style>
<Style Selector="Button.tab.split /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="CornerRadius" Value="8,0,0,8" />
<Setter Property="BorderThickness" Value="1,1,0,1" />
<Style Selector="Button.headerghost /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="Background" Value="Transparent" />
<Setter Property="BorderBrush" Value="{StaticResource BorderHover}" />
<Setter Property="BorderThickness" Value="1" />
<Setter Property="CornerRadius" Value="9" />
</Style>
<Style Selector="Button.tab.caret">
<Setter Property="Padding" Value="0" />
<Setter Property="CornerRadius" Value="0,8,8,0" />
<Style Selector="Button.headerghost:pointerover /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="BorderBrush" Value="{StaticResource Accent}" />
<Setter Property="Foreground" Value="{StaticResource Text}" />
</Style>
<Style Selector="Button.tab.caret /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="CornerRadius" Value="0,8,8,0" />
<!--
── v5b: THE SESSION SIDEBAR'S ROWS ──────────────────────────────────────────────────────────────────
QUICK ACCESS's pins and, on the terminal surface, SNIPS — both h33, radius 9, a glyph and a mono
label, flat until the pointer finds them. See <c>SessionSidebar.axaml</c>.
-->
<Style Selector="Button.sidebarrow">
<Setter Property="Height" Value="33" />
<Setter Property="MinHeight" Value="0" />
<Setter Property="Padding" Value="12,8" />
<Setter Property="HorizontalAlignment" Value="Stretch" />
<Setter Property="HorizontalContentAlignment" Value="Left" />
<Setter Property="VerticalContentAlignment" Value="Center" />
<Setter Property="CornerRadius" Value="9" />
</Style>
<Style Selector="Button.sidebarrow /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="Background" Value="Transparent" />
<Setter Property="BorderThickness" Value="0" />
<Setter Property="CornerRadius" Value="9" />
</Style>
<Style Selector="Button.sidebarrow:pointerover /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="Background" Value="{StaticResource Track}" />
</Style>
<!--
The "+ Pin folder" / "+ Add Snip" row at the foot of each section: 30 tall rather than 33, and its own
quieter foreground — the mock draws these as the same greyed-out "add" idiom in both sections.
-->
<Style Selector="Button.sidebaradd">
<Setter Property="Height" Value="30" />
<Setter Property="MinHeight" Value="0" />
<Setter Property="Padding" Value="12,8" />
<Setter Property="HorizontalAlignment" Value="Stretch" />
<Setter Property="HorizontalContentAlignment" Value="Left" />
<Setter Property="VerticalContentAlignment" Value="Center" />
<Setter Property="CornerRadius" Value="9" />
<Setter Property="FontSize" Value="11.5" />
<Setter Property="Foreground" Value="{StaticResource TextFaint}" />
</Style>
<Style Selector="Button.sidebaradd /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="Background" Value="Transparent" />
<Setter Property="BorderThickness" Value="0" />
<Setter Property="CornerRadius" Value="9" />
</Style>
<Style Selector="Button.sidebaradd:pointerover /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="Background" Value="{StaticResource Track}" />
</Style>
<!--
@@ -661,6 +800,34 @@
<Setter Property="Background" Value="Transparent" />
</Style>
<!--
── v5b: THE SFTP PANES' OWN FILE ROWS ───────────────────────────────────────────────────────────────
A third list shape, on the same reasoning A LIST OF TILES gives above it: the design draws a file row
with 7-pixel rounded corners and a single fill — Track, not Hover or AccentWash — for both hover and
selected, and neither the accent wash nor a square-cornered highlight peeking out from behind a rounded
row is the mock's own choice. See TransfersScreen.axaml's local and remote listings.
No selected+unhovered accent strip either, unlike Border.rowmark's own rows: the design has nothing to
distinguish "selected" from "hovered" beyond which one is true at the moment, and :selected alone
already answers that once the pointer has moved on — a strip drawn on top would be a second, redundant
mark for the one state this list bothers to keep after the pointer leaves.
Declared after ListBox's own base rules, for the reason recorded three times over already in this file:
Avalonia has no specificity, and an exception declared above the rule it excepts does nothing at all.
-->
<Style Selector="ListBox.filerows > ListBoxItem /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="CornerRadius" Value="7" />
</Style>
<Style Selector="ListBox.filerows > ListBoxItem:pointerover /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="Background" Value="{StaticResource Track}" />
</Style>
<Style Selector="ListBox.filerows > ListBoxItem:selected /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="Background" Value="{StaticResource Track}" />
</Style>
<Style Selector="ListBox.filerows > ListBoxItem:selected:pointerover /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="Background" Value="{StaticResource Track}" />
</Style>
<!--
The card itself. A fixed width and a free height, which is the pair that makes a WrapPanel of these
into a grid: equal columns, and a card that grew a third line of tags is taller than its neighbours
@@ -914,6 +1081,41 @@
<Setter Property="Foreground" Value="{StaticResource Text}" />
</Style>
<!--
── v5b: THE SFTP PANE HEADERS' QUIET GLYPHS ─────────────────────────────────────────────────────────
Two variants of the icon-only button above, for the two places wave C's SFTP restyle draws one that is
not the drawer's own chrome: a pane's UP/REFRESH/DELETE affordances — see TransfersScreen.axaml, whose
remote pane keeps DELETE's own destructive colour rather than borrowing the plain one — and a pane's
drive picker, which is a chip rather than a square icon and so borrows Border.chip's own geometry
instead, for the reason Button.chiptoggle borrows it too.
-->
<Style Selector="Button.paneicon.danger">
<Setter Property="Foreground" Value="{StaticResource Danger}" />
</Style>
<Style Selector="Button.paneicon.danger:pointerover /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="Background" Value="{StaticResource DangerWash}" />
<Setter Property="Foreground" Value="{StaticResource Danger}" />
</Style>
<Style Selector="Button.panechip">
<Setter Property="Padding" Value="8,4" />
<Setter Property="MinHeight" Value="0" />
<Setter Property="FontFamily" Value="{StaticResource MonoFont}" />
<Setter Property="FontSize" Value="11" />
<Setter Property="FontWeight" Value="Medium" />
<Setter Property="Foreground" Value="{StaticResource TextFaint}" />
</Style>
<Style Selector="Button.panechip /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="Background" Value="Transparent" />
<Setter Property="BorderBrush" Value="{StaticResource BorderMid}" />
<Setter Property="BorderThickness" Value="1" />
<Setter Property="CornerRadius" Value="6" />
</Style>
<Style Selector="Button.panechip:pointerover /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="Background" Value="{StaticResource Track}" />
<Setter Property="Foreground" Value="{StaticResource Text}" />
</Style>
<!--
The status dot, in one place rather than as a converter in code.