Merge branch 'claude/connection-host-default-text-9e501d'

This commit is contained in:
2026-08-03 14:56:07 +02:00
6 changed files with 71 additions and 68 deletions
@@ -1,25 +1,29 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<!-- <!--
The launcher mark, and it is the same mark ServerScreen and LockedScreen draw: a square The launcher mark, and it is the same mark PhoneShell's header and the desktop titlebar draw:
outline in the accent with >_ inside it. Redrawn as a vector rather than exported as a >_ in the canvas colour on a solid accent tile. Filled rather than outlined since v2.
bitmap so there is one geometry to change and no set of five PNG densities to forget one
of.
#5B8CFF is AccentColor from DodoSSH.Client.Shell's Theme/Palette.axaml, written out The tile is not in this file. It is the background layer — @color/dodo_accent, see ic_launcher.xml
because an Android resource cannot reference a XAML dictionary. The same duplication — and that is the whole trick of the filled design on Android: the rounding a launcher applies is
colors.xml already carries for the window background, and the same rule applies: if the its mask, so letting the mask make the tile gets a squircle on one device and a circle on another
palette moves, this moves with it. without either being drawn here. A rounded rectangle painted into this layer would be a second
rounded shape inside the first, visibly clipped at the corners on any device whose mask is not the
one it was drawn for.
108x108 with the artwork inside the middle 72 is the adaptive-icon contract — the outer #0E1220 is AccentInk from DodoSSH.Client.Shell's Theme/Palette.axaml, written out because an
18 on each edge is what the launcher eats for masking and parallax. That 72 is a width, Android resource cannot reference a XAML dictionary. It equals Canvas today and is named
though, and the mark is a square: its corners are what a circular mask reaches first. At separately in the palette for a reason worth keeping in mind here: this is ink on the accent, not
48 across the corners land 33.9 out against a radius of 36 and read as clipped even the window behind it, and it follows AccentInk if the two ever part.
though they technically clear it. 42 puts them at 29.7, which is margin one can see.
The stroke widths are the one place this deliberately departs from the screen. In the app 108x108 with the artwork inside the middle 72 is the adaptive-icon contract — the outer 18 on each
the box is a 1px border on 44px; scaled honestly that would be 1.0 here, and a launcher edge is what the launcher eats for masking and parallax. The glyph spans 36.06..71.94, which is
drawing this at 48dp would render it at half a pixel and show nothing. 2.2 and 2.8 are half the width of that 72 and centred in it. That half is taken from the headers rather than
what keep it reading as the same hairline mark at the size it is actually looked at. invented: the phone draws >_ at font size 10 on a 26px tile and the titlebar at 9 on 20px, both a
little under half the tile across, and a launcher icon is looked at from further away than either.
The stroke width is the one place this deliberately departs from the screen. In the app the glyph
is a bold mono face whose stems come out near a fifth of its height; 5.4 here is nearer a quarter,
which is what keeps it reading as the same mark at the size it is actually looked at.
--> -->
<vector xmlns:android="http://schemas.android.com/apk/res/android" <vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp" android:width="108dp"
@@ -27,28 +31,21 @@
android:viewportWidth="108" android:viewportWidth="108"
android:viewportHeight="108"> android:viewportHeight="108">
<!-- The box: 42 across, centred, square-cornered as the Border in the app is. -->
<path
android:pathData="M33,33 L75,33 L75,75 L33,75 Z"
android:fillColor="#00000000"
android:strokeColor="#5B8CFF"
android:strokeWidth="2.2" />
<!-- The chevron of >_ --> <!-- The chevron of >_ -->
<path <path
android:pathData="M44.8,48.75 L51.9,54 L44.8,59.25" android:pathData="M36.06,43.33 L49.91,53.57 L36.06,63.8"
android:fillColor="#00000000" android:fillColor="#00000000"
android:strokeColor="#5B8CFF" android:strokeColor="#0E1220"
android:strokeWidth="2.8" android:strokeWidth="5.4"
android:strokeLineCap="round" android:strokeLineCap="round"
android:strokeLineJoin="round" /> android:strokeLineJoin="round" />
<!-- The underscore, on the baseline the chevron bottoms out at. --> <!-- The underscore, on the baseline the chevron bottoms out at. -->
<path <path
android:pathData="M54,59.7 L63.2,59.7" android:pathData="M54,64.68 L71.94,64.68"
android:fillColor="#00000000" android:fillColor="#00000000"
android:strokeColor="#5B8CFF" android:strokeColor="#0E1220"
android:strokeWidth="2.8" android:strokeWidth="5.4"
android:strokeLineCap="round" /> android:strokeLineCap="round" />
</vector> </vector>
@@ -4,9 +4,14 @@
the wallpaper's colours. The system tints this by its alpha and discards the colour, so the wallpaper's colours. The system tints this by its alpha and discards the colour, so
the geometry is the foreground's and white is only a way of saying "opaque here". the geometry is the foreground's and white is only a way of saying "opaque here".
Note what that means for the filled design: the accent tile is the background layer, and a
themed icon drops the background entirely. So the shape that survives here is the glyph, not
the tile — which is the right way round anyway. Filling this layer to the edges to stand in
for the tile would tint to a featureless square with nothing of the mark left in it.
Worth shipping rather than leaving out: a launcher with themed icons on and no monochrome Worth shipping rather than leaving out: a launcher with themed icons on and no monochrome
layer to use falls back to the full-colour icon, so the one app on the home screen still layer to use falls back to the full-colour icon, so the one app on the home screen still
drawn in green is this one. drawn in blue is this one.
--> -->
<vector xmlns:android="http://schemas.android.com/apk/res/android" <vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp" android:width="108dp"
@@ -15,24 +20,18 @@
android:viewportHeight="108"> android:viewportHeight="108">
<path <path
android:pathData="M33,33 L75,33 L75,75 L33,75 Z" android:pathData="M36.06,43.33 L49.91,53.57 L36.06,63.8"
android:fillColor="#00000000" android:fillColor="#00000000"
android:strokeColor="#FFFFFF" android:strokeColor="#FFFFFF"
android:strokeWidth="2.2" /> android:strokeWidth="5.4"
<path
android:pathData="M44.8,48.75 L51.9,54 L44.8,59.25"
android:fillColor="#00000000"
android:strokeColor="#FFFFFF"
android:strokeWidth="2.8"
android:strokeLineCap="round" android:strokeLineCap="round"
android:strokeLineJoin="round" /> android:strokeLineJoin="round" />
<path <path
android:pathData="M54,59.7 L63.2,59.7" android:pathData="M54,64.68 L71.94,64.68"
android:fillColor="#00000000" android:fillColor="#00000000"
android:strokeColor="#FFFFFF" android:strokeColor="#FFFFFF"
android:strokeWidth="2.8" android:strokeWidth="5.4"
android:strokeLineCap="round" /> android:strokeLineCap="round" />
</vector> </vector>
@@ -5,12 +5,17 @@
adaptive icons landed in 26, so there is no device this ships to that would need the adaptive icons landed in 26, so there is no device this ships to that would need the
bitmaps. Density buckets exist to pick a PNG; a vector has nothing to pick between. bitmaps. Density buckets exist to pick a PNG; a vector has nothing to pick between.
The background is the same @color/dodo_window the window, status bar and navigation bar The background is the accent, and that is the tile itself rather than a backdrop for one:
use, so the mark sits on the app's own near-black rather than on a second dark that is the v2 mark is >_ knocked out of a solid accent square, so the square is this layer and the
almost but not quite it. launcher's mask is what rounds it. See ic_launcher_foreground.xml for why the rounding is
left to the mask instead of drawn.
It was @color/dodo_window until the mark went from outlined to filled, which is worth
knowing if a home screen still shows the dark version: a launcher caches icons, and the
cache outlives the install that changed them.
--> -->
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> <adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/dodo_window" /> <background android:drawable="@color/dodo_accent" />
<foreground android:drawable="@drawable/ic_launcher_foreground" /> <foreground android:drawable="@drawable/ic_launcher_foreground" />
<monochrome android:drawable="@drawable/ic_launcher_monochrome" /> <monochrome android:drawable="@drawable/ic_launcher_monochrome" />
</adaptive-icon> </adaptive-icon>
@@ -10,4 +10,11 @@
hidden. hidden.
--> -->
<color name="dodo_window">#0E1220</color> <color name="dodo_window">#0E1220</color>
<!--
AccentColor from the same palette, here because the launcher icon's background layer is a colour
and not a drawable. Same hand-kept duplication as above, and the same rule: if the palette moves,
this moves with it.
-->
<color name="dodo_accent">#5B8CFF</color>
</resources> </resources>
+18 -23
View File
@@ -11,15 +11,19 @@
# Coordinates are the launcher's 108-unit viewport, mapped so the middle 72 fills the canvas. # Coordinates are the launcher's 108-unit viewport, mapped so the middle 72 fills the canvas.
# That 72 is not an arbitrary crop: it is the part of an adaptive icon a launcher actually shows, # That 72 is not an arbitrary crop: it is the part of an adaptive icon a launcher actually shows,
# the outer 18 on each edge being what it eats for masking and parallax. Rendering the whole 108 # the outer 18 on each edge being what it eats for masking and parallax. Rendering the whole 108
# here would draw the mark at 39% of the width — correct arithmetic, and a stamp lost in a field # here would draw the glyph at half the size it is meant to be — correct arithmetic, and a stamp
# of background. Matching what the phone displays means matching the 72. # lost in a field of accent. Matching what the phone displays means matching the 72.
#
# The one thing this draws that the phone's vector does not is the tile. On Android the tile is
# the background layer and the launcher's mask rounds it; Windows has no mask, so the rounding
# happens here.
Set-StrictMode -Version Latest Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop' $ErrorActionPreference = 'Stop'
Add-Type -AssemblyName System.Drawing Add-Type -AssemblyName System.Drawing
$window = [System.Drawing.ColorTranslator]::FromHtml('#0E1220') # dodo_window / CanvasColor $accent = [System.Drawing.ColorTranslator]::FromHtml('#5B8CFF') # dodo_accent / AccentColor
$accent = [System.Drawing.ColorTranslator]::FromHtml('#5B8CFF') # AccentColor $ink = [System.Drawing.ColorTranslator]::FromHtml('#0E1220') # AccentInk
# Every size Windows asks for: 16 in a titlebar and a tree, 32 on the desktop, 48 in a large-icon # Every size Windows asks for: 16 in a titlebar and a tree, 32 on the desktop, 48 in a large-icon
# view, 256 for the preview pane. Shipping fewer means Windows downsamples one of these to fill # view, 256 for the preview pane. Shipping fewer means Windows downsamples one of these to fill
@@ -33,7 +37,7 @@ function New-MarkPng([int]$size)
$g.SmoothingMode = [System.Drawing.Drawing2D.SmoothingMode]::AntiAlias $g.SmoothingMode = [System.Drawing.Drawing2D.SmoothingMode]::AntiAlias
$g.PixelOffsetMode = [System.Drawing.Drawing2D.PixelOffsetMode]::HighQuality $g.PixelOffsetMode = [System.Drawing.Drawing2D.PixelOffsetMode]::HighQuality
# The background, rounded as a launcher mask rounds it. A square-cornered tile would be the # The accent tile, rounded as a launcher mask rounds it. A square-cornered tile would be the
# one icon on the taskbar with corners, which reads as unfinished rather than as deliberate. # one icon on the taskbar with corners, which reads as unfinished rather than as deliberate.
$radius = [double]$size * 0.22 $radius = [double]$size * 0.22
$d = $radius * 2.0 $d = $radius * 2.0
@@ -43,7 +47,7 @@ function New-MarkPng([int]$size)
$path.AddArc($size - $d, $size - $d, $d, $d, 0, 90) $path.AddArc($size - $d, $size - $d, $d, $d, 0, 90)
$path.AddArc(0.0, $size - $d, $d, $d, 90, 90) $path.AddArc(0.0, $size - $d, $d, $d, 90, 90)
$path.CloseFigure() $path.CloseFigure()
$brush = New-Object System.Drawing.SolidBrush($window) $brush = New-Object System.Drawing.SolidBrush($accent)
$g.FillPath($brush, $path) $g.FillPath($brush, $path)
# 108-viewport units to pixels, with the outer 18 dropped on each edge. # 108-viewport units to pixels, with the outer 18 dropped on each edge.
@@ -51,30 +55,21 @@ function New-MarkPng([int]$size)
function P([double]$x, [double]$y) { New-Object System.Drawing.PointF((($x - 18.0) * $scale), (($y - 18.0) * $scale)) } function P([double]$x, [double]$y) { New-Object System.Drawing.PointF((($x - 18.0) * $scale), (($y - 18.0) * $scale)) }
# A stroke thinner than a pixel renders as a grey suggestion of itself, which at 16px is the # A stroke thinner than a pixel renders as a grey suggestion of itself, which at 16px is the
# difference between a mark and a smudge. The phone's file already bumps these widths for the # difference between a mark and a smudge. The phone's file already bumps this width for the
# same reason at 48dp; the floor is that argument carried down to the sizes Windows asks for. # same reason at 48dp; the floor is that argument carried down to the sizes Windows asks for.
function Pen([double]$units) $pen = New-Object System.Drawing.Pen($ink, [float][Math]::Max(5.4 * $scale, 1.0))
{ $pen.StartCap = [System.Drawing.Drawing2D.LineCap]::Round
$pen = New-Object System.Drawing.Pen($accent, [float][Math]::Max($units * $scale, 1.0)) $pen.EndCap = [System.Drawing.Drawing2D.LineCap]::Round
$pen.StartCap = [System.Drawing.Drawing2D.LineCap]::Round $pen.LineJoin = [System.Drawing.Drawing2D.LineJoin]::Round
$pen.EndCap = [System.Drawing.Drawing2D.LineCap]::Round
$pen.LineJoin = [System.Drawing.Drawing2D.LineJoin]::Round
return $pen
}
# The box: 42 across, centred, square-cornered as the Border in the app is.
$box = Pen 2.2
$g.DrawPolygon($box, @((P 33 33), (P 75 33), (P 75 75), (P 33 75)))
# The chevron of >_ and the underscore on the baseline it bottoms out at. # The chevron of >_ and the underscore on the baseline it bottoms out at.
$ink = Pen 2.8 $g.DrawLines($pen, @((P 36.06 43.33), (P 49.91 53.57), (P 36.06 63.8)))
$g.DrawLines($ink, @((P 44.8 48.75), (P 51.9 54), (P 44.8 59.25))) $g.DrawLine($pen, (P 54 64.68), (P 71.94 64.68))
$g.DrawLine($ink, (P 54 59.7), (P 63.2 59.7))
$stream = New-Object System.IO.MemoryStream $stream = New-Object System.IO.MemoryStream
$bitmap.Save($stream, [System.Drawing.Imaging.ImageFormat]::Png) $bitmap.Save($stream, [System.Drawing.Imaging.ImageFormat]::Png)
$box.Dispose(); $ink.Dispose(); $brush.Dispose(); $path.Dispose(); $g.Dispose(); $bitmap.Dispose() $pen.Dispose(); $brush.Dispose(); $path.Dispose(); $g.Dispose(); $bitmap.Dispose()
return $stream.ToArray() return $stream.ToArray()
} }
Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.2 KiB

After

Width:  |  Height:  |  Size: 9.6 KiB