Take the frozen bar off the splash, and leave the line that is true
ci / build and test (push) Successful in 3m3s
ci / android head (push) Successful in 3m56s
ci / api image (push) Successful in 21s
ci / desktop nightly (push) Successful in 2m12s

The indeterminate bar did not move, and it was never going to. Composition is
a synchronous block and DodoSshApp posts it to the very dispatcher that would
have to animate it: Background priority buys the splash its first paint and
nothing after it, because once composition starts the UI thread is inside it
until the main window is ready, and a ProgressBar's indeterminate animation is
driven by that thread's clock. One frame, then ~2.4 seconds of stillness — the
whole of the time the window is on screen.

A frozen indeterminate bar does not read as indeterminate. It reads as a
determinate bar stuck at zero, which is worse than no bar at all: it says the
work has stalled at the moment the application is busiest, which is the one
thing a splash must never say.

Freeing the UI thread would have been the other fix, and it is a real one —
the I/O in Compose has no business on that thread. It is also a rework of
startup for the sake of an ornament. There is nothing here that can honestly
be measured anyway: a SQLite open, a loopback bind, a TPM probe and an
install-layout read report no progress between them. So the bar goes.

"Starting…" was already on the card, it is true for as long as the card is up,
it needs no clock to stay true, and the window closing is what says it
finished. CouldNotStart is one assignment now — it used to have to stop the
bar as well, so that nothing went on claiming to work over a process that had
died, and there is no longer anything on the card that could.

The window keeps its MinHeight, which is what stops two lines of content
collapsing it, and the failure path keeps SizeToContent for the same reason it
always had it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
laurensandClaude Opus 5 committed 2026-08-26 16:18:34 +02:00
1 parent fd92b80d4f
commit 7d1f0a4b53
2 files changed
+21 -19

No files matched your search

+15 -10
View File
@@ -29,18 +29,23 @@
<TextBlock x:Name="Line" Classes="hint" Text="Starting…" />
<!--
Indeterminate, because there is nothing honest to measure. Composition opens a SQLite file, binds a
loopback socket, asks the platform about a TPM and reads an install layout off disk; none of them
reports progress and inventing a bar that crawled to 90% and waited would be a worse lie than a bar
that only says "still going".
── AND NO PROGRESS BAR, WHICH IS A REMOVAL RATHER THAN AN OMISSION ─────────────────────────────────
Four pixels, matching the update banner's own and the transfers screen's, so the one moving thing in
this application looks like itself wherever it appears.
There was an indeterminate one here, and on screen it did not move. The reason is the same fact this
whole window exists for: composition is a synchronous block, and DodoSshApp posts it to the very
dispatcher that would have to animate the bar. Background priority buys the splash its first paint
and nothing after it — once composition starts, the UI thread is inside it until the main window is
ready, and an indeterminate bar's animation is driven by that thread's clock. So it painted one
frame and froze.
A frozen indeterminate bar does not read as "indeterminate". It reads as a determinate bar stuck at
zero, which is the one thing worse than no bar at all: it says the work has stalled at the exact
moment the application is busiest. Nothing here can honestly be measured either — a SQLite open, a
loopback bind, a TPM probe and an install-layout read report no progress between them.
So the line above is the whole of it. "Starting…" is true for as long as it is on screen, it needs
no clock to stay true, and the window closing is what says it finished.
-->
<ProgressBar x:Name="Bar" Height="4" Margin="0,4,0,0"
IsIndeterminate="True"
Foreground="{StaticResource Accent}"
Background="{StaticResource Chip}" />
</StackPanel>
@@ -19,14 +19,11 @@ internal sealed partial class SplashCard : UserControl
/// Says why nothing is going to happen, and stops pretending otherwise.
/// </summary>
/// <remarks>
/// The bar stopping is half the message. A composition that threw leaves this window as the only one
/// there is — there is no main window to hand over to — so an indeterminate bar would go on saying
/// "still going" over a process that has stopped, which is the one thing a splash must never do.
/// The line is the whole message now that the progress bar has gone — see the markup for why it went.
/// It used to have to stop the bar as well: a composition that threw leaves this window as the only one
/// there is, with no main window to hand over to, so anything still claiming to be working would be
/// doing it over a process that has stopped. One assignment says it instead, and there is nothing left
/// on this card that could contradict it.
/// </remarks>
internal void CouldNotStart(string why)
{
Line.Text = why;
Bar.IsIndeterminate = false;
Bar.Value = 0;
}
internal void CouldNotStart(string why) => Line.Text = why;
}