UI/HDTProgress.xaml

<Window
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Hephaestus Deployment Toolkit"
    WindowStyle="None"
    ResizeMode="NoResize"
    SizeToContent="WidthAndHeight"
    WindowStartupLocation="CenterScreen"
    Topmost="True"
    Background="#FF1E1E1E">
 
    <!--
        DESIGN 11.1'S PROGRESS WINDOW, IN MDT'S SHAPE: a modest card in the
        middle of a full-screen backdrop.
 
        BOTH HALVES ARE LOAD-BEARING AND THEY COME FROM DIFFERENT PLACES.
 
        THE CARD IS MDT'S. LiteTouch shows a centred "Installation Progress"
        dialog - sequence, step, a bar - and an admin has watched that dialog a
        thousand times. A full-screen takeover of numbers reads as a kiosk
        rather than as a deployment, and the first draft of this file was one.
 
        THE BACKDROP IS GONE, AND THE REASON IT EXISTED EXPIRED. DESIGN 11.1
        asked for full-screen because behind this window was a console the
        payload hid only for the wizard, and around a small dialog a technician
        would have seen the black edges of a half-drawn X:\Windows\system32>
        prompt. Start-HDTDeployment now hides that console at step 4a, for the
        whole run, and a boot image's BGInfo paints the machine's serial, model
        and address onto the wallpaper - so a full-screen ground had become a
        sheet covering the very thing it was invented to protect anybody from
        seeing. MDT solves this with a deployment wallpaper, and now so does
        this: the card, on the wallpaper, and nothing else.
 
        SizeToContent, NOT A HEIGHT SOMEBODY MAINTAINS. Every part of this card
        that can grow is already bounded - the step name wraps to MaxHeight 56,
        the step bar collapses when nothing is reporting - so the window is
        exactly as tall as what is in it, on a 1024x768 WinPE screen and a
        1920x1080 bench machine alike.
 
        NO X, NO TITLE BAR, NO BUTTONS. There is nothing here to answer - it is
        a status board, not a dialog - and a deployment screen with a corner
        that makes it disappear is a deployment nobody can account for.
 
        EVERY VALUE COMES FROM Get-HDTDeploymentProgress, which derives it from
        the JSONL stream the engine writes anyway (DESIGN 11.1: driven by the
        event stream, not by a parallel progress API). The screen and the log
        cannot disagree, because they are the same facts.
 
        Topmost, AND ONLY BECAUSE THIS WINDOW OUTLIVES WinPE. In WinPE there
        is no shell to compete with - cmd.exe is it - but the full-OS leg draws
        this board on a DESKTOP, and a deployed machine showed the taskbar and
        the Start menu opening straight over it. It is opaque, so unlike the boot
        status panel it obeys z-order properly and Topmost does what it says.
 
        NO x:Class AND NO MERGED DICTIONARY, for the reason every window in this
        image carries: there is no compiler in WinPE, and a second file that
        must reach the RAM disk intact is a XamlParseException waiting for the
        one machine nobody can debug.
    -->
 
    <!--
        THE SAME GROUND THE WIZARD STANDS ON. This was a navy backdrop, which
        made the two surfaces of one product look like two products - a
        technician sees the wizard and then this, minutes apart, on the same
        machine. HDTWizardShell.xaml paints #FF1E1E1E and its panels #FF252526,
        and so does this.
 
        THE ROOT PAINTS ITS OWN GROUND. Window.Background alone is not enough:
        anything rendering this tree without the Window around it gets whatever
        is behind it, and it cost exactly one screenshot to learn that on the
        wizard.
    -->
    <Grid Background="#FF1E1E1E">
 
        <!--
            THE CARD. Fixed width so the layout does not reflow on a 1024x768
            WinPE screen and a 1920x1080 one into two different-looking
            products; centred both ways so it lands where a dialog would.
        -->
        <Border Width="720" VerticalAlignment="Center" HorizontalAlignment="Center"
                Background="#FF252526" BorderBrush="#FF3A3A3C" BorderThickness="1">
            <StackPanel Margin="36,30,36,30">
 
                <!-- ============ who this machine is ============ -->
                <Grid>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="*" />
                        <ColumnDefinition Width="Auto" />
                    </Grid.ColumnDefinitions>
 
                    <StackPanel Grid.Column="0">
                        <TextBlock x:Name="HDTProgressComputerName" Text="" Foreground="White"
                                   FontSize="26" FontWeight="SemiBold" TextTrimming="CharacterEllipsis" />
                        <TextBlock x:Name="HDTProgressSequenceName" Text="" Foreground="#FFCCCCCC"
                                   FontSize="15" Margin="0,4,0,0" TextTrimming="CharacterEllipsis" />
                    </StackPanel>
 
                    <!--
                        THE PHASE ANSWERS THE FIRST QUESTION a technician has
                        about a machine that has been running twenty minutes:
                        has it rebooted yet, or is it still in WinPE?
                    -->
                    <Border Grid.Column="1" Background="#FF0E639C" Padding="14,5" VerticalAlignment="Top">
                        <TextBlock x:Name="HDTProgressPhase" Text="" Foreground="White"
                                   FontSize="14" FontWeight="SemiBold" />
                    </Border>
                </Grid>
 
                <Border Height="1" Background="#FF3A3A3C" Margin="0,22,0,22" />
 
                <!-- ============ what it is doing ============ -->
                <TextBlock x:Name="HDTProgressStepGroup" Text="" Foreground="#FF8A8A8A"
                           FontSize="13" Margin="0,0,0,6" TextTrimming="CharacterEllipsis" />
 
                <!--
                    TWO LINES OF ROOM AND NO MORE. A step name is authored on the
                    share and can be any length; without a bound, a long one
                    would push the bar off the card - and the bar is the control
                    a technician is actually looking at.
                -->
                <TextBlock x:Name="HDTProgressStepName" Text="" Foreground="White"
                           FontSize="20" TextWrapping="Wrap" MaxHeight="56"
                           TextTrimming="CharacterEllipsis" />
 
                <!--
                    WHICH ONE OF THE MANY. "Install Applications" is one step
                    and eleven installers, and a technician watching a machine
                    sit on that name for twenty minutes cannot tell Acrobat from
                    the one that hung. Every step that loops already writes what
                    it is on - "installing 1 of 2: Acrobat Acrobat Reader DC",
                    "staging Latitude 5420: 64%" - and until this line existed
                    the log knew and the screen did not.
 
                    A HEARTBEAT LANDS HERE TOO. A step that has gone quiet says
                    "... - still running after 45s" every fifteen seconds, and
                    on a card where nothing else is moving that sentence is the
                    difference between a slow machine and a hung one.
 
                    ONE LINE, ELLIPSIS, AND THE LOG KEEPS THE WHOLE OF IT.
                    "applying Z:\OperatingSystems\...\install.wim (index 1) to
                    W:\: 45%" is longer than any card; wrapping it would push
                    the bars down the screen every time a long one arrived, and
                    the bars are what a technician is looking at. The message is
                    written for the LOG, which is the record - this is a view of
                    it, and a view is allowed to run out of room.
 
                    MinHeight, SO THE CARD DOES NOT BREATHE. This line is blank
                    between steps by design (a finished step is not still
                    installing TightVNC), and a control that collapses when it
                    empties would move the counter and both bars up and down
                    twice per step on a board somebody is watching from across a
                    room.
                -->
                <TextBlock x:Name="HDTProgressActivity" Text="" Foreground="#FFCCCCCC"
                           FontSize="14" Margin="0,6,0,0" MinHeight="19"
                           TextTrimming="CharacterEllipsis" />
 
                <!--
                    THE COUNTER AND THE STEP'S OWN PERCENTAGE SHARE A LINE, and
                    the percentage is here rather than beside its bar so that
                    both bars are the same width. A bar with a caption at the
                    end of it is a bar that stops short of the one above it, and
                    the pair then reads as ragged rather than as two measures of
                    the same machine.
                -->
                <Grid Margin="0,10,0,0">
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="*" />
                        <ColumnDefinition Width="Auto" />
                    </Grid.ColumnDefinitions>
 
                    <TextBlock Grid.Column="0" x:Name="HDTProgressStepCounter" Text=""
                               Foreground="#FFCCCCCC" FontSize="15" />
 
                    <TextBlock Grid.Column="1" x:Name="HDTProgressStepPercent" Text=""
                               Foreground="#FFCCCCCC" FontSize="15" Margin="10,0,0,0" />
                </Grid>
 
                <!--
                    A REAL BAR, NOT A MARQUEE. An indeterminate animation says
                    "not hung" and nothing else; the counter above and the fill
                    here say how far through the sequence this machine is.
                -->
                <ProgressBar x:Name="HDTProgressBar" Height="14" Minimum="0" Maximum="100" Value="0"
                             Margin="0,16,0,0" Background="#FF1E1E1E" Foreground="#FF0E639C"
                             BorderBrush="#FF3A3A3C" BorderThickness="1" />
 
                <!--
                    THE SECOND BAR IS MDT'S TOO. LiteTouch shows the sequence's
                    progress and the current operation's, one above the other,
                    and an admin has read that pair a thousand times.
 
                    IT IS THE ONLY THING ON THIS CARD THAT MOVES DURING AN
                    APPLY. Writing 18 GB of Windows is one step: the counter,
                    the sequence bar and the step name are all correct and
                    motionless for nine minutes, and a motionless screen is
                    indistinguishable from a hung machine to the person standing
                    in front of it. dism prints a percentage as it works and
                    DESIGN 11.1 carries it here through the log.
 
                    THINNER, AND A LIGHTER BLUE. It is subordinate to the bar
                    above it - one step of the sequence, not the sequence - and
                    at a glance from across a room the two must not be mistaken
                    for each other.
 
                    COLLAPSED UNTIL SOMETHING REPORTS. Most steps take a second
                    and say nothing about themselves; an empty bar sitting at
                    zero under every one of them is a control that looks broken.
                -->
                <ProgressBar x:Name="HDTProgressStepBar" Height="8" Minimum="0" Maximum="100" Value="0"
                             Margin="0,8,0,0" Visibility="Collapsed"
                             Background="#FF1E1E1E" Foreground="#FF3794FF"
                             BorderBrush="#FF3A3A3C" BorderThickness="1" />
 
                <!-- ============ how long, and how it is going ============ -->
                <Grid Margin="0,18,0,0">
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="*" />
                        <ColumnDefinition Width="Auto" />
                    </Grid.ColumnDefinitions>
 
                    <!--
                        THE STATUS LINE IS WHERE A FAILURE LANDS, painted by a
                        trigger on the text rather than by the engine - the same
                        split the wizard's rail and message line use: the engine
                        states what a thing IS and the markup decides what that
                        looks like.
                    -->
                    <TextBlock Grid.Column="0" x:Name="HDTProgressStatus" Text=""
                               FontSize="15" VerticalAlignment="Center">
                        <TextBlock.Style>
                            <Style TargetType="TextBlock">
                                <Setter Property="Foreground" Value="#FFCCCCCC" />
                                <Style.Triggers>
                                    <DataTrigger Binding="{Binding Tag, RelativeSource={RelativeSource Self}}" Value="Failed">
                                        <Setter Property="Foreground" Value="#FFF48771" />
                                        <Setter Property="FontWeight" Value="SemiBold" />
                                    </DataTrigger>
                                    <DataTrigger Binding="{Binding Tag, RelativeSource={RelativeSource Self}}" Value="Succeeded">
                                        <Setter Property="Foreground" Value="#FF89D185" />
                                        <Setter Property="FontWeight" Value="SemiBold" />
                                    </DataTrigger>
                                </Style.Triggers>
                            </Style>
                        </TextBlock.Style>
                    </TextBlock>
 
                    <!--
                        THE CLOCK, AND IT IS A CLOCK. Every other value on this
                        card is derived from the log; this one is not, because
                        the log cannot supply it. It used to be: the engine
                        summed the stretches the records' own timestamps ran
                        forwards through, which advances only when something
                        WRITES a record - so between records it was frozen by
                        construction. Measured on LT-D5M1NN3,
                        run-20260829-223623: step 7 wrote step.start and then
                        nothing for minutes, and this number sat at its opening
                        value the whole time. Gaps of 12.7s, 12.3s, 10.2s and
                        9.3s in the driver step of the same run.
 
                        SO THE LOG SUPPLIES THE STEP'S START TIME AND THE HOST
                        SUBTRACTS IT FROM ITS OWN CLOCK, on the timer that is
                        already repainting this card four times a second. Time
                        passes whether or not a deployment has anything to say
                        about it.
 
                        IT IS THIS STEP'S CLOCK, NOT THE RUN'S, AND THE LABEL
                        SAYS SO. A run clock would have to be measured across
                        the reboot, where WinPE's unsynchronised clock is
                        corrected - routinely BACKWARDS, by ten hours and
                        fifty-three minutes on LT-7FJ45S2, run-20260829-172208 -
                        and across that jump nothing is measurable at all. The
                        step running now started on THIS side of the reboot,
                        stamped by the same clock the host is reading, so the
                        skew is on both sides of the subtraction and cancels.
 
                        AND IT IS THE NUMBER THE QUESTION IS ABOUT ANYWAY: not
                        "how long has this machine been deploying" but "has this
                        step been going longer than it should".
                    -->
                    <TextBlock Grid.Column="1" x:Name="HDTProgressElapsed" Text="" Foreground="#FF8A8A8A"
                               FontSize="15" VerticalAlignment="Center" />
                </Grid>
            </StackPanel>
        </Border>
    </Grid>
</Window>