Merge branch 'feat/self-update'

Self-update for app and installer. Integrates cleanly with the
worker-log-footer feature that landed on main in parallel — the
shell VM now carries both worker-log state and update-check state,
and MainWindow hosts both the update banner and the footer log line.

Conflict resolved in IslandsShellViewModel.cs: kept nullable property
types from main's test-only parameterless constructor work, and added
the UpdateCheck property exposing the injected service.
This commit is contained in:
mika kuns
2026-04-23 15:24:07 +02:00
33 changed files with 1074 additions and 26 deletions

View File

@@ -21,7 +21,7 @@
<KeyBinding Gesture="Shift+OemQuestion" Command="{Binding FocusSearchCommand}"/>
<KeyBinding Gesture="Ctrl+N" Command="{Binding FocusAddTaskCommand}"/>
</Window.KeyBindings>
<Grid RowDefinitions="36,*,22">
<Grid RowDefinitions="36,Auto,*,22">
<!-- Custom title bar -->
<Border Grid.Row="0"
Background="{DynamicResource DeepBrush}"
@@ -58,6 +58,17 @@
Foreground="{DynamicResource TextDimBrush}"
LetterSpacing="1.4"
VerticalAlignment="Center"/>
<!-- Help menu -->
<Menu Margin="12,0,0,0"
Background="Transparent"
VerticalAlignment="Center">
<MenuItem Header="Help"
FontSize="11"
Foreground="{DynamicResource TextDimBrush}">
<MenuItem Header="Check for updates"
Command="{Binding CheckForUpdatesCommand}"/>
</MenuItem>
</Menu>
</StackPanel>
<!-- Middle: draggable strip -->
@@ -81,8 +92,47 @@
</Grid>
</Border>
<!-- Update banner -->
<Border Grid.Row="1"
Background="{DynamicResource DeepBrush}"
BorderBrush="{DynamicResource LineBrush}"
BorderThickness="0,0,0,1"
Padding="14,6"
IsVisible="{Binding IsUpdateBannerVisible}">
<Grid ColumnDefinitions="*,Auto,Auto">
<TextBlock Grid.Column="0"
VerticalAlignment="Center"
Foreground="{DynamicResource TextDimBrush}"
FontSize="12">
<Run Text="Update available: v"/>
<Run Text="{Binding UpdateCheck.CurrentVersion}"/>
<Run Text=" → v"/>
<Run Text="{Binding UpdateBannerLatestVersion}"/>
</TextBlock>
<Button Grid.Column="1"
Margin="0,0,8,0"
Padding="10,3"
Content="Update now"
Command="{Binding UpdateNowCommand}"/>
<Button Grid.Column="2"
Padding="10,3"
Content="Dismiss"
Command="{Binding DismissBannerCommand}"/>
</Grid>
</Border>
<!-- Inline update status (appears at right of banner row when no banner) -->
<TextBlock Grid.Row="1"
HorizontalAlignment="Right"
VerticalAlignment="Center"
Margin="0,0,14,0"
FontSize="11"
Foreground="{DynamicResource TextFaintBrush}"
Text="{Binding InlineUpdateStatus}"
IsVisible="{Binding InlineUpdateStatus, Converter={x:Static ObjectConverters.IsNotNull}}"/>
<!-- Background gradient layer -->
<Border Grid.Row="1">
<Border Grid.Row="2">
<Border.Background>
<RadialGradientBrush Center="50%,50%" GradientOrigin="50%,50%" RadiusX="70%" RadiusY="70%">
<GradientStop Offset="0" Color="{StaticResource DeepColor}" />
@@ -92,7 +142,7 @@
</Border>
<!-- Three islands -->
<Grid Grid.Row="1" Margin="7" ColumnDefinitions="260,*,320">
<Grid Grid.Row="2" Margin="7" ColumnDefinitions="260,*,320">
<Border Grid.Column="0" Classes="island" Margin="7">
<islands:ListsIslandView DataContext="{Binding Lists}"/>
</Border>
@@ -106,7 +156,7 @@
</Grid>
<!-- Footer: connection status -->
<Border Grid.Row="2"
<Border Grid.Row="3"
Background="{DynamicResource DeepBrush}"
BorderBrush="{DynamicResource LineBrush}"
BorderThickness="0,1,0,0">