chore: release v2.4

- Add theme system (Dark/Light palettes, ModernTheme, ThemeManager)
- Add InputDialog, Spinner common view
- Add DuplicatesCsvExportService
- Refresh views, dialogs, and view models across tabs
- Update localization strings (en/fr)
- Tweak services (transfer, permissions, search, user access, ownership elevation, bulk operations)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Dev
2026-04-20 11:23:11 +02:00
parent 8f30a60d2a
commit 12dd1de9f2
93 changed files with 8708 additions and 1159 deletions
@@ -18,7 +18,7 @@
<GroupBox Header="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[grp.dup.criteria]}" Margin="0,0,0,8">
<StackPanel Margin="4">
<TextBlock Text="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[lbl.dup.note]}"
TextWrapping="Wrap" FontSize="11" Foreground="#555" Margin="0,0,0,6" />
TextWrapping="Wrap" FontSize="11" Foreground="{DynamicResource TextMutedBrush}" Margin="0,0,0,6" />
<CheckBox Content="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[chk.dup.size]}"
IsChecked="{Binding MatchSize}" Margin="0,2" />
<CheckBox Content="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[chk.dup.created]}"
@@ -44,28 +44,51 @@
<Button Content="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[btn.cancel]}"
Command="{Binding CancelCommand}" Height="28" Margin="0,0,0,8" />
<Button Content="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[btn.open.results]}"
<Label Content="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[export.split.label]}" Margin="0,4,0,0" />
<ComboBox SelectedIndex="{Binding SplitModeIndex}" Height="26" Margin="0,0,0,4">
<ComboBoxItem Content="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[export.split.single]}" />
<ComboBoxItem Content="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[export.split.bySite]}" />
</ComboBox>
<Label Content="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[export.html.layout.label]}" Margin="0,2,0,0" />
<ComboBox SelectedIndex="{Binding HtmlLayoutIndex}" Height="26" Margin="0,0,0,6">
<ComboBoxItem Content="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[export.html.layout.separate]}" />
<ComboBoxItem Content="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[export.html.layout.tabbed]}" />
</ComboBox>
<Button Content="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[audit.btn.exportCsv]}"
Command="{Binding ExportCsvCommand}" Height="28" Margin="0,0,0,4" />
<Button Content="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[audit.btn.exportHtml]}"
Command="{Binding ExportHtmlCommand}" Height="28" Margin="0,0,0,4" />
<TextBlock Text="{Binding StatusMessage}" TextWrapping="Wrap" FontSize="11" Foreground="#555" Margin="0,4" />
<TextBlock Text="{Binding StatusMessage}" TextWrapping="Wrap" FontSize="11" Foreground="{DynamicResource TextMutedBrush}" Margin="0,4" />
</StackPanel>
</ScrollViewer>
<!-- Results DataGrid -->
<DataGrid ItemsSource="{Binding Results}" IsReadOnly="True" AutoGenerateColumns="False"
VirtualizingPanel.IsVirtualizing="True" Margin="4,8,8,8">
VirtualizingPanel.IsVirtualizing="True" Margin="4,8,8,8"
ScrollViewer.HorizontalScrollBarVisibility="Auto"
ScrollViewer.VerticalScrollBarVisibility="Auto"
ColumnWidth="Auto">
<DataGrid.Columns>
<DataGridTextColumn Header="Group" Binding="{Binding GroupName}" Width="160" />
<DataGridTextColumn Header="Copies" Binding="{Binding GroupSize}" Width="60"
<DataGridTextColumn Header="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[duplicates.col.group]}"
Binding="{Binding GroupName}" Width="160" />
<DataGridTextColumn Header="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[duplicates.col.copies]}"
Binding="{Binding GroupSize}" Width="60"
ElementStyle="{StaticResource RightAlignStyle}" />
<DataGridTextColumn Header="Name" Binding="{Binding Name}" Width="160" />
<DataGridTextColumn Header="Library" Binding="{Binding Library}" Width="120" />
<DataGridTextColumn Header="Size"
<DataGridTextColumn Header="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[report.col.name]}"
Binding="{Binding Name}" Width="160" />
<DataGridTextColumn Header="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[report.col.library]}"
Binding="{Binding Library}" Width="120" />
<DataGridTextColumn Header="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[report.col.size]}"
Binding="{Binding SizeBytes, Converter={StaticResource BytesConverter}}"
Width="90" ElementStyle="{StaticResource RightAlignStyle}" />
<DataGridTextColumn Header="Created" Binding="{Binding Created, StringFormat=yyyy-MM-dd}" Width="100" />
<DataGridTextColumn Header="Modified" Binding="{Binding Modified, StringFormat=yyyy-MM-dd}" Width="100" />
<DataGridTextColumn Header="Path" Binding="{Binding Path}" Width="*" />
<DataGridTextColumn Header="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[report.col.created]}"
Binding="{Binding Created, StringFormat=yyyy-MM-dd}" Width="100" />
<DataGridTextColumn Header="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[report.col.modified]}"
Binding="{Binding Modified, StringFormat=yyyy-MM-dd}" Width="100" />
<DataGridTextColumn Header="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[report.col.path]}"
Binding="{Binding Path}" Width="400" />
</DataGrid.Columns>
</DataGrid>
</DockPanel>